2009-02-20 16 views
27

(En el contexto de .NET por lo que vale)¿Cuándo se necesitan las interfaces?

Tiendo a no utilizar la herencia y rara vez uso interfaces. Me encontré con alguien que piensa que las interfaces son lo mejor desde Spit. Él los usa en todas partes. No entiendo esto y de ahí las preguntas que siguen. Solo quiero verificar mi comprensión de las interfaces.

Si está utilizando interfaces en todas partes, supongo que puede predecir el futuro, los requisitos de su aplicación están definidos y nada cambiará en su aplicación. Para mí, especialmente durante el desarrollo temprano, las interfaces se vuelven un lastre. La aplicación es muy dinámica a lo largo de su vida. Si necesita restar o agregar miembros a la interfaz, muchas cosas se romperán. El chico de arriba dice que crea otra interfaz para manejar los nuevos miembros. Nada se rompe

¿No es esa composición? ¿Por qué no usar composición sin interfaces? Mas flexible.

¿Cómo maneja el caso donde se debe restar un miembro de la interfaz? Básicamente no lo hace. Las cosas simplemente se rompen y eso es genial porque ahora puedes ver todas las áreas afectadas y arreglarlas. En lugar de averiguar más elegantemente dónde están todas las rutas de código relacionadas, ¿deberíamos arrancar partes de las clases a través de la fuerza bruta?

Pienso en una aplicación de software como un gráfico. Un gráfico completo es el peor caso, teniendo n (n-1)/2. Esto significa que cada clase habla a cada clase. Una tela de araña confusa. n-1 es el mejor, en el que se trata de una estricta jerarquía de comunicación. Agregar otra interfaz solo para compensar un nuevo miembro necesario agrega una vertici al gráfico, lo que significa más bordes y una mayor realización de la ecuación n (n-1)/2. La composición sin interfaces se parece más a mixins. Solo las clases seleccionadas usan métodos particulares. Con una interfaz, todas las clases están obligadas a usar miembros, incluso si no los necesitan. El enfoque composition/mixin no agrega nuevos bordes innecesarios.

+1

Tengo que estar en desacuerdo con algunas de sus suposiciones. Las interfaces y la herencia tienen sus usos y es de esperar que se refactorice la aplicación a lo largo de su vida útil. "¿Por qué no usar composición sin interfaces?" Porque puede probar elegantemente si los objetos dispares exponen una interfaz. – overslacked

+1

En realidad, su análisis gráfico es correcto, pero su conclusión es incorrecta. Si tiene n llamadas que llaman m, tiene n * m bordes. Si todas esas llamadas pueden organizarse a través de una única interfaz (optimista pero plausible), entonces solo tiene n + m bordes. La implementación efectiva de interfaces reducirá en gran medida la complejidad del gráfico. – CurtainDog

+0

"... todas esas llamadas pueden organizarse a través de una única interfaz ..." Quizás en una aplicación muy simple. "... entonces solo tienes n + m bordes."Si la interfaz es heredada por clases que usan todos los miembros de la interfaz, entonces es un lavado. Mi escenario se refiere al caso de que te vuelves loco con las interfaces. Lee la segunda a la última oración nuevamente – 4thSpace

Respuesta

40

Las interfaces no fuerzan las clases a usan métodos. Obligan a la implementación de clases a implementar todos los métodos, pero esa es una cuestión diferente.

Me gusta la forma en que las interfaces separan la API de la implementación. Es cierto que esto también se hace con modificadores de acceso, pero las interfaces lo hacen más claro. Lo que es más importante, las interfaces también facilitan la burla, lo que significa que puede probar las clases en unidades dependientes de la interfaz incluso antes de haberla implementado.

Y sí, esto significa que a menudo termino con una interfaz que solo tiene una única implementación de producción. Eso no es un problema en mi opinión, porque he ganado la capacidad de prueba.

