2009-08-05 20 views
30

Si bien ciertas pautas establecen que debe usar una interfaz cuando quiere definir un contrato para una clase donde la herencia no es clara (IDomesticated) y la herencia cuando la clase es una extensión de otra (Cat : Mammal, Snake : Reptile), hay casos en los que (en mi opinión) estas pautas entran en un área gris.Cuándo usar interfaces o clases abstractas? Cuándo usar ambos?

Por ejemplo, supongamos que mi implementación fue Cat : Pet. Pet es una clase abstracta. En caso de que se ampliara a Cat : Mammal, IDomesticated donde Mammal es una clase abstracta y IDomesticated es una interfaz? ¿O estoy en conflicto con los principios KISS/YAGNI (aunque no estoy seguro de si habrá una clase Wolf en el futuro, que no podría heredar de Pet)?

Alejándonos del metafórico Cat sy Pet s, digamos que tengo algunas clases que representan fuentes de datos entrantes. Todos necesitan implementar la misma base de alguna manera. Podría poner en práctica algo de código genérico de una clase abstracta Source y heredar de ella. También podría simplemente hacer una interfaz ISource (que se siente más "derecho" a mí) y volver a implementar el código genérico en cada clase (que es menos intuitivo). Finalmente, pude "tener el pastel y comérselo" haciendo tanto la clase abstracta como la interfaz. ¿Qué es mejor?

Estos dos casos muestran los puntos para utilizar solo una clase abstracta, solo una interfaz y el uso de una clase abstracta y una interfaz. ¿Son todas estas elecciones válidas, o hay "reglas" para cuando una debería usarse sobre otra?


me gustaría aclarar que por "utilizando tanto una clase abstracta y una interfaz" que incluye el caso cuando representan esencialmente la misma cosa (Source y ISource ambos tienen los mismos miembros), pero la clase agrega funcionalidad genérica mientras que la interfaz especifica el contrato.

También vale la pena señalar que esta pregunta es principalmente para los idiomas que no admiten herencia múltiple (como .NET y Java).

+1

Me gustaría notar que vi varias preguntas de "Interfaz vs. Clases abstractas", pero en esta pregunta, estoy más que interesado en saber cuándo usar las interfaces * both * y las clases abstractas (si esto es una cosa válida que hacer.) – Blixt

+0

