Interfaz vs Clase base


¿Cuándo debo usar una interfaz y cuándo debo 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 tener interfaces para ISheds o IBarks (IMakesNoise?), porque se pueden colocar en una mascota por mascota, pero no entiendo cuál usar para una Mascota genérica.

Author: Howler, 2008-09-11

30 answers

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

Tanto un perro como un gato son animales, específicamente, mamíferos cuadrúpedos (los animales son demasiado generales). Supongamos que usted tiene una clase abstracta Mammal, para ambos:

public abstract class Mammal

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

  • Feed
  • Mate

Todos los cuales son comportamientos que tienen más o menos la misma implementación entre cualquiera especie. 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 sigue 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 se pueden hacer mascotas. Aquí 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.

Sus definiciones de Perro y Gato ahora deberían tener el siguiente aspecto:

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

Teóricamente se pueden anular desde una clase base superior, pero esencialmente una interfaz le permite agregar solo las cosas que necesita en una clase sin la necesidad de herencia.

En consecuencia, debido a que generalmente solo puede heredar de una clase abstracta (en la mayoría de los lenguajes OO de tipo estático, eso es... las excepciones incluyen C++) pero ser capaz de implementar múltiples interfaces, le permite construir objetos estrictamente según sea necesario.

 463
Author: Jon Limjap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-04 15:56:27

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

Prefieren las interfaces sobre las clases abstractas

Algunos puntos principales:

  • Las clases existentes se pueden adaptar fácilmente para implementar una nueva interfaz . Todo lo que tienes que hacer es añadir los métodos requeridos si todavía no lo hacen existir y añadir una cláusula implements a la declaración de clase.

  • Las interfaces son ideales para definir mixins . Vagamente hablando, un mixin es un tipo que una clase puede implementar además de su " primaria escriba " para declarar que proporciona algún comportamiento opcional. Por ejemplo, Comparable es una interfaz mixin que permite a una clase declarar que su instances are ordered with respect to otros objetos mutuamente comparables.

  • Las interfaces permiten la construcción de tipo no jerárquico frameworks . Las jerarquías de tipo son genial para organizar algunas cosas, pero otras cosas no caen perfectamente en un jerarquía rígida.

  • Las interfaces permiten mejoras de funcionalidad seguras y potentes a través del wrap - per class idiom (en inglés). Si usa clases abstractas para definir tipos, deja al programador que quiere añadir funcionalidad sin alternativa pero para usar la herencia.

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

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

PS.: Compra el libro. Es mucho más detallado.

 134
Author: Marcio Aguiar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-04 11:46:00

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 se pueden mezclar interfaces.

Pero las clases base son útiles para implementaciones simples y utilidades comunes. Así que proporcione una clase base abstracta para ahorrar tiempo y código.

 106
Author: Jason Cohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 15:34:45

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

La herencia (clases base) representa una relación "es-a". Por ejemplo, un perro o un gato" es-una " mascota. Esta relación siempre representa el (único) propósito de la clase (en conjunción con el "principio de responsabilidad única").

Interfaces, por otro lado, representan características adicionales de una clase. Yo lo llamaría una relación "es", como en "Foo es desechable", de ahí la interfaz IDisposable en C#.

 93
Author: Thomas Danecker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-15 11:05:27

Interfaces

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

Clases base

  • Le permite agregar una implementación predeterminada que obtiene de forma gratuita por derivación
  • Excepto C++, solo se puede derivar de una clase. Incluso si podría de múltiples clases, por lo general es una mala idea.
  • Cambiar la clase base es relativamente fácil. Las derivaciones no necesitan hacer nada especial
  • Base las clases pueden declarar funciones protegidas y públicas 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 la jerarquía de tipos (IS A)
  • Las derivaciones de clases pueden llegar a depender de algún comportamiento base (tener un conocimiento intrincado de la implementación padre). Las cosas pueden ser desordenadas si haces un cambio en la implementación base para un tipo y rompes con los demás.
 57
Author: Gishu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-08 20:29:52

