Pregunta Interfaz vs clase Base


¿Cuándo debería usar una interfaz y cuándo debería usar una clase base?

¿Debería ser siempre una interfaz si no quiero definir realmente una implementación base de los métodos?

Si tengo una clase de Perro y Gato. ¿Por qué querría implementar IPet en lugar de PetBase? Puedo entender que tenga interfaces para ISheds o IBarks (IMakesNoise?), Porque se pueden ubicar en una mascota por mascota, pero no entiendo cuál usar para una mascota genérica.


708


origen


Respuestas:


Tomemos el ejemplo de una clase de Perro y Gato, e ilustremos el uso de C #:

Tanto un perro como un gato son animales, específicamente, mamíferos cuadrúpedos (los animales son también muy generales). Supongamos que tienes una clase abstracta Mammal, para ambos:

public abstract class Mammal

Esta clase base probablemente tendrá métodos predeterminados tales como:

  • Alimentar
  • Compañero

Todos ellos son comportamientos que tienen más o menos la misma implementación entre ambas especies. Para definir esto, tendrás:

public class Dog : Mammal
public class Cat : Mammal

Ahora supongamos que hay otros mamíferos, que generalmente veremos en un zoológico:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Esto seguirá siendo válido porque en el núcleo de la funcionalidad Feed() y Mate() seguirá siendo el mismo

Sin embargo, las jirafas, rinocerontes e hipopótamos no son exactamente animales de los que puedes hacer mascotas. Ahí es donde una interfaz será útil:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

La implementación del contrato anterior no será la misma entre un gato y un perro; poner sus implementaciones en una clase abstracta para heredar será una mala idea.

Tus definiciones de Perro y Gato ahora deberían verse así:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

Teóricamente, puede anularlos desde una clase base superior, pero básicamente una interfaz le permite agregar solo las cosas que necesita a una clase sin la necesidad de herencia.

En consecuencia, dado que generalmente solo se puede heredar de una clase abstracta (en la mayoría de los lenguajes OO tipados estáticamente, es decir, las excepciones incluyen C ++), pero puede implementar múltiples interfaces, le permite construir objetos de forma estricta. según sea necesario base.


459



Bueno, Josh Bloch se dijo a sí mismo en Efectivo Java 2d:

Prefiere las interfaces sobre las clases abstractas

Algunos puntos principales:

  • Las clases existentes pueden actualizarse fácilmente para implementar un nuevo   interfaz. Todo lo que tienes que hacer es agregar   los métodos requeridos si aún no   existir y agregar una cláusula de implementaciones para   la declaración de clase.

  • Las interfaces son ideales para definir mixins. En términos generales, un   Mixin es un tipo que una clase puede   implementar además de su   escriba "para declarar que proporciona   algún comportamiento opcional. Por ejemplo,   Comparable es una interfaz mixin que   permite a una clase declarar que es   instancias se ordenan con respecto a   otros objetos mutuamente comparables.

  • Las interfaces permiten la construcción de tipo no jerárquico   marcos. Las jerarquías de tipo son   ideal para organizar algunas cosas, pero   otras cosas no caen en una   Jerarquía rígida.

  • Las interfaces permiten mejoras de la funcionalidad seguras y potentes mediante el   expresión de la clase por envolvente. Si utiliza   clases abstractas para definir tipos,   dejar al programador que quiere agregar   funcionalidad sin otra alternativa, pero   para usar la herencia

Además, puedes combinar las virtudes   de interfaces y clases abstractas por   proporcionando un esqueleto abstracto   clase de implementación para ir con cada   interfaz no trivial que exportas.

Por otro lado, las interfaces son muy difíciles de desarrollar. Si agrega un método a una interfaz, romperá todas sus implementaciones.

PD .: Compre el libro. Es mucho más detallado.


134



El estilo moderno es definir IPet y PetBase.

La ventaja de la interfaz es que otro código puede usarla sin ningún tipo de vínculo con otro código ejecutable. Completamente "limpio". También las interfaces se pueden mezclar.

Pero las clases base son útiles para implementaciones simples y utilidades comunes. Por lo tanto, proporcione una clase base abstracta para ahorrar tiempo y código.


