2009-08-26 8 views
11

Trabajo en una empresa donde algunos requieren justificación para el uso de una interfaz en nuestro código (Visual Studio C# 3.5).Good Case For Interfaces

Me gustaría solicitar un razonamiento de Iron Clad para el que se requieren las interfaces. (Mi objetivo es PROBAR que las interfaces son una parte normal de la programación.)

No necesito convencerme, solo necesito un buen argumento para convencer a los demás.

El tipo de argumento Busco se basa de hecho, no basada en la comparación (es decir, "porque la biblioteca .NET que utiliza" se basa comparación.)

El argumento en contra de ellos es así: Si una clase es correctamente configurado (con sus miembros públicos y privados), entonces una interfaz es solo una carga adicional, porque aquellos que usan la clase están restringidos a miembros públicos. Si necesita tener una interfaz implementada por más de 1 clase, simplemente configure la herencia/polimorfismo.

+1

No entiendo el argumento en contra de ... –

+16

si necesita explicarle a su equipo por qué las interfaces son un buen estándar de programación, entonces debe encontrar un nuevo trabajo ... sin ofensa ... –

+1

Acepto la búsqueda de un nuevo comentario de trabajo. – Finglas

Respuesta

26

Desacoplar el código. Al programar las interfaces, desacopla el código utilizando la interfaz del código que implementa la interfaz. Esto le permite cambiar la implementación sin tener que refactorizar todo el código que la usa. Esto funciona junto con la herencia/polimorfismo, lo que le permite usar cualquiera de una serie de posibles implementaciones de manera intercambiable.

Burlarse y probar la unidad. Los marcos de burla se utilizan más fácilmente cuando los métodos son virtuales, que se obtienen de forma predeterminada con interfaces. Esta es realmente la razón principal por la que creo interfaces.

Comportamiento de definición que se puede aplicar a muchas clases diferentes que les permite ser utilizadas indistintamente, incluso cuando no hay una relación (que no sea el comportamiento definido) entre las clases. Por ejemplo, una clase de Caballo y una bicicleta pueden tener ambos un método de paseo. Puede definir una interfaz IRideable que defina el comportamiento de Ride y cualquier clase que use este comportamiento puede usar un objeto Horse o Bicycle sin forzar una herencia no natural entre ellos.

+0

iba a escribir algo así, pero no soy lo suficientemente elocuente. así que dejo que alguien mejor lo haga. El desacoplamiento es la parte más importante de la programación que proporciona flexibilidad, capacidad de administración y reutilización. –

+2

Como una adición al comentario Horse/Bicycle, lo bueno de las interfaces es que permite generalizar las operaciones en su clase sin limitar su herencia: http://stackoverflow.com/questions/558152/asp-net-unfamiliar-with- interfaces/558342 # 558342 – Juliet

+0

Si un cambio de implementación implicó una nueva firma de método, entonces la adición de dicho método rompe todos los clientes que usan la interfaz. ¿Qué tipo de desacoplamiento es ese? ¿Deberíamos aspirar a una combinación de interfaz y clase abstracta? – IrishChieftain

1

Si su tienda realiza pruebas automáticas, las interfaces son una gran ayuda para la inyección de dependencias y para poder probar una unidad de software aisladamente.

2
  • desarrollo basado en pruebas
  • Unidad de Pruebas

sin interfaces produciendo código disociado sería un dolor. La mejor práctica es codificar contra una interfaz en lugar de una implementación concreta. Las interfaces parecen basura al principio, pero una vez que descubres los beneficios, siempre las usarás.

6

Para habilitar la prueba unitaria de la clase.

Para realizar un seguimiento de las dependencias de manera eficiente (si la interfaz no se ha revisado y no se ha tocado, es posible que solo la semántica de la clase haya cambiado).

Porque no hay sobrecarga de tiempo de ejecución.

Para habilitar la inyección de dependencia.

... y tal vez porque es friggin '2009, no los años 70, y los diseñadores de idiomas modernos en realidad tienen una idea de lo que están haciendo?

No es que las interfaces se deben lanzar en todas las interfaces de clase: solo aquellas que son fundamentales para el sistema y que probablemente experimenten cambios y/o extensiones significativas.

2
  • Puede implementar múltiples interfaces. No puedes heredar de múltiples clases.
  • .. eso es todo. Los puntos que otros hacen sobre el desacoplamiento del código y el desarrollo basado en pruebas no llegan al quid de la cuestión, porque también se puede hacer eso con clases abstractas.
+0

no necesariamente, las clases abstractas se deben usar cuando hay una lógica común para compartir. Es posible que tenga 2 clases que implementen la misma interfaz pero que tengan una estructura completamente diferente. –

+0

¿Por qué molestarse con una clase abstracta si solo va a anular la implementación base? Por lo tanto, las interfaces son mejores para las pruebas unitarias. Las clases abstractas sí tienen su lugar. – Finglas

+0

Estoy de acuerdo en que las clases abstractas obviamente _deberían proporcionar alguna implementación, de lo contrario deberían ser interfaces. Pero estoy respondiendo directamente la pregunta. –

10

Además de lo explicado en otras respuestas, interfaces permiten simular herencia múltiple en .NET que de otro modo no está permitido.

Alas como alguien dijo

La tecnología está dominada por dos tipos de personas: los que entienden lo que no manejan, y los que logran lo que no entienden.

+2

Este es un muy buen punto. Como .NET no admite herencia múltiple, el uso de interfaces es prácticamente obligatorio. –

+0

Meta-Knight: Esa es la línea del partido, pero no estoy seguro de creerlo. ¿No puedes estructurar las cosas de manera diferente para no requerir múltiples superclases en una subclase? ¿Diría también que "como .NET no es compatible con el despacho múltiple, escribir su propio despachador es prácticamente obligatorio"? Siempre hay sistemas de objetos más potentes, pero no siempre tiene que emular todas sus características solo para hacer algo. – Ken

2

Las interfaces le permiten declarar un concepto que se puede compartir entre muchos tipos (IEnumerable) al tiempo que permite que cada uno de esos tipos tenga su propia jerarquía de herencia.

En este caso, lo que estamos diciendo es "esto se puede enumerar, pero esa no es su única característica definitoria".

Las interfaces le permiten tomar la cantidad mínima de decisiones necesarias al definir las capacidades del implementador. Cuando crea una clase en lugar de una interfaz, ya ha declarado que su concepto es solo de clase y no se puede usar para las estructuras. También toma otras decisiones al declarar miembros en una clase, como visibilidad y virtualidad.

Por ejemplo, puede hacer una clase abstracta con todos los miembros abstractos públicos, y eso está muy cerca de una interfaz, pero ha declarado que ese concepto es sobrescribible en todas las clases secundarias, mientras que no debería haberlo hecho esa decisión si usaste una interfaz.

También facilitan las pruebas unitarias, pero no creo que sea un argumento sólido, ya que puede construir un sistema sin pruebas unitarias (no recomendado).

4

Las interfaces y clases abstractas modelan cosas diferentes. Derivas de una clase cuando tienes una relación isA, entonces la clase base modela algo concreto. Implementa una interfaz cuando su clase puede realizar un conjunto específico de tareas.

Piense en algo que sea Serializable, realmente no tiene sentido (desde el punto de vista del diseño/modelado) tener una clase base llamada Serializable ya que no tiene sentido decir que algo es Serializable. Tener algo implementar una interfaz Serializable tiene más sentido cuando dice 'esto es algo que la clase puede hacer, no lo que la clase es'

+1

+1 para la última oración solo! –

0

No entiendo cómo es su sobrecarga.

Las interfaces proporcionan flexibilidad, código manejable y capacidad de reutilización. Al codificar en una interfaz, no necesita preocuparse por el código de implementación concreto o la lógica de la clase determinada que está utilizando. Solo esperas un resultado. Muchas clases tienen una implementación diferente para la misma característica (StreamWriter, StringWriter, XmlWriter) ... no necesita preocuparse por cómo implementan la escritura, solo necesita llamarla.

+3

Si crea sus propias interfaces, es una carga adicional, porque debe crear y mantener más código. Para cada método que desee insertar en la interfaz, debe duplicar la firma del método. Luego, cada vez que necesite cambiar la firma del método, tendrá que cambiarla en al menos dos lugares. – airportyh

+0

Estás apoyando ambos argumentos aquí. StreamWriter y StringWriter son implementaciones de una clase abstracta (TextWriter); XmlWriter no está relacionado excepto para Object e IDisposable. –

+0

Sí, buen punto, no estaba pensando en cómo .NET realmente lo implementa. Estaba tratando de pensar en un ejemplo rápido de clases que compartan una funcionalidad (escritura) pero las implementen de manera diferente. –

1

El problema con el argumento de la herencia es que o tendrás una clase de dioses gigantesca o una jerarquía tan profunda que te hará girar la cabeza. Además de eso, terminarás con métodos en una clase que no necesitas o que no tienen ningún sentido.

Veo mucho "sin herencia múltiple" y si bien es cierto, probablemente no dividirá a su equipo porque puede tener múltiples niveles de herencia para obtener lo que desea.

Me viene a la mente una implementación IDisposable. Su equipo pondría un método Dispose en la clase Object y dejaría que se propagara a través del sistema, tenga o no sentido para un objeto o no.

18

El argumento en contra de ellos es por lo tanto: Si una clase es configurar correctamente (con sus miembros públicos y privados) y luego una interfaz es sólo adicional por encima porque los que utilizan la clase son restringido a miembros del público . Si necesita tener una interfaz que sea implementada por más de 1 clase, entonces solo configure la herencia/polimorfismo.

Considere el siguiente código:

interface ICrushable 
{ 
    void Crush(); 
} 

public class Vehicle 
{ 
} 

public class Animal 
{ 
} 

public class Car : Vehicle, ICrushable 
{ 
    public void Crush() 
    { 
    Console.WriteLine("Crrrrrassssh"); 
    } 
} 

public class Gorilla : Animal, ICrushable 
{ 
    public void Crush() 
    { 
    Console.WriteLine("Sqqqquuuuish"); 
    } 
} 

lo hace ningún sentido en absoluto para establecer una jerarquía de clases que se refiere a los vehículos Animales aunque ambos pueden ser aplastados por mi gigantesca máquina de trituración? No.

+2

+1 para el buen ejemplo de gorilas de sqishing – usr

1

Una interfaz declara un contrato que cualquier objeto que lo implemente se adherirá. Esto hace que asegurar la calidad en el código sea mucho más fácil que tratar de imponer la estructura escrita (no el código) o verbal, el momento en que una clase está decorada con la referencia de interfaz los requisitos/contrato es claro y el código no compilará hasta que hayas implementado esa interfaz es completamente segura y segura.

Hay muchas otras buenas razones para el uso de interfaces (listados aquí) pero probablemente no resuenan con la dirección tan bien como una buena pasada de moda comunicado, la 'calidad';)

