Pregunta

Esta es una pregunta de seguimiento de esta pregunta .

Supongamos que tengo un árbol de herencia de la siguiente manera:

Car -> Ford -> Mustang -> MustangGT

¿Hay algún beneficio en definir interfaces para cada de estas clases? Ejemplo:

ICar -> IFord -> IMustang -> IMustangGT

Puedo ver que quizás otras clases (como Chevy ) deseen implementar Icar o IFord y tal vez incluso IMustang , pero probablemente no sea IMustangGT porque es muy específico. ¿Las interfaces son superfluas en este caso?

Además, creo que cualquier clase que quiera implementar IFord definitivamente querría usar su única herencia heredando de Ford para no duplicar el código. Si eso es un hecho, ¿cuál es el beneficio de implementar también IFord ?

¿Fue útil?

Solución

En mi experiencia, las interfaces se utilizan mejor cuando tiene varias clases, cada una de las cuales necesita responder al mismo método o métodos para que puedan ser utilizados indistintamente por otro código que se escribirá en la interfaz común de esas clases. El mejor uso de una interfaz es cuando el protocolo es importante pero la lógica subyacente puede ser diferente para cada clase. Si de lo contrario estaría duplicando la lógica, considere las clases abstractas o la herencia de clase estándar.

Y en respuesta a la primera parte de su pregunta, recomendaría no crear una interfaz para cada una de sus clases. Esto desordenaría innecesariamente su estructura de clase. Si encuentra que necesita una interfaz, siempre puede agregarla más tarde. ¡Espero que esto ayude!

Adam

Otros consejos

También estoy de acuerdo con respuesta de adamalex que las interfaces deben ser compartidas por clases que deberían responder a ciertos métodos.

Si las clases tienen una funcionalidad similar, pero no están directamente relacionadas entre sí en una relación ancestral, entonces una interfaz sería una buena manera de agregar esa función a las clases sin duplicar la funcionalidad entre las dos. (O tenga implementaciones múltiples con solo diferencias sutiles).

Mientras usamos la analogía de un automóvil, un ejemplo concreto. Digamos que tenemos las siguientes clases:

Car -> Ford   -> Escape  -> EscapeHybrid
Car -> Toyota -> Corolla -> CorollaHybrid
Los

automóviles tienen ruedas y pueden Drive () y Steer () . Por lo tanto, esos métodos deberían existir en la clase Car . (Probablemente la clase Car será una clase abstracta).

Al pasar la línea, obtenemos la distinción entre Ford y Toyota (probablemente implementado como diferencia en el tipo de emblema en el automóvil, nuevamente probablemente una clase abstracta. )

Luego, finalmente tenemos una clase Escape y Corolla que son clases que se implementan completamente como un automóvil.

Ahora, ¿cómo podríamos hacer un vehículo híbrido ?

Podríamos tener una subclase de Escape que es EscapeHybrid que agrega un método FordsHybridDrive () y una subclase de Corolla que es CorollaHybrid con el método ToyotasHybridDrive () . Los métodos básicamente están haciendo lo mismo, pero aún así tenemos métodos diferentes. Yuck. Parece que podemos hacerlo mejor que eso.

Digamos que un híbrido tiene un método HybridDrive () . Como no queremos terminar teniendo dos tipos diferentes de híbridos (en un mundo perfecto), podemos crear una interfaz IHybrid que tenga un método HybridDrive () .

Entonces, si queremos hacer una clase EscapeHybrid o CorollaHybrid , todo lo que tenemos que hacer es implementar el < code> IHybrid interface .

Para un ejemplo del mundo real, echemos un vistazo a Java. Una clase que puede hacer una comparación de un objeto con otro objeto implementa la interfaz Comparable . Como su nombre lo indica, la interfaz debe ser para una clase que sea comparable , de ahí el nombre " Comparable " ;.

Como cuestión de interés, un ejemplo de automóvil se usa en la lección Interfaces de el Tutorial de Java .

No debe implementar ninguna de esas interfaces.

La herencia de clase describe qué es un objeto is (por ejemplo: es identidad). Esto está bien, sin embargo, la mayoría de las veces lo que un objeto es es mucho menos importante que lo que hace un objeto . Aquí es donde entran las interfaces.

Una interfaz debe describir lo que hace un objeto ) , o cómo actúa. Con esto quiero decir su comportamiento y el conjunto de operaciones que tienen sentido dado ese comportamiento.

Como tal, los buenos nombres de interfaz generalmente deben tener la forma IDriveable , IHasWheels , y así sucesivamente. A veces, la mejor manera de describir este comportamiento es hacer referencia a otro objeto bien conocido, por lo que puede decir "actúa como uno de estos". (por ejemplo: IList ) pero en mi humilde opinión, esa forma de denominación es minoritaria.

Dada esa lógica, los escenarios donde la herencia de la interfaz tiene sentido son completamente diferentes de los escenarios donde la herencia de objetos tiene sentido, a menudo estos escenarios no se relacionan con entre sí en absoluto.

Espero que eso te ayude a pensar a través de las interfaces que realmente necesitas :-)

Yo diría que solo haga una interfaz para las cosas que necesita consultar. Es posible que tenga otras clases o funciones que deben saber sobre un automóvil, pero ¿con qué frecuencia habrá algo que necesite saber sobre un vado?

No construyas cosas que no necesitas. Si resulta que necesita las interfaces, es un pequeño esfuerzo volver y construirlas.