105



Las interfaces y las clases base representan dos formas diferentes de relaciones.

Herencia (clases base) representan una relación "is-a". P.ej. un perro o un gato "es" una "mascota". Esta relación siempre representa el (único) propósito de la clase (en conjunto con el "principio de responsabilidad única")

Interfaces, por otro lado, representar características adicionales de una clase. Yo diría que es una relación "es", como en "Foo es desechable ", de ahí el IDisposable interfaz en C #.


93



Interfaces

  • Define el contrato entre 2 módulos. No puede tener ninguna implementación.
  • La mayoría de los idiomas le permiten implementar múltiples interfaces
  • La modificación de una interfaz es un cambio radical. Todas las implementaciones necesitan ser recompiladas / modificadas.
  • Todos los miembros son públicos. Las implementaciones deben implementar a todos los miembros.
  • Las interfaces ayudan en el desacoplamiento. Puedes usar frameworks simulados para burlar cualquier cosa que esté detrás de una interfaz
  • Las interfaces normalmente indican un tipo de comportamiento
  • Las implementaciones de interfaz están desacopladas / aisladas entre sí

Clases base

  • Te permite agregar un poco defecto implementación que obtiene de forma gratuita por derivación
  • Excepto C ++, solo puede derivar de una clase. Incluso si pudiera de varias clases, generalmente es una mala idea.
  • Cambiar la clase base es relativamente fácil. Las derivaciones no necesitan hacer nada especial
  • Las clases base pueden declarar funciones públicas y protegidas a las que se puede acceder mediante derivaciones
  • Las clases base abstractas no se pueden burlar fácilmente como las interfaces
  • Las clases base normalmente indican jerarquía de tipos (IS A)
  • Las derivaciones de clase pueden depender de algún comportamiento de base (tener un conocimiento complejo de la implementación de los padres). Las cosas pueden ser complicadas si haces un cambio en la implementación de base para un chico y rompes los otros.

57



En general, debería favorecer las interfaces sobre las clases abstractas. Una razón para usar una clase abstracta es si tiene una implementación común entre clases concretas. Por supuesto, aún debe declarar una interfaz (IPet) y tener una clase abstracta (PetBase) implementar esa interfaz. Utilizando interfaces pequeñas y distintas, puede usar múltiples para mejorar aún más la flexibilidad. Las interfaces permiten la máxima cantidad de flexibilidad y portabilidad de tipos a través de los límites. Al pasar las referencias a través de los límites, siempre pase la interfaz y no el tipo concreto. Esto permite que el extremo receptor determine la implementación concreta y brinde la máxima flexibilidad. Esto es absolutamente cierto cuando se programa de manera TDD / BDD.

The Gang of Four declaró en su libro "Debido a que la herencia expone una subclase a los detalles de la implementación de su padre, a menudo se dice que 'la herencia rompe la encapsulación'. Yo creo que esto es cierto.


56



Esto es bastante específico de .NET, pero el libro de Pautas de Diseño de Marco argumenta que, en general, las clases brindan más flexibilidad en un marco evolutivo. Una vez que se envía una interfaz, no tiene la oportunidad de cambiarla sin romper el código que usó esa interfaz. Sin embargo, con una clase, puede modificarlo y no romper el código que lo vincula. Mientras realice las modificaciones correctas, lo que incluye agregar nuevas funcionalidades, podrá ampliar y evolucionar su código.

Krzysztof Cwalina dice en la página 81:

En el transcurso de las tres versiones de .NET Framework, he hablado de esta guía con bastantes desarrolladores de nuestro equipo. Muchos de ellos, incluidos aquellos que inicialmente no estaban de acuerdo con las directrices, han dicho que lamentan haber enviado alguna API como interfaz. No he oído hablar de ningún caso en el que alguien lamentó haber enviado una clase.

Dicho esto, ciertamente hay un lugar para las interfaces. Como guía general, siempre proporcione una implementación abstracta de la clase base de una interfaz, si no es por nada, como ejemplo de una forma de implementar la interfaz. En el mejor de los casos, la clase base ahorrará mucho trabajo.


46



Juan,