1

Bueno, mi primera reacción es que si tiene que explicar por qué necesita interfaces, es una batalla cuesta arriba de todos modos :)

Dicho esto, aparte de todas las razones mencionadas anteriormente, las interfaces son la única manera de una programación débilmente acoplada, arquitecturas n-tier donde necesita actualizar/reemplazar componentes sobre la marcha, etc. - en la experiencia personal, sin embargo, ese era un concepto demasiado esotérico para el jefe del equipo de arquitectura con el resultado de que vivimos en todo el infierno - en el mundo de .net sin excepción!

1

¡Perdónenme por el pseudo código con anticipación!

Lectura en principios SOLIDOS. Hay algunos motivos en los principios SOLID para usar interfaces. Las interfaces le permiten desacoplar sus dependencias en la implementación. Puede llevar esto un paso más allá al usar una herramienta como StructureMap para realmente hacer que el acoplamiento se derrita.

en la que podría ser utilizado para

Widget widget1 = new Widget; 

Esto dice específicamente que desea crear una nueva instancia de widget. Sin embargo, si hace esto dentro de un método de otro objeto, ahora está diciendo que el otro objeto depende directamente del uso de Widget.Entonces podríamos decir algo como

public class AnotherObject 
{ 
    public void SomeMethod(Widget widget1) 
    { 
     //..do something with widget1 
    } 
} 