En general, debe preferir 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, todavía debe declarar una interfaz (IPet) y tener una clase abstracta (PetBase) implementar esa interfaz.Utilizando interfaces pequeñas y distintas, puede usar múltiplos para mejorar aún más la flexibilidad. Las interfaces permiten la máxima flexibilidad y portabilidad de los tipos a través de las fronteras. Al pasar referencias límites, siempre pasan la interfaz y no el tipo concreto. Esto permite al receptor determinar la implementación concreta y proporciona la máxima flexibilidad. Esto es absolutamente cierto cuando se programa de manera TDD / BDD.

La Pandilla de los Cuatro 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". Creo que esto es verdad.

 56
Author: Kilhoffer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 17:53:39

Esto es bastante específico de.NET, pero el libro Framework Design Guidelines argumenta que en las clases generales se da más flexibilidad en un framework en evolución. Una vez que se envía una interfaz, no tienes la oportunidad de cambiarla sin romper el código que usó esa interfaz. Sin embargo, con una clase, puede modificarla y no romper el código que enlaza con ella. Siempre que realice las modificaciones adecuadas, lo que incluye agregar nuevas funcionalidades, podrá ampliar y evolucionar su codificar.

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 los 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 un solo caso en el que alguien se arrepintiera de haber enviado una clase.

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

 46
Author: fryguybob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 16:18:49

Juan,

Me gusta pensar en las interfaces como una forma de caracterizar una clase. Una clase de raza de perro en particular, decir un YorkshireTerrier, puede ser un descendiente de la clase de perro padre, pero también se implementa IFurry, IStubby, y IYippieDog. Así que la clase define lo que 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 tirarlos a mi colección Ocean. Así que ahora puedo llegar a través de un conjunto particular de objetos y encontrar los que cumplen con los criterios que estoy mirando sin inspeccionar la clase demasiado de cerca.

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, entonces generalmente no necesita existir. No me dicen nada útil.

Este pensamiento, sin embargo, va en contra de la idea de que cada clase debe tener una interfaz y debe codificar a la interfaz. Eso está bien, pero terminas con muchas interfaces uno a uno para las clases y hace que las cosas sean confusas. Entiendo que la idea es que realmente no cuesta nada hacer y ahora puedes cambiar las cosas con facilidad. Sin embargo, me parece que rara vez hago eso. La mayoría de las veces solo estoy modificando la clase existente en su lugar y tengo exactamente los mismos problemas que siempre tuve si la interfaz pública de esa clase necesita cambiar, excepto que ahora tengo que cambiarla en dos lugar.

Así que si piensas como yo definitivamente dirías que el Gato y el perro son IPettable. Es una caracterización que coincide con ambos.

La otra parte de esto, sin embargo, es ¿deberían tener la misma clase base? La pregunta es si necesitan ser tratados ampliamente como la misma cosa. Ciertamente, ambos son Animales, pero eso encaja con la forma en que los vamos a usar juntos.

Digamos que quiero reunir todas las clases de animales y ponerlas en mi contenedor de Arca.

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

¿Necesitan siquiera estar vinculados entre sí? Es suficiente con 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 anticipación algún día podría necesitarlo y por lo general nunca lo hago. Incluso cuando lo hago, normalmente encuentro que tengo que hacer mucho para arreglarlo. Eso es porque la primera clase que estoy creando no es el Perro, No soy tan afortunado, sino el Ornitorrinco. Ahora toda mi jerarquía de clases se basa en el caso extraño y tengo un montón de código desperdiciado.

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

 19
Author: Flory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 21:32:07

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

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

Salud

 15
Author: RWendi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-20 20:26:31

Recomiendo usar la composición en lugar de la herencia siempre que sea posible. Utilice interfaces pero utilice objetos miembro para la implementación base. De esta manera, puede definir una fábrica que construya sus objetos para que se comporten 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 todos los mutables el comportamiento se define en objetos auxiliares.

Así que en lugar de IPet o PetBase, podría terminar con una Mascota 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 tratan con comportamientos muy básicos en todo el sistema.

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

 12
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 19:03:01

Explicado bien en este 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 JavaWorld.com artículo, autor Tony Sintes, 04/20/01


Interfaz vs clase abstracta

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