Me gusta pensar en las interfaces como una forma de caracterizar una clase. Una clase particular de razas de perros, por ejemplo, un YorkshireTerrier, puede ser descendiente de la clase de perro padre, pero también implementa IFurry, IStubby e IYippieDog. Entonces la clase define qué es la clase, pero la interfaz nos dice cosas al respecto.

La ventaja de esto es que me permite, por ejemplo, reunir todos los IYippieDog y ponerlos en mi colección Ocean. Así que ahora puedo acceder a un conjunto particular de objetos y encontrar los que cumplen con los criterios que estoy mirando sin inspeccionar demasiado la clase.

Encuentro que las interfaces realmente deberían definir un subconjunto del comportamiento público de una clase. Si define todo el comportamiento público para todas las clases que implementan, por lo general no es necesario que exista. No me dicen nada útil.

Sin embargo, este pensamiento va en contra de la idea de que cada clase debe tener una interfaz y debe codificar a la interfaz. Está bien, pero terminas con muchas interfaces de uno a uno con las clases y hace que las cosas sean confusas. Entiendo que la idea es que realmente no cuesta nada hacer y ahora puedes intercambiar cosas dentro y fuera con facilidad. Sin embargo, me parece que raramente hago eso. La mayoría de las veces solo estoy modificando la clase existente y tengo exactamente los mismos problemas que siempre he tenido si la interfaz pública de esa clase necesita cambios, excepto que ahora tengo que cambiarla en dos lugares.

Entonces, si piensas como yo, definitivamente dirías que Cat y Dog son IPettable. Es una caracterización que los empareja a ambos.

La otra parte de esto es ¿deberían tener la misma clase base? La pregunta es, ¿deben tratarse ampliamente como la misma cosa? Ciertamente, ambos son animales, pero eso se ajusta a cómo vamos a usarlos juntos.

Supongamos que quiero reunir todas las clases de animales y ponerlas en mi contenedor Ark.

¿O necesitan ser mamíferos? Tal vez necesitamos algún tipo de fábrica de ordeño cruzado de animales?

¿Incluso necesitan estar vinculados? ¿Es suficiente simplemente saber que ambos son IPettable?

A menudo siento el deseo de derivar una jerarquía de clase completa cuando realmente solo necesito una clase. Lo hago con la esperanza de que algún día pueda necesitarlo y, por lo general, nunca lo necesito. Incluso cuando lo hago, suelo encontrar que tengo que hacer mucho para solucionarlo. Eso es porque la primera clase que estoy creando no es el Perro, no tengo tanta suerte, sino que es el Ornitorrinco. Ahora toda la jerarquía de clases se basa en el extraño caso y tengo un montón de código desperdiciado.

También puede encontrar en algún momento que no todos los gatos son IPtableables (como ese sin pelo). Ahora puedes mover esa interfaz a todas las clases derivadas que se ajusten. Descubrirá un cambio mucho menor que, de repente, los gatos ya no se derivan de PettableBase.


19



Aquí está la definición básica y simple de la interfaz y la clase base:

  • Clase base = herencia de objetos.
  • Interfaz = herencia funcional.

aclamaciones


15



Recomiendo usar composición en lugar de herencia siempre que sea posible. Usa interfaces, pero utiliza objetos miembros para la implementación básica. De esta forma, puede definir una fábrica que construye sus objetos para comportarse de cierta manera. Si desea cambiar el comportamiento, cree un nuevo método de fábrica (o fábrica abstracta) que cree diferentes tipos de subobjetos.

En algunos casos, puede encontrar que sus objetos primarios no necesitan interfaces en absoluto, si todo el comportamiento mutable se define en los objetos auxiliares.

Entonces, en lugar de IPet o PetBase, puede terminar con un Pet que tiene un parámetro IFurBehavior. El parámetro IFurBehavior se establece mediante el método CreateDog () de PetFactory. Es este parámetro el que se llama para el método shed ().

Si hace esto, encontrará que su código es mucho más flexible y la mayoría de sus objetos simples se ocupan de comportamientos muy básicos en todo el sistema.

Recomiendo este patrón incluso en lenguajes de herencia múltiple.