Todavía estamos vinculados al uso de Widget aquí. Pero al menos esto es más comprobable ya que podemos inyectar la implementación de Widget en SomeMethod. Ahora bien, si tuviéramos que usar una interfaz en su lugar, podríamos desacoplar aún más las cosas.

public class AnotherObject 
{ 
    public void SomeMethod(IWidget widget1) 
    { 
     //..do something with widget1 
    } 
} 

en cuenta que estamos ahora que no requieren una implementación específica de Widget sino que estamos pidiendo nada que se ajuste a iwidget interfaz. Esto significa que cualquier cosa podría ser inyectada, lo que significa que en el uso diario del código podríamos inyectar una implementación real de Widget. Pero esto también significa que cuando queremos probar este código, podemos inyectar un falso/simulacro/stub (dependiendo de su comprensión de estos términos) y probar nuestro código.

Pero, ¿cómo podemos llevar esto más allá. Con el uso de StructureMap podemos desacoplar este código aún más. Con el último ejemplo de código de nuestro código de llamada mi mirada algo como esto

public class AnotherObject 
{ 
    public void SomeMethod(IWidget widget1) 
    { 
     //..do something with widget1 
    } 
} 

public class CallingObject 
{ 
    public void AnotherMethod() 
    { 
     IWidget widget1 = new Widget(); 
     new AnotherObject().SomeMethod(widget1); 
    } 
} 