Por otro lado, no escribo una interfaz para cada clase. Me gusta escribir interfaces donde un objeto esencialmente proporciona un servicio - autenticación, acceso a datos, etc. Los objetos de datos simples (incluso con un comportamiento significativo) no son tan útiles en términos de interfaces, IME.

+0

Gracias a la definición aclarada en tu primer párrafo. Estas clases no están expuestas a terceros o como servicios web. Se aplica su última frase. ¿Qué quiso decir con "... tiene una implementación de producción única"? – 4thSpace

+0

"tiene una implementación de producción única" = "Solo hay una implementación de la interfaz en el código que no es de prueba. " –

+0

Todavía no sigue lo que quiere decir anteriormente. Parece que tiene mucho cuidado con el uso de las interfaces en general. – 4thSpace

7

Según wikipedia,

El uso principal de polimorfismo en la industria (teoría de la programación orientada a objetos) es la capacidad de los objetos que pertenecen a diferentes tipos de responder a método, campo o propiedad llamadas del mismo nombre , cada uno según un comportamiento apropiado específico del tipo. El programador (y el programa) no tiene que saber el tipo exacto del objeto por adelantado, por lo que el comportamiento exacto se determina en tiempo de ejecución (esto se denomina vinculación tardía o vinculación dinámica).

Eso es lo que hace que las interfaces sean tan útiles.

"El chico de arriba dice que crea otra interfaz para manejar los nuevos miembros. Nada se rompe".

Estoy adivinando aquí, pero parece que este tipo proviene de un fondo COM de la vieja escuela (¡podría estar equivocado, por supuesto!). Me hace temblar al pensar en todo el código que he trabajado en los que he visto cosas como esta:

  • IWidgetManager
  • IWidgetManager2
  • IWidgetManager3

Eso no es una buena manera de para trabajar con interfaces En mi experiencia, he visto los dos extremos: miedo a cambiar las interfaces hasta el punto en que se crean nuevas interfaces cada vez que se agregan nuevos miembros, o no se usan interfaces y teniendo un producto que sufre un alto grado de coupling. Debes encontrar un buen equilibrio. No siempre es el fin del mundo cambiar una interfaz.

¿Cuál es el tamaño del proyecto en el que está trabajando? Puede ser difícil ver el beneficio de las interfaces si se trata de un proyecto de tamaño relativamente pequeño. Si, por otro lado, es un proyecto con varios cientos de miles de líneas de código y se compone de muchos módulos, los beneficios se vuelven mucho más evidentes.

+0

No estoy seguro de lo que quiere decir con "Eso es lo que hace que las interfaces sean tan deliciosas" o su primera frase. No seguir cómo dedujo tal conclusión con la información que proporcioné. Estoy sugiriendo sutilmente que las interfaces no son un martillo para las uñas, lo que no tiene nada que ver con el polimorfismo. – 4thSpace

+1

Puedes lograr el polimorfismo a través de la herencia sin usar ninguna interfaz. No son un prerrequisito para el polimorfismo. –

1

Las interfaces tienen más sentido para mí en el contexto de la inyección de dependencias y los marcos de IoC. Me gusta la idea de que pueda definir un conjunto de comportamientos (métodos) y "garantizar" esos comportamientos a través de la implementación de una interfaz. Ahora puede conectar funciones completamente nuevas en un sistema existente con un solo ensamblaje y una actualización de archivo de configuración.

El diseño eficaz de las interfaces requiere una buena planificación anticipada, y creo que son más útiles en el contexto de sistemas y marcos grandes. Cuando son útiles, son realmente útiles. Algunos de mis favoritos: (? LINQ a nadie)

  • IComparable (a decidir cómo los objetos se comparan uno contra el otro)
  • IQueryable
  • IDisposable (mantener su estado de using mano)
+0

Sí y es una buena descripción de cuándo presentar la solicitud. Un marco grande es similar a una aplicación personalizada que expone su API, que se realiza mejor a través de una interfaz. – 4thSpace