Las clases abstractas le permiten definir algunos comportamientos; obligan a sus subclases a proporcionar otros. Por ejemplo, si tener 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 aplicaciones. Sin embargo, hay algunas funciones específicas de la aplicación que solo su aplicación puede realizar. Dicha funcionalidad puede incluir tareas de inicio y apagado, que a menudo dependen de la aplicación. Así que en lugar de tratar de definir ese comportamiento en sí, la clase base abstracta puede declarar cierre abstracto y 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 el momento 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 hija.

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 manera 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 debe 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á lleno de declaraciones de casos inútiles. Y para agregar insulto a la lesión, tendrá que actualizar esas declaraciones de casos cada vez que agregue un nuevo algoritmo. Con todo, esta no es una forma muy orientada a objetos para programa.

Con el patrón de estrategia, simplemente puede encapsular el algoritmo detrás de un objeto. Si lo hace, puede proporcionar nuevos complementos multimedia en cualquier momento. Llamemos a la clase de plug-in MediaStrategy. Ese objeto tendría un método: playStream (Stream s). Así que para añadir un nuevo algoritmo, simplemente extendemos nuestra clase de algoritmo. Ahora, cuando el programa se encuentra con el nuevo tipo de medios, simplemente delega la reproducción de la transmisión a nuestra estrategia de medios. Por supuesto, necesitarás algo de plomerí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. Generalmente debe preferir las interfaces sobre la herencia cuando desea que un objeto tenga un tipo determinado; en este caso, MediaStrategy. Confiar en la herencia para la identidad de tipo es peligroso; te encierra en un particular jerarquía de herencia. Java no permite la herencia múltiple, por lo que no se puede extender algo que le da una implementación útil o más identidad de tipo.

 12
Author: Richard Harrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-16 20:39:24

También tenga en cuenta que no debe dejarse arrastrar por OO ( ver blog) y siempre modele objetos basados en el comportamiento requerido, si estuviera diseñando una aplicación donde el único comportamiento que requiere es un nombre genérico y una especie para un animal, solo necesitaría una clase Animal con una propiedad para el nombre, en lugar de millones de clases para todos los animales posibles en el mundo.

 10
Author: Sijin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-22 14:18:10

Tengo una regla general aproximada

Funcionalidad: probablemente sea diferente en todas las partes: Interfaz.

Los datos y la funcionalidad, las partes serán en su mayoría las mismas, las partes diferentes: clase abstracta.

Los datos y la funcionalidad funcionan realmente, si se extienden solo con ligeros cambios: clase ordinaria (concreta)

Datos y funcionalidad, sin cambios planificados: clase ordinaria (concreta) con modificador final.

Datos, y tal vez funcionalidad: solo lectura: miembros de enumeración.

Esto es muy áspero y listo y no está estrictamente definido, pero hay un espectro de interfaces donde todo está destinado a ser cambiado a enumeraciones donde todo está arreglado un poco como un archivo de solo lectura.

 8
Author: Akhil Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-14 08:56:40

Las interfaces deben ser pequeñas. Muy pequeño. Si realmente está descomponiendo sus objetos, entonces sus interfaces probablemente solo contendrán unos pocos métodos y propiedades muy específicas.

Las clases abstractas son atajos. ¿Hay cosas que todas las derivadas de PetBase comparten que puedes codificar una vez y terminar con ellas? Si es así, entonces es hora de una clase abstracta.

Las clases abstractas también son limitantes. Mientras que le dan un gran atajo para producir objetos hijo, cualquier dado object solo puede implementar una clase abstracta. Muchas veces, encuentro esto una limitación de clases abstractas, y es por eso que uso muchas interfaces.

Las clases abstractas pueden contener varias interfaces. Su clase abstract de PetBase puede implementar IPet (mascotas tienen dueños) e IDigestion (mascotas comen, o al menos deberían). Sin embargo, PetBase probablemente no implementará IMammal, ya que no todas las mascotas son mamíferos y no todos los mamíferos son mascotas. Puede agregar una MammalPetBase que extienda PetBase y agregar IMammal. FishBase podría tener PetBase y añadir iFish. iFish tendría ISwim e IUnderwaterBreather como interfaces.