12



Explicado bien en esto Artículo de Java World

Personalmente tiendo a usar interfaces para definir interfaces, es decir, partes del diseño del sistema que especifican cómo se debe acceder a algo.

No es raro que tenga una clase implementando 1 o más interfaces.

Clases abstractas que uso como base para otra cosa.

El siguiente es un extracto del artículo mencionado anteriormente Artículo de JavaWorld.com, autor Tony Sintes, 20/04/01


Interfaz vs. clase abstracta

Elegir interfaces y clases abstractas no es una proposición cualquiera. Si necesita cambiar su diseño, conviértalo en una interfaz. Sin embargo, puede tener clases abstractas que proporcionan algún comportamiento predeterminado. Las clases abstractas son excelentes candidatos dentro de los marcos de aplicación.

Las clases abstractas te permiten definir algunos comportamientos; obligan a tus subclases a proporcionar a los demás. Por ejemplo, si tiene un marco de aplicación, una clase abstracta puede proporcionar servicios predeterminados, como el manejo de eventos y mensajes. Esos servicios permiten que su aplicación se conecte a su marco de aplicación. Sin embargo, hay algunas funcionalidades específicas de la aplicación que solo su aplicación puede realizar. Dicha funcionalidad puede incluir tareas de inicio y cierre, que a menudo dependen de la aplicación. Entonces, en lugar de tratar de definir ese comportamiento en sí, la clase base abstracta puede declarar el cierre abstracto y los métodos de inicio. La clase base sabe que necesita esos métodos, pero una clase abstracta le permite a su clase admitir que no sabe cómo realizar esas acciones; solo sabe que debe iniciar las acciones. Cuando es hora de iniciar, la clase abstracta puede llamar al método de inicio. Cuando la clase base llama a este método, Java llama al método definido por la clase secundaria.

Muchos desarrolladores olvidan que una clase que define un método abstracto también puede llamar a ese método. Las clases abstractas son una excelente forma de crear jerarquías de herencia planificadas. También son una buena opción para las clases sin hoja en las jerarquías de clase.

Clase vs. interfaz

Algunos dicen que debes definir todas las clases en términos de interfaces, pero creo que la recomendación parece un poco extrema. Utilizo interfaces cuando veo que algo en mi diseño cambiará con frecuencia.

Por ejemplo, el patrón de Estrategia le permite intercambiar nuevos algoritmos y procesos en su programa sin alterar los objetos que los utilizan. Un reproductor multimedia puede saber cómo reproducir CD, MP3 y archivos wav. Por supuesto, no desea codificar esos algoritmos de reproducción en el reproductor; eso hará que sea difícil agregar un nuevo formato como AVI. Además, su código estará plagado de declaraciones de casos inútiles. Y para colmo de males, tendrá que actualizar esas declaraciones de casos cada vez que agregue un nuevo algoritmo. En general, esta no es una forma de programar muy orientada a objetos.

Con el patrón de Estrategia, puede simplemente encapsular el algoritmo detrás de un objeto. Si lo hace, puede proporcionar complementos de medios nuevos en cualquier momento. Llamemos a la clase de complemento MediaStrategy. Ese objeto tendría un método: playStream (Stream s). Entonces, para agregar un nuevo algoritmo, simplemente ampliamos nuestra clase de algoritmo. Ahora, cuando el programa encuentra el nuevo tipo de medio, simplemente delega la reproducción de la secuencia en nuestra estrategia de medios. Por supuesto, necesitará un poco de fontanería para crear una instancia adecuada de las estrategias de algoritmo que necesitará.

Este es un excelente lugar para usar una interfaz. Hemos utilizado el patrón de Estrategia, que indica claramente un lugar en el diseño que cambiará. Por lo tanto, debe definir la estrategia como una interfaz. En general, debe favorecer las interfaces sobre la herencia cuando desee que un objeto tenga un determinado tipo; en este caso, MediaStrategy. Confiar en la herencia para la identidad de tipo es peligroso; te encierra en una jerarquía de herencia particular. Java no permite la herencia múltiple, por lo que no puede ampliar algo que le proporcione una implementación útil o más identidad de tipo.


12