5

Las interfaces tienen muchas situaciones útiles. Cuando necesite agregar un comportamiento específico a una clase que se puede encontrar en una amplia variedad de clases, ese es el momento perfecto para una interfaz. Un buen ejemplo es la interfaz IDisposable: tiene algún recurso que, cuando haya terminado, debe desaparecer de manera oportuna. ¿Es una conexión de base de datos? ¿Es un asa de ventana? No importa

Otro ejemplo sería cuando realmente no sabe cómo debe implementarse, como una interfaz para un objeto que aún no existe. Tal vez el objeto debe ser suministrado por un cliente de su biblioteca, o debe ser implementado por un módulo completamente diferente que no esté bajo su control. Básicamente puede diseñar un contrato para los métodos disponibles en la clase.

Dicho esto, solo los uso cuando sea necesario.Si puedo hacerlo con una clase regular, o si es algo intrínseco al objeto en particular, lo convertiré en una clase. El uso de interfaces para cada clase presenta algunas ventajas, como han dicho otros, pero eso implica una sobrecarga adicional de la que no veo una ganancia neta decente. La mayoría de las veces, he diseñado las estructuras de mi clase para que sean planas y anchas, con el menor número de dependencias posible.

En resumen: si necesita una funcionalidad común que se implementa de forma radicalmente diferente, una interfaz es justo lo que necesita.

0

Creo que usted describe el método top-down design.
No tiene que hacerlo, pero a veces realmente ayuda.

2

"El tipo anterior dice que él crea otra interfaz para manejar el nuevo miembro (s). Nada se rompe.

no es que la composición? ¿Por qué no usar composición sin interfaces? Más flexible".

Parece que está pensando en la composición en términos de herencia, como en "Heredaré todas estas capacidades en este único objeto para hacer este trabajo". Esa es una mala forma de escribir código. Es como decir: "Quiero construir un rascacielos, así aprenderé cada trabajo que hay que saber, desde cómo crear los planos hasta cómo hacer que la base sea pobre e instalar la iluminación ..."

Piense en la composición en lugar de objetos separados que cada uno realiza una sola tarea. Para hacer un trabajo complejo, ahora puedo confiar en estos objetos separados para realizar sus tareas individuales. Es como contratar al arquitecto y al equipo de construcción para construir el rascacielos. No necesito saber en gran detalle cómo hacen cada uno su trabajo, solo que pueden hacerlo. En el código, esto significa inyectar dependencias en un objeto para realizar tareas complejas en lugar de heredar.

¿Dónde encajan las interfaces? Son el contrato entre los objetos individuales. Le permiten la capacidad de no preocuparse por implementaciones individuales y concretas. Le permiten hablar un idioma común con un grupo de objetos que comparten la misma responsabilidad, pero que en realidad pueden tener una implementación diferente. La interfaz se convierte en una abstracción del objeto que realiza la tarea. No necesito saber cómo funciona cada chico con un martillo, solo que sabe cómo HitNail().

Cuando comienza a componer un sistema complejo de código con muchas clases pequeñas que tienen responsabilidades únicas, entonces pronto se entera de que puede encontrarse con serios problemas si confía demasiado en la implementación concreta de una clase determinada, y eso la clase comienza a cambiar ... Entonces, en lugar de confiar en la implementación concreta, creamos una interfaz: una abstracción. Y decimos: "No me importa cómo trabajas con JobX, solo que lo haces".

Existen otros beneficios para interfaces como pruebas, burlas, etc. Pero esas no son la razón para codificar una interfaz. El motivo es crear un sistema de código que no dependa de detalles y, por lo tanto, esté altamente acoplado entre sí. Esta es la razón por la cual, con su cerebro de pensamiento gráfico, debería tener miedo de juntar un montón de clases concretas. Porque los cambios en una de esas clases causarán un efecto dominó.