Sí, mi ejemplo es extremadamente complicado para el ejemplo simple, pero eso es parte de lo bueno de cómo las interfaces y las clases abstractas funcionan juntas.

 7
Author: Jarrett Meyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 15:34:03

El caso de las Clases Base sobre Interfaces se explicó bien en las Directrices de Codificación Submain. NET:

Clases base vs. Interfaces Un tipo de interfaz es parcial descripción de un valor, potencialmente soportado por muchos tipos de objetos. Utilizar clases base en lugar de interfaces siempre que sea posible. Desde un versionado perspectiva, las clases son más flexibles que las interfaces. Con una clase, puedes enviar la versión 1.0 y luego en la versión 2.0 añadir un nuevo método a clase. Mientras el método no sea abstracto, cualquier clase derivada existente continúa para funcionar sin cambios.

Porque las interfaces no soportan herencia de implementación, el patrón que se aplica a las clases hace no se aplica a las interfaces. Añadir un método a una interfaz es equivalente para agregar un método abstracto a una base clase; cualquier clase que implemente el interfaz se romperá porque la clase no aplica el nuevo método. Las interfaces son apropiadas en el situaciones siguientes:

  1. Varias clases no relacionadas quieren soportar el protocolo.
  2. Estas clases ya tienen clases base establecidas (para ejemplo, algunos son controles de interfaz de usuario (UI) , y algunos son servicios web XML).
  3. La agregación no es apropiada ni practicable. En todos los demás situación, la herencia de clase es un mejor modelo.
 6
Author: YeahStu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-18 20:25:45

Una diferencia importante es que solo puede heredar una clase base, pero puede implementar muchas interfaces. Por lo tanto, solo desea usar una clase base si está absolutamente seguro de que no necesitará heredar una clase base diferente. Además, si encuentra que su interfaz se está haciendo grande, debería comenzar a dividirla en algunas piezas lógicas que definan la funcionalidad independiente, ya que no hay una regla que su clase no pueda implementar todos ellos (o que puedes definir una interfaz diferente que solo los hereda todos para agruparlos).

 4
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-04 21:23:35

Cuando empecé a aprender sobre programación orientada a objetos, cometí el error fácil y probablemente común de usar la herencia para compartir un comportamiento común, incluso cuando ese comportamiento no era esencial para la naturaleza del objeto.

Para seguir construyendo sobre un ejemplo muy usado en esta pregunta en particular, hay un montón de cosas que se pueden acariciar - novias, coches, mantas borrosas... - así que podría haber tenido una clase Petable que proporcionó este comportamiento común, y varios clases heredadas de él.

Sin embargo, ser petable no es parte de la naturaleza de ninguno de estos objetos. Hay conceptos mucho más importantes que son esenciales para su naturaleza: la novia es una persona, el automóvil es un vehículo terrestre, el gato es un mamífero...

Los comportamientos deben asignarse primero a las interfaces (incluida la interfaz predeterminada de la clase), y promoverse a una clase base solo si son (a) comunes a un gran grupo de clases que son subconjuntos de una clase más grande - en el mismo sentido que "gato" y "persona"son subconjuntos de "mamífero".

El problema es que, después de entender el diseño orientado a objetos lo suficientemente mejor que lo hice al principio, normalmente lo hará automáticamente sin siquiera pensarlo. Así que la pura verdad de la declaración "código para una interfaz, no una clase abstracta" se vuelve tan obvia que le cuesta creer que alguien se molestaría en decirlo, y comenzar a tratar de leer otros significados en ella.

Otra cosa Añadiría que si una clase es puramente abstracta - sin miembros o métodos no abstractos, no heredados expuestos al hijo, padre o cliente-entonces ¿por qué es una clase? Podría ser reemplazado, en algunos casos por una interfaz y en otros casos por Null.

 4
Author: Don Edwards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-11 22:14:03

Fuente: http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C# es un lenguaje maravilloso que ha madurado y evolucionado en los últimos 14 años. Esto es genial para nosotros los desarrolladores porque un lenguaje maduro nos proporciona una gran cantidad de características del lenguaje que están a nuestra disposición.