Como se puede ver en el código anterior hemos eliminado la dependencia en el SomeMethod mediante introducción de un objeto que se ajusta a iwidget. Pero en CallingObject(). AnotherMethod todavía tenemos la dependencia. ¡También podemos utilizar StructureMap para eliminar esta dependencia!

[PluginFamily("Default")] 
public interface IAnotherObject 
{ 
    ... 
} 

[PluginFamily("Default")] 
public interface ICallingObject 
{ 
    ... 
} 

[Pluggable("Default")] 
public class AnotherObject : IAnotherObject 
{ 
    private IWidget _widget; 
    public AnotherObject(IWidget widget) 
    { 
     _widget = widget; 
    } 

    public void SomeMethod() 
    { 
     //..do something with _widget 
    } 
} 

[Pluggable("Default")] 
public class CallingObject : ICallingObject 
{ 
    public void AnotherMethod() 
    { 
     ObjectFactory.GetInstance<IAnotherObject>().SomeMethod(); 
    } 
} 

Observe que en ningún lugar del código anterior estamos instantando una implementación real de AnotherObject. Como todo está cableado para StructurMap, podemos permitir que StructureMap pase en las implementaciones apropiadas dependiendo de cuándo y dónde se ejecuta el código. Ahora el código es verdaderamente flexible ya que podemos especificar a través de la configuración o programáticamente en una prueba qué implementación queremos usar. Esta configuración se puede realizar sobre la marcha o como parte de un proceso de compilación, etc. Pero no tiene que estar cableada en ninguna parte.

1

Las aplicaciones no responden a su pregunta con respecto a un caso de Interfaces.

Sin embargo recomiendo coger la persona en cuestión para leer ..

Head First Design Patterns

- Lee

3

Las interfaces no son 'requeridas para' en absoluto, es una decisión de diseño. Creo que necesita convencerse a sí mismo, por qué, caso por caso, es beneficioso usar una interfaz, porque hay una sobrecarga al agregar una interfaz. Por otro lado, para contrarrestar el argumento en contra de las interfaces porque puede 'simplemente' usar herencia: la herencia tiene sus inconvenientes, una de ellas es que, al menos en C# y Java, solo puede usar herencia una vez (herencia única); pero el segundo, y quizás más importante, es que la herencia requiere que comprendas el funcionamiento no solo de la clase padre, sino de todas las clases ancestrales, lo que hace que la extensión sea más difícil pero también más quebradiza, porque un cambio en la clase padre la implementación podría romper fácilmente las subclases. Este es el quid del argumento de la "composición sobre la herencia" que el libro de GOF nos enseñó.

+0

¿Qué es el libro de GOF? – Vaccano

+0

http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612 – airportyh

3

Le han dado un conjunto de pautas que sus jefes han considerado apropiadas para su lugar de trabajo y dominio del problema. Entonces, para ser persuasivo sobre el cambio de esas pautas, no se trata de probar que las interfaces son buenas en general, se trata de probar que las necesita en su lugar de trabajo.

¿Cómo demuestra que necesita interfaces en el código que escribe en su lugar de trabajo?Encontrando un lugar en su código base actual (no en algún código del producto de otra persona, y ciertamente no en algún ejemplo de juguete sobre Duck implementando el método makeNoise en IAnimal) donde una solución basada en interfaz es mejor que una solución basada en herencia. Muestre a sus jefes el problema al que se enfrenta y pregunte si tiene sentido modificar las pautas para adaptarse a situaciones como esa. Es un momento de aprendizaje en el que todos están viendo los mismos hechos en lugar de pegarse en la cabeza con generalidades y especulaciones.

La guía parece estar impulsada por la preocupación de evitar la sobreinnovación y la generalización prematura. Entonces, si hace una discusión a lo largo de las líneas deberíamos tener una interfaz aquí en caso de que en el futuro tengamos que ..., tiene buenas intenciones, pero para sus jefes activa las mismas campanas de alarma de sobreingeniería que Motivó la guía en primer lugar.

Espere hasta que haya un buen caso objetivo para esto, eso se aplica tanto a las técnicas de programación que usa en el código de producción como a las cosas con las que comienza discusiones con sus gerentes.