posible duplicado de [Interface vs Abstract Class (OO general)] (http://stackoverflow.com/questions/761194/interface-vs-abstract-class-general-oo) –

+0

posible duplicado de [¿Cuándo utilizar una interfaz en lugar de una clase abstracta y viceversa?] (Http://stackoverflow.com/questions/479142/when-to-use-an-interface-instead-of-an-abstract-class-and -vice-versa) – nawfal

Respuesta

33

Como primera regla general, prefiero las clases abstractas sobre las interfaces, based on the .NET Design Guidelines. El razonamiento se aplica mucho más que .NET, pero se explica mejor en el libro Framework Design Guidelines.

El razonamiento principal detrás de la preferencia de las clases base abstractas es el control de versiones, porque siempre puede agregar un nuevo miembro virtual a una clase base abstracta sin romper los clientes existentes. Eso no es posible con las interfaces.

Existen situaciones en las que una interfaz sigue siendo la elección correcta (especialmente cuando no le interesan las versiones), pero conocer las ventajas y desventajas le permite tomar la decisión correcta.

Por lo tanto, como una respuesta parcial antes de continuar: Tener una interfaz y una clase base solo tiene sentido si decide codificar contra una interfaz en primer lugar. Si permite una interfaz, debe codificar solo contra esa interfaz, ya que de lo contrario estaría violando el Principio de Sustitución de Liskov. En otras palabras, incluso si proporciona una clase base que implementa la interfaz, no puede permitir que su código consuma esa clase base.

Si decide codificar contra una clase base, tener una interfaz no tiene sentido.

Si decide codificar contra una interfaz, tener una clase base que proporcione funcionalidad predeterminada es opcional. No es necesario, pero puede acelerar las cosas para los implementadores, por lo que puede proporcionar uno como cortesía.

Un ejemplo que me viene a la mente es en ASP.NET MVC. La canalización de solicitudes funciona en IController, pero hay una clase base de Controlador que normalmente usa para implementar el comportamiento.

Respuesta final: Si usa una clase base abstracta, use solo eso. Si se usa una interfaz, una clase base es una cortesía opcional para los implementadores.


Actualización: que ya no prefieren clases abstractas a través de interfaces, y no tienen por mucho tiempo; en cambio, prefiero la composición sobre la herencia, usando SOLID como guía.

(Si bien podría editar el texto anterior directamente, cambiaría radicalmente la naturaleza de la publicación, y como algunas personas lo han encontrado lo suficientemente valioso como para votarlo, prefiero dejar el texto original en pie, y en lugar de añadir esta nota. la última parte del mensaje es aún significativa, por lo que sería una vergüenza para eliminarlo, también.)

+0

¡Ah, gracias por los útiles consejos! Siento que se dice que las interfaces son "buenas prácticas" (y por lo tanto trato de encontrar usos para ellas) pero en la práctica no resuelven realmente tantos problemas, al menos no en proyectos pequeños a medianos ... Creo que intentaré seguir con las clases base abstractas en mi próximo proyecto a menos que encuentre una buena razón particular para ir con una interfaz. – Blixt

+2

Defina una interfaz y una clase abstracta que la implemente. Entonces, la mayoría de las implementaciones pueden extender la clase abstracta sin problemas de control de versiones (se pueden agregar nuevos métodos a la interfaz y la clase abstracta), mientras que si eso no es posible (por ejemplo, cuando ya hay otro súper), una implementación puede implementar la interfaz. –

+0

Con JDK8 y los métodos predeterminados para las interfaces, el argumento de versiones parece incluso menos relevante ahora. Sin embargo, una distinción interesante en la que pensar. – mibollma

2

siempre uso de estas directrices:

  • Use interfaces para la herencia múltiple TIPO (como .NET/Java no utiliza herencia múltiple)
  • Use clases abstractas para un reutilizable aplicación de un tipo

La regla de la preocupación dominante dicta que una clase siempre tiene una preocupación principal y 0 o más (consulte http://citeseer.ist.psu.edu/tarr99degrees.html). Esos 0 o más que otros, a continuación, poner en práctica a través de interfaces, como la clase a continuación, implementa todos los tipos que tiene para poner en práctica su propio (y todas las interfaces que implementa).

En un mundo de herencia de implementación múltiple (por ejemplo, C++/Eiffel), uno heredaría de las clases que implementan las interfaces. (En teoría, en la práctica podría no funcionar tan bien.)

2

Si desea proporcionar la opción de reemplazar completamente su implementación, use una interfaz. Esto se aplica especialmente para interacciones entre componentes principales, estos siempre deben estar desacoplados por las interfaces.

También puede haber razones técnicas para prefering una interfaz, por ejemplo para permitir burlarse en pruebas de unidad.

Internamente en un componente, puede estar bien usar una clase abstracta directamente para acceder a una jerarquía de clases.

Si usa una interfaz y tiene una jerarquía de clases de implementación, es una buena práctica tener una clase abstracta que contenga las partes comunes de la implementación. P.ej.

interface Foo 
abstract class FooBase implements Foo 
class FunnyFoo extends FooBase 
class SeriousFoo extends FooBase 

También podría tener más clases abstractas heredadas entre sí para una jerarquía más complicada.

2

También hay algo llamado el principio DRY - No repetir.

En su ejemplo de fuentes de datos, usted dice que hay algún código genérico que es común entre diferentes implementaciones. Para mí, parece que la mejor manera de manejar eso sería tener una clase abstracta con el código genérico y algunas clases concretas que lo amplíen.

La ventaja es que cada corrección de errores en el código genérico beneficia a todas las implementaciones concretas.

Si solo va a la interfaz, tendrá que mantener varias copias del mismo código que está buscando problemas.

En cuanto a la interfaz abstracta + si no hay una justificación inmediata para ello, no lo haría. Extraer la interfaz de la clase abstracta es una fácil refactorización, por lo que lo haría solo cuando realmente se necesita.

+0

La mayoría de los escenarios donde tiene una interfaz no dará como resultado "varias copias del mismo código". – RichardOD

+0

En este escenario específico según OP tiene un código común. Si usas interfaces, puedes tenerla en un lugar usando alguna delegación, pero no me molestaría. –

19

que tienden a utilizar las clases base abstractas (o no) para describir lo que algo es , mientras que uso de interfaces para describir las capacidades de un objeto.

Un gato es un mamífero pero uno de Es capacidades es que es Pettable.

O, para decirlo de otra manera, las clases son sustantivos, mientras que las interfaces se acercan a los adjetivos.

12

de MSDN, Recommendations for Abstract Classes vs. Interfaces

  • Si prevé la creación de múltiples versiones de tu componente, crea una clase abstracta. Las clases abstractas proporcionan una manera simple y fácil de versionar sus componentes. Al actualizar la clase base, todas las clases heredadas se actualizan automáticamente con el cambio. Las interfaces, por otro lado, no se pueden cambiar una vez creadas. Si se requiere una nueva versión de una interfaz, debe crear una interfaz completamente nueva.

  • Si la funcionalidad que está creando será útil en una amplia gama de objetos dispares, utilice una interfaz. Las clases abstractas deberían usarse principalmente para objetos que están estrechamente relacionados, mientras que las interfaces son las más adecuadas para proporcionar funcionalidad común a clases no relacionadas.

  • Si está diseñando pequeñas y concisas funciones, use interfaces. Si está diseñando unidades funcionales grandes, use una clase abstracta.

  • Si desea proporcionar funcionalidad común implementada entre todas las implementaciones de su componente, use una clase abstracta. Las clases abstractas le permiten implementar parcialmente su clase, mientras que las interfaces no contienen implementación para ningún miembro.

0

Consulte a continuación pregunta SE para directrices genéricas:

Interface vs Abstract Class (general OO)

caso de uso práctico para la interfaz:

  1. Implementación de Strategy_pattern: Definir su estrategia una interfaz. Cambie la implementación dinámicamente con una implementación concreta de la estrategia en tiempo de ejecución.

  2. Defina una capacidad entre varias clases no relacionadas.

Caso práctico el uso de clase abstracta:

  1. Implementación de Template_method_pattern: Definir un esqueleto de un algoritmo. Las clases secundarias no pueden cambiar la estructura del algortihm pero pueden redefinir una parte de la implementación en clases secundarias.

  2. Cuando desee compartir variables no estáticas y no finales entre varias clases relacionadas con "tiene una relación".

El uso tanto de la clase y la interfaz abstradt:

Si usted va para una clase abstracta, puede mover los métodos abstractos de interfaz y clase abstracta puede simplemente poner en práctica esa interfaz. Todos los casos de uso de clases abstractas pueden caer en esta categoría.

Cuestiones relacionadas