Sin embargo, con mucho poder se convierte en mucha responsabilidad. Algunas de estas características pueden ser mal utilizadas, o a veces es difícil entender por qué elegiría usar una característica sobre otra. A lo largo de los años, una característica con la que he visto a muchos desarrolladores luchar es cuándo elegir usar una interfaz o elegir usar una clase abstracta. Ambos tienen allí ventajas y desventajas y la hora y el lugar correctos para utilizar cada uno. Pero, ¿cómo decidimos???

Ambos prevén la reutilización de la funcionalidad común entre tipos. La diferencia más obvia de inmediato es que las interfaces no proporcionan ninguna implementación para su funcionalidad, mientras que las clases abstractas le permiten implementar algún comportamiento" base "o" predeterminado "y luego tienen la capacidad de" anular " este comportamiento predeterminado con los tipos derivados de las clases si es necesario.

Todo esto está muy bien y proporciona una gran reutilización del código y se adhiere al principio SECO (No te repitas) del desarrollo de software. Las clases abstractas son geniales para usar cuando tienes una relación" es una".

Por ejemplo: Un golden retriever "es un" tipo de perro. También lo es un caniche. Ambos puede ladrar,como todos los perros. Sin embargo, es posible que desee afirmar que el parque de caniches es significativamente diferente de la corteza de perro "predeterminada". Por lo tanto, podría tener sentido para usted implementar algo de la siguiente manera:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Como puede ver, esta sería una gran manera de mantener su código SECO y permitir que se llame a la implementación de la clase base cuando cualquiera de los tipos solo puede confiar en la corteza predeterminada en lugar de una implementación de caso especial. Las clases como GoldenRetriever, Boxer, Lab podrían podrían heredar la corteza" predeterminada " (clase bass) sin cargo solo porque implementan la clase abstracta Dog.

Pero estoy seguro de que ya lo sabías.

Usted está aquí porque quiere entender por qué es posible que desee elegir una interfaz en lugar de una clase abstracta o viceversa. Bueno, una razón por la que es posible que desee elegir una interfaz en lugar de una clase abstracta es cuando no tiene o desea evitar una implementación predeterminada. Esto es generalmente porque los tipos que están implementando el interfaz no relacionada en una relación" es una". En realidad, no tienen que estar relacionados en absoluto, excepto por el hecho de que cada tipo "es capaz" o tiene "la capacidad" de hacer algo o tener algo.

Ahora, ¿qué diablos significa eso? Bueno, por ejemplo: un humano no es un pato and y un pato no es un humano. Bastante obvio. Sin embargo, tanto un pato como un humano tienen "la capacidad" de nadar (dado que el humano pasó sus lecciones de natación en 1er grado :) ). Además, dado que un pato no es un humano o vicio al contrario, esto no es una relación" es", sino una relación" es capaz " y podemos usar una interfaz para ilustrar que:

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

El uso de interfaces como el código anterior le permitirá pasar un objeto a un método que "es capaz" de hacer algo. Al código no le importa cómo lo hace All Todo lo que sabe es que puede llamar al método Swim en ese objeto y ese objeto sabrá qué comportamiento tomar en tiempo de ejecución basado en su tipo.

Una vez más, esto ayuda a que su código permanezca SECO que no tendría que escribir varios métodos que están llamando al objeto para preformar la misma función principal (ShowHowHumanSwims (human), ShowHowDuckSwims(duck), etc.)

El uso de una interfaz aquí permite que los métodos de llamada no tengan que preocuparse por qué tipo es cuál o cómo se implementa el comportamiento. Solo sabe que dada la interfaz, cada objeto tendrá que haber implementado el método de natación por lo que es seguro llamarlo en su propio código y permitir que el comportamiento del método de natación sea manejado dentro de su propia clase.

Resumen:

Así que mi regla general principal es usar una clase abstracta cuando se desea implementar una funcionalidad "predeterminada" para una jerarquía de clases o/y las clases o tipos con los que se está trabajando comparten una relación "es una" (ej. caniche "es un" tipo de perro).