Además, en el lado pedante, espero que no estés construyendo algo que se parezca a esta jerarquía. Esto no es para lo que se debe usar la herencia.

Créelo solo una vez que ese nivel de funcionalidad sea necesario.

El código de refactorización siempre está en proceso continuo.

Hay herramientas disponibles que le permitirán extraer a la interfaz si es necesario. P.EJ. http://geekswithblogs.net/ JaySmith / archive / 2008/02/27 / refactor-visual-studio-extract-interface.aspx

Haga un ICar y todo lo demás (Make = Ford, Model = Mustang y demás) como miembros de una clase que implementa la interfaz.

Es posible que desee tener su clase Ford y, por ejemplo, la clase GM y ambas implementar ICar para usar el polimorfismo si no desea seguir la ruta de verificación de Make == Whatever , eso está listo a tu estilo.

De todos modos, en mi opinión, esos son atributos de un automóvil, no al revés, solo necesita una interfaz porque los métodos son comunes: freno, aceleración, etc.

¿Puede un Ford hacer cosas que otros autos no pueden hacer? No lo creo.

Crearía los dos primeros niveles, ICar e IFord y dejaría el segundo nivel solo hasta que necesite una interfaz en ese segundo nivel.

Piense detenidamente cómo sus objetos deben interactuar entre sí dentro del dominio de su problema y considere si necesita tener más de una implementación de un concepto abstracto en particular. Utilice las interfaces para proporcionar un contrato en torno a un concepto con el que interactúan otros objetos.

En su ejemplo, sugeriría que Ford es probablemente un fabricante y Mustang es un valor de nombre de modelo utilizado por el fabricante Ford, por lo tanto, podría tener algo más como:

IVehichle - > CarImpl, MotorbikeImpl: un fabricante tiene muchos ModelNames

En esta respuesta sobre la diferencia entre interfaz y clase , Le expliqué que:

    La interfaz
  • expone qué es un concepto (en términos de " qué es " válido, en el momento de la compilación), y se utiliza para valores (MyInterface x = ...)
  • La clase
  • expone lo que hace un concepto (ejecutado realmente en tiempo de ejecución), y se usa para valores o para objetos (MyClass x o aMyClass.method ())

Entonces, si necesita almacenar en una variable 'Ford' (noción de 'valor') diferentes subclases de Ford, cree un IFord. De lo contrario, no te molestes hasta que realmente lo necesites.

Ese es un criterio: si no se cumple, IFord probablemente sea inútil.
Si se cumple, se aplican los otros criterios expuestos en las respuestas anteriores: si un Ford tiene una API más rica que un automóvil, un IFord es útil para fines de polimorfismos. Si no, ICar es suficiente.

En mi opinión, las interfaces son una herramienta para imponer el requisito de que una clase implemente una firma determinada o (como me gusta pensar) un cierto "Comportamiento". Para mí, creo que si el Capital I al comienzo de mis nombres en la superficie como un pronombre personal, y trato de nombrar mis interfaces para que puedan leerse de esa manera ... ICanFly, IKnowHowToPersistMyself IAmDisplayable, etc., así que en su ejemplo , No crearía una interfaz para reflejar la firma pública completa de ninguna clase específica. Analizaría la firma pública (el comportamiento) y luego separaría a los miembros en grupos lógicos más pequeños (cuanto más pequeños mejor) como (usando su ejemplo) IMove, IUseFuel, ICarryPassengers, ISteerable, IAccelerate, IDepreciate, etc. Y luego aplicar esas interfaces a cualquier otra clase en mi sistema las necesita

En general, la mejor manera de pensar sobre esto (y muchas preguntas en OO) es pensar sobre la noción de un contrato.

Un contrato se define como un acuerdo entre dos (o más) partes, que establece obligaciones específicas que cada parte debe cumplir; en un programa, esto es qué servicios proporcionará una clase y qué debe proporcionar la clase para obtener los servicios. Una interfaz establece un contrato que cualquier clase que implemente la interfaz debe cumplir.

Sin embargo, teniendo esto en cuenta, su pregunta depende en cierta medida del idioma que esté usando y de lo que quiera hacer.

Después de muchos años de hacer OO (como, Dios mío, 30 años), generalmente escribiría una interfaz para cada contrato, especialmente en Java, porque hace las pruebas mucho más fáciles: si tengo una interfaz para la clase, Puedo construir objetos simulados fácilmente, casi trivialmente.

Las interfaces están destinadas a ser una API pública genérica, y los usuarios estarán restringidos a usar esta API pública. A menos que desee que los usuarios utilicen los métodos específicos de tipo de IMustangGT , es posible que desee limitar la jerarquía de la interfaz a ICar y IExpensiveCar .

Solo hereda de interfaces y clases abstractas.

Si tiene un par de clases que son casi iguales, y necesita implementar la mayoría de los métodos, use e Interface en combinación con la compra del otro objeto.
Si las clases de Mustang son tan diferentes, no solo cree una interfaz ICar, sino también IMustang.
Entonces, la clase Ford y Mustang pueden heredar de ICar, y Mustang y MustangGT de ICar y IMustang.

Si implementa la clase Ford y un método es el mismo que Mustang, compre en Mustang:

class Ford{
  public function Foo(){
    ...
    Mustang mustang  = new Mustang();
    return mustang.Foo();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top