Cuando se combina con una abstracción en lugar de una clase concreta, limita el acoplamiento. Dices que solo voy a estar unido al contrato en el que ambos estamos de acuerdo, y nada más ". Y si la clase que implementa ese contrato cambia su forma interna de completar su tarea, no le importa, porque no está confiando en una propiedad o método sin contrato. Usted se basa únicamente en el contrato acordado.

+0

"Parece que piensas en la composición en términos de herencia ..." - No. Como mencioné, rara vez heredo. Estaba pensando en términos de pequeñas clases que exponen a un pequeño número de miembros. Otras clases componen estas clases más pequeñas al exponerlas a través de un método/propiedad. Mejor que la interfaz? – 4thSpace

2

Si quiere saber cuándo usar interfaces, y para qué sirve, creo que debería echarle un vistazo al libro: Head First Desing Patterns.

Este es el libro que realmente me ayudó a entender lo que es tan genial acerca de las interfaces.

Antes de leer este libro, sabía lo que era una interfaz, pero no tenía ni idea de cuándo debería usarla.

+0

Es un muy buen libro para demostrar interfaces. –

+0

De hecho, este es solo un libro muy bueno que creo que todo programador debería leer: P Esto realmente cambió la forma en que programé/pensé en la programación, algo que hubiera deseado haber aprendido en la escuela. – Martin

+1

También me gusta agregar Head First Object-Oriented Analysis and Design, este también es un buen libro. – Ali

0

me gustaría refieren a las directrices de diseño .net en esto: http://msdn.microsoft.com/en-us/library/ms229013.aspx

Hay muchas razones para utilizar las interfaces, pero también me parece que son a menudo usado en exceso por las razones equivocadas. Las interfaces proporcionan mucha más flexibilidad cuando se trabaja con tipos de valor, y son increíblemente útiles cuando se trabaja con colecciones, etc., pero al diseñar una jerarquía de clases para mis propios proyectos, siempre trato de pensar en la simplicidad primero, y las interfaces a menudo conducen (innecesariamente) situaciones más complejas.

Mi regla de oro es implementar cada interfaz BCL que tenga sentido, y solo agregar mis propias interfaces en mis diseños cuando en realidad proporciona algo muy valioso. En lugar de tener IWidgetManager, IWidgetManager2, etc ... Preferiría tener WidgetManager de clase abstracta e implementar métodos concretos según sea necesario.

2

Se trata de dependencias. Desea que su código dependa de los tipos de interfaz en lugar de los tipos concretos cuando corresponda. Por ejemplo, es posible que tenga varios tipos concretos que realizan un comportamiento similar pero que lo implementan de forma diferente. ¿Desea que su base de código tenga dependencias en esos múltiples tipos concretos, o una interfaz? ¿Cuán flexible crees que será tu código base cuando necesites hacer cambios?

Para su punto acerca de no querer usar miembros públicos mediante el uso de la composición, diría que simplemente está encapsulando dependencias innecesarias. Si desea utilizar la composición, entonces reduce en gran medida las dependencias al componer tipos de interfaz en lugar de tipos concretos.

Para una mejor explicación, intente consultar la literatura en inversion of control.

0

Parece que esta persona no entendió el concepto de programación en una interfaz. No se refiere a la programación usando solo la palabra clave interface en .Net/Java o clase con nada más que funciones virtuales puras en C++. La interfaz a la que se hace referencia en ese principio es una construcción de alto nivel que encapsula el bajo nivel unos de un sistema. Al igual que con la multiplicación, encapsula la idea de agregar un número a sí mismo una cantidad específica de repeticiones. Pero cuando decimos 3 * 2 no nos importa si es 3 + 3, 2 + 2 + 2 o (2 + 2) + 2 donde la parte entre paréntesis está en caché. Mientras recibamos un 6 de él.