Por otro lado, use una interfaz cuando no tenga una relación "es una" pero tenga tipos que compartan "la capacidad" de hacer algo o tener algo (ej. Duck "no es" un humano. Obstante, pato y humano comparten "la habilidad" de nadar).

Otra diferencia a tener en cuenta entre las clases abstractas y las interfaces es que una clase puede implementar de una a muchas interfaces, pero una clase solo puede heredar de UNA clase abstracta (o cualquier clase para el caso). Sí, puede anidar clases y tener una jerarquía de herencia (que muchos programas tienen y deberían tener), pero no puede heredar dos clases en una definición de clase derivada (esta regla se aplica a C#. En algunos otros idiomas que son capaces de hacer esto, generalmente solo debido a la falta de interfaces en estos lenguajes).

También recuerde al usar interfaces que se adhieran al Principio de Segregación de Interfaces (ISP). ISP afirma que ningún cliente debe ser obligado a depender de métodos que no utiliza. Por esta razón, las interfaces deben centrarse en tareas específicas y suelen ser muy pequeñas (ej. IDisposable, IComparable ).

Otro consejo es que si está desarrollando pequeños y concisos bits de funcionalidad, use interfaces. Si usted es el diseño de grandes unidades funcionales, utilizar una clase abstracta.

Espero que esto aclare las cosas para algunas personas!

También si se te ocurren mejores ejemplos o quieres señalar algo, ¡por favor hazlo en los comentarios a continuación!

 4
Author: Jason Roell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-09 20:42:00

Comentarios anteriores sobre el uso de clases abstractas para la implementación común está definitivamente en la marca. Un beneficio que aún no he visto mencionado es que el uso de interfaces hace que sea mucho más fácil implementar objetos simulados con el propósito de pruebas unitarias. Definir IPet y PetBase como Jason Cohen describió le permite simular diferentes condiciones de datos fácilmente, sin la sobrecarga de una base de datos física (hasta que decida que es hora de probar la cosa real).

 3
Author: Scott Lawrence,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 22:01:10

No utilice una clase base a menos que sepa lo que significa, y que se aplica en este caso. Si se aplica, úselo, de lo contrario, use interfaces. Pero tenga en cuenta la respuesta sobre las interfaces pequeñas.

La herencia pública se usa en exceso en OOD y expresa mucho más de lo que la mayoría de los desarrolladores se dan cuenta o están dispuestos a cumplir. Véase el Principio de Sustituibilidad de Liskov

En resumen, si A "es a" B entonces A no requiere más de B y no entrega menos de B, para cada método exponer.

 3
Author: theschmitzer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-12 21:24:45

Conceptualmente, una Interfaz se utiliza para definir formal y semi-formalmente un conjunto de métodos que un objeto proporcionará. Formalmente significa un conjunto de nombres y firmas de métodos, semi-formalmente significa documentación legible por humanos asociada con esos métodos. Las interfaces son solo descripciones de una API (después de todo, API significa Programador de aplicaciones Interfaz), no pueden contener ninguna implementación y no es posible usar o ejecutar una Interfaz. Sólo explicitan el contrato de cómo debe interactuar con un objeto.

Las clases proporcionan una implementación, pueden declarar que implementan cero, una o más interfaces. Si una Clase está destinada a ser heredada, la convención es anteponer el nombre de la clase con "Base".

Hay una distinción entre una Clase Base y una Clase Base Abstracta (ABC). ABCs mezcla la interfaz y la implementación juntas. Abstract fuera de la programación de computadoras significa "resumen", es decir "Abstract = = Interface". abstracto La clase Base puede describir tanto una interfaz como una implementación vacía, parcial o completa que se pretende heredar.

Las opiniones sobre cuándo usar Interfaces versus Clases Base Abstractas versus solo Clases variarán enormemente en función de lo que esté desarrollando y en qué idioma esté desarrollando. Las interfaces a menudo se asocian solo con lenguajes de tipo estático como Java o C#, pero los lenguajes de tipo dinámico también pueden tener Interfaces y Abstractos Clases Base. En Python, por ejemplo, la distinción se hace clara entre una Clase, que declara que implementa una Interfaz, y un objeto, que es una instancia de una Clase, y se dice que proporciona esa Interfaz. Es posible en un lenguaje dinámico que dos objetos que son instancias de la misma Clase, puedan declarar que proporcionan completamente diferentes interfaces. En Python esto solo es posible para atributos de objeto, mientras que los métodos son compartidos entre todos los objetos de una Clase. Sin embargo, en Ruby, los objetos pueden tener métodos por instancia, por lo que es posible que la Interfaz entre dos objetos de la misma Clase pueda variar tanto como el programador desee (sin embargo, Ruby no tiene ninguna forma explícita de declarar Interfaces).

En los lenguajes dinámicos, la Interfaz de un objeto a menudo se asume implícitamente, ya sea introspectando un objeto y preguntándole qué métodos proporciona (Mire Antes de Saltar) o preferiblemente simplemente intentando use la Interfaz deseada en un objeto y capture excepciones si el objeto no proporciona esa Interfaz (Más fácil de Pedir Perdón que Permiso). Esto puede conducir a" falsos positivos " donde dos Interfaces tienen el mismo nombre de método pero son semánticamente diferentes, sin embargo, la compensación es que su código es más flexible ya que no necesita especificar demasiado por adelantado para anticipar todos los posibles usos de su código.

 3
Author: Wheat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-15 22:34:03

Otra opción a tener en cuenta es usar la relación "has-a", también conocida como "se implementa en términos de composición" o"."A veces esta es una forma más limpia y flexible de estructurar las cosas que usar "es-una" herencia.

Puede no tener tanto sentido lógicamente decir que el Perro y el Gato "tienen" una Mascota, pero evita las trampas comunes de herencia múltiple:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Sí, este ejemplo muestra que hay mucha duplicación de código y falta de elegancia involucrada en hacer las cosas por aquí. Pero uno también debe apreciar que esto ayuda a mantener el Perro y el Gato desacoplados de la clase de Mascotas (en que el Perro y el Gato no tienen acceso a los miembros privados de la Mascota), y deja espacio para que el Perro y el Gato hereden de otra cosa possibly posiblemente la clase de Mamíferos.

La composición es preferible cuando no se requiere acceso privado y no es necesario referirse a Perros y Gatos utilizando referencias/punteros genéricos para mascotas. Las interfaces le dan esa capacidad de referencia genérica y pueden ayudar a cortar abajo en la verbosidad de su código, pero también pueden ofuscar las cosas cuando están mal organizados. La herencia es útil cuando necesita acceso de miembro privado, y al usarla se está comprometiendo a acoplar altamente sus clases de Perro y Gato a su clase de Mascota, lo cual es un alto costo a pagar.

Entre herencia, composición e interfaces no hay una manera que siempre sea correcta, y ayuda considerar cómo se pueden usar las tres opciones en armonía. De los tres, la herencia es típicamente la opción que se debe utilizar con menos frecuencia.

 3
Author: Parappa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-03 22:37:18

Prefiera las interfaces sobre las clases abstractas

Justificación, los puntos principales a considerar [dos ya mencionados aquí] son:

  • Las interfaces son más flexibles, porque una clase puede implementar múltiples interfaz. Dado que Java no tiene herencia múltiple, usando clases abstractas evita que sus usuarios utilicen cualquier otra clase jerarquía. En general, prefiera las interfaces cuando no hay por defecto implementaciones o estado. Las colecciones Java ofrecen buenos ejemplos de esto (Mapa, Conjunto, etc.).
  • Las clases abstractas tienen la ventaja de permitir un mejor avance compatibilidad. Una vez que los clientes utilizan una interfaz, no puede cambiarla; si usan una clase abstracta, aún puedes agregar comportamiento sin rompiendo el código existente. Si la compatibilidad es una preocupación, considere usar clases abstractas.
  • Incluso si tiene implementaciones predeterminadas o estado interno, considere ofrecer una interfaz y una implementación abstracta de la misma. Esta voluntad ayudar a los clientes, pero aún así permitirles una mayor libertad si deseado [1].
    Por supuesto, el tema ha sido discutido en profundidad en otros lugares [2,3].

[1] Añade más código, por supuesto, pero si la brevedad es su principal preocupación, probablemente debería haber evitado Java en primer lugar!

[2] Joshua Bloch, Java efectivo, ítems 16-18.

[3] http://www.codeproject.com/KB/ar...

 3
Author: Jaimin Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-27 11:43:54

Depende de sus requisitos. Si IPet es lo suficientemente simple, preferiría implementarlo. De lo contrario, si PetBase implementa una tonelada de funcionalidad que no desea duplicar, entonces tenga en ella.

El inconveniente de implementar una clase base es el requisito de override (o new) métodos existentes. Esto los convierte en métodos virtuales, lo que significa que debe tener cuidado con la forma en que usa la instancia del objeto.

Por último, la única herencia de.NET me mata. Un ejemplo ingenuo: Supongamos que está haciendo un control de usuario, por lo que hereda UserControl. Pero, ahora estás bloqueado para heredar PetBase. Esto le obliga a reorganizarse, como hacer un miembro de la clase PetBase, en su lugar.

 2
Author: spoulson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 15:29:09

@ Joel: Algunos lenguajes (por ejemplo, C++) permiten la herencia múltiple.

 2
Author: Einar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 15:30:55

Normalmente no implemento ninguno hasta que lo necesito. Estoy a favor de las interfaces sobre las clases abstractas porque eso da un poco más de flexibilidad. Si hay un comportamiento común en algunas de las clases herederas, lo muevo hacia arriba y hago una clase base abstracta. No veo la necesidad de ambos, ya que esencialmente sirven el mismo propósito, y tener ambos es un mal olor a código (imho) que la solución ha sido sobre-diseñada.

 2
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 15:31:47

Con respecto a C#, en algunos sentidos las interfaces y las clases abstractas pueden ser intercambiables. Sin embargo, las diferencias son: i) las interfaces no pueden implementar código; ii) debido a esto, las interfaces no pueden llamar a la subclase de la pila; y iii) solo se puede heredar la clase abstracta en una clase, mientras que varias interfaces pueden implementarse en una clase.

 2
Author: CarneyCode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-23 11:51:10

He encontrado que un patrón de Interfaz > Abstract > Concrete funciona en el siguiente caso de uso:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

La clase abstracta define los atributos compartidos predeterminados de las clases concretas, pero impone la interfaz. Por ejemplo:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Ahora, ya que todos los mamíferos tienen pelo y pezones (AFAIK, no soy zoólogo), podemos enrollar esto en la clase base abstracta

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

Y luego las clases concretas simplemente definen que caminan erguidos.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

Este diseño es agradable cuando hay un montón de clases concretas, y usted no quiere mantener repetitivo solo para programar a una interfaz. Si se agregaran nuevos métodos a la interfaz, rompería todas las clases resultantes, por lo que todavía está obteniendo las ventajas del enfoque de interfaz.

En este caso, el resumen podría ser concreto; sin embargo, la designación abstracta ayuda a enfatizar que este patrón se está empleando.

 2
Author: Adam Hughes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-04 18:28:29

Heredero de una clase base debe tener una relación. La interfaz representa Una relación" implementa una". Así que solo utilice una clase base cuando sus herederos mantendrán la es una relación.

 1
Author: Aaron Fischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-11 18:23:33

Utilice interfaces para hacer cumplir un contrato entre familias de clases no relacionadas. Por ejemplo, puede tener métodos de acceso comunes para clases que representan colecciones, pero que contienen datos radicalmente diferentes, es decir, una clase puede representar un conjunto de resultados de una consulta, mientras que la otra puede representar las imágenes de una galería. Además, puede implementar múltiples interfaces, lo que le permite combinar (y significar) las capacidades de la clase.

Usar Herencia cuando las clases tienen un común relación y por lo tanto tienen una similair firma estructural y de comportamiento, es decir, Coche, Moto, Camión y SUV son todos los tipos de vehículos de carretera que podrían contener un número de ruedas, una velocidad máxima

 1
Author: sunwukung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-14 21:26:25