De hecho, el concepto de interfaces se puede completar con un interface o abstract class o una combinación de estos como en el caso de muchos de los patrones de GoF. Es solo que la palabra clave interface nubla el agua con ambigüedad.

Es gracioso. El tipo de pensamiento de este tipo probablemente sea el que generó comentarios como los que se centran en el episodio # 38 de StackOverflow.

3

Las interfaces le ayudan mantener las dependencias de las abstracciones.

El código que usa la interfaz solo depende de la interfaz, por lo que sabe que no hay dependencias artificiales en los detalles. Esto le da mucha libertad en cuanto al cambio de código en el futuro, ya que sabe exactamente qué debería & no se debe romper cuando 'arregla' un error o refactor.

En mi opinión, es la esencia del buen diseño del código.

+0

tu tipo de tropiezo. su punto es que las interfaces se ponen en marcha * antes de que la API sea realmente conocida *; esa es una forma realmente inapropiada de usar interfaces. Son un contrato. ¿Firmas contratos sin completar entendiendo lo que estás obteniendo? – alchemical

0

Parece que su amigo estaba seriamente mal utilizando las interfaces.

También he visto aplicaciones web con interfaces en todas partes. Algunas personas parecen tener la idea de que una interfaz es de alguna manera mejor que solo una firma de método regular. Los parámetros simples ya nos proporcionan un contrato; en la mayoría de los casos, creo que esto es suficiente y hace que el código sea más simple.

Me parece que las interfaces son más útiles en casos como IDisposable o ICollection, donde apuntan a un cierto conjunto de funcionalidades que se esperan de un objeto. En estos casos, parecen ser la herramienta adecuada para el trabajo.

0

Utilizamos en gran medida interfaces en elementos de interfaz de usuario personalizados, donde cada uno espera que exista un cierto método en otro (y la interfaz fuerza esa existencia).

0

El uso principal de una interfaz es permitir la variación de la implementación permitiendo el cambio de código en tiempo de ejecución. Hay una serie de razones/razones para hacer esto.

En el pasado, algunos han argumentado que cada clase en el sistema debe tener una interfaz, pero esto es ampliamente reconocido como overkill. Las interfaces se usan ahora donde las instancias de clases pueden cambiar como parte de la operación del sistema (para representar el estado): patrones GoF como Estrategia y Comando capturan este uso, y/o partes del sistema deben ser reemplazadas para pruebas (es decir, dependencia). inyección). Si los desarrolladores siguen las prácticas de desarrollo basadas en pruebas, los objetos clave de infraestructura tendrán interfaces. Esto permite que las pruebas hagan "burlas" de estos objetos para probar el flujo de control del sistema. Existe una relación entre este uso de la interfaz y el Principio de sustitución de Liskov (uno de los principios del diseño OO)

Otro uso para las interfaces menos relacionadas con lo que está disponible para la persona que llama es la interfaz del marcador. Que es una forma de asociar metadatos con la definición de clase. Esto puede afectar la forma en que el sistema ve el objeto (por ejemplo, permitir que se serialice) o simplemente puede servir como documentación.

0

Una de las razones más importantes para usar una interfaz es el hecho de que nos permiten escribir pruebas de unidad para nuestras clases y pasar en nuestras propias dependencias. Al poner la dependencia detrás de una interfaz, abrimos "Seams" en nuestro código de aplicación, donde las pruebas unitarias pueden escribirse fácilmente. No veo este ángulo de prueba mencionado en muchas de las respuestas, pero es muy importante entender que sin interfaces, estas dependencias (como un servicio web o una referencia del sistema de archivos) pueden ser muy difíciles de probar o, en el mejor de los casos son bastante condicionales He escrito una publicación aquí: http://jeffronay.com/why-use-an-interface/ que entra en mucho más detalle con ejemplos de código que muestran una clase de servicio sin una interfaz, y luego la misma clase reescribió utilizando una interfaz para demostrar la flexibilidad de prueba al usar interfaces.

Cuestiones relacionadas