why interfaces example crear classes oop interface language-agnostic base-class static-typing

oop - interfaces - Interfaz vs clase base



typescript constructor interface (30)

Interfaces

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

Clases de base

  • Le permite agregar alguna implementación predeterminada que obtenga gratis por derivación
  • Excepto C ++, solo se puede derivar de una clase. Incluso si pudiera provenir de múltiples clases, usualmente 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 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 una jerarquía de tipos (IS A)
  • Las derivaciones de clase pueden llegar a depender de algún comportamiento básico (tienen un conocimiento complejo de la implementación de los padres). Las cosas pueden ser complicadas si realiza un cambio en la implementación base para un individuo y rompe a los demás.

¿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 básica de los métodos?

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


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

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

aclamaciones


Bueno, Josh Bloch se dijo en Effective Java 2d :

Prefiere interfaces sobre clases abstractas

Algunos puntos principales:

  • Las clases existentes se pueden actualizar fácilmente para implementar una nueva interfaz . Todo lo que tiene que hacer es agregar los métodos requeridos si aún no existen y agregar una cláusula de implementos a la declaración de clase.

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

  • Las interfaces permiten la construcción de estructuras tipo no jerárquicas . Las jerarquías de tipos son excelentes para organizar algunas cosas, pero otras no encajan perfectamente en una jerarquía rígida.

  • Las interfaces permiten mejoras de funcionalidad seguras y potentes a través del lenguaje de envoltura por clase. Si usa clases abstractas para definir tipos, deja al programador que desea agregar funcionalidad sin otra alternativa que usar la herencia.

Además, puede combinar las virtudes de las interfaces y las clases abstractas proporcionando una clase de implementación esquelética abstracta para cada interfaz no trivial que exporte.

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

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


El caso de las Clases de base sobre interfaces se explicó bien en las Directrices de codificación de .NET Submain:

Clases de base frente a interfaces Un tipo de interfaz es una descripción parcial de un valor, potencialmente compatible con muchos tipos de objetos. Utilice clases base en lugar de interfaces siempre que sea posible. Desde la perspectiva del control de versiones, las clases son más flexibles que las interfaces. Con una clase, puede enviar la Versión 1.0 y luego, en la Versión 2.0, agregar un nuevo método a la clase. Mientras el método no sea abstracto, cualquier clase derivada existente continuará funcionando sin cambios.

Debido a que las interfaces no admiten la herencia de implementación, el patrón que se aplica a las clases no se aplica a las interfaces. Agregar un método a una interfaz es equivalente a agregar un método abstracto a una clase base; cualquier clase que implemente la interfaz se interrumpirá porque la clase no implementa el nuevo método. Las interfaces son apropiadas en las siguientes situaciones:

  1. Varias clases no relacionadas quieren apoyar el protocolo.
  2. Estas clases ya tienen clases base establecidas (por ejemplo, algunas son controles de interfaz de usuario (UI) y otras son servicios web XML).
  3. La agregación no es apropiada o practicable. En todas las demás situaciones, la herencia de clase es un mejor modelo.

El estilo moderno es definir IPet y PetBase.

La ventaja de la interfaz es que otro código puede usarlo 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 también para ahorrar tiempo y código.


En general, debe 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) que implemente esa interfaz. Al usar interfaces pequeñas y distintas, puede usar múltiples para mejorar aún más la flexibilidad. Las interfaces permiten la máxima flexibilidad y portabilidad de los tipos a través de los límites. Al pasar 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 proporciona la máxima flexibilidad. Esto es absolutamente cierto cuando se programa en un modo 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.


Esto es bastante específico de .NET, pero el libro Framework Design Guidelines sostiene que, en general, las clases ofrecen 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 la vincula. Mientras haga las modificaciones correctas, lo que incluye agregar nuevas funciones, podrá ampliar y desarrollar su código.

Krzysztof Cwalina dice en la página 81:

En el transcurso de las tres versiones de .NET Framework, he hablado sobre esta guía con unos cuantos desarrolladores de nuestro equipo. Muchos de ellos, incluidos aquellos que inicialmente no estaban de acuerdo con las directrices, han dicho que lamentan haber enviado algunas API como interfaz. No he escuchado de un solo caso en el que alguien se arrepintiera de haber enviado una clase.

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


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 utilizo como base para otra cosa.

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

Interfaz vs. clase abstracta

La elección de interfaces y clases abstractas no es una proposición de uno u otro. 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 le permiten definir algunos comportamientos; obligan a tus subclases a proporcionar otras. 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 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. Entonces, en lugar de intentar definir ese comportamiento en sí mismo, la clase base abstracta puede declarar métodos de cierre e inicio abstractos. 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 manera de crear jerarquías de herencia planificadas. También son una buena opción para las clases sin hojas en las jerarquías de clases.

Clase vs interfaz

Algunos dicen que deberías 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 usan. Un reproductor de medios 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, deberá actualizar esas declaraciones de casos cada vez que agregue un nuevo algoritmo. Con todo, esta no es una forma de programación muy orientada a objetos.

Con el patrón de Estrategia, simplemente puede encapsular el algoritmo detrás de un objeto. Si lo hace, puede proporcionar nuevos complementos de medios en cualquier momento. Llamemos a la clase plug-in MediaStrategy. Ese objeto tendría un método: playStream (Stream s). Entonces, para agregar un nuevo algoritmo, simplemente extendemos nuestra clase de algoritmo. Ahora, cuando el programa encuentra el nuevo tipo de medios, simplemente delega la reproducción de la transmisión a nuestra estrategia de medios. Por supuesto, necesitará un poco de plomería para instanciar adecuadamente 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, debería favorecer las interfaces sobre la herencia cuando desee 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 una jerarquía de herencia particular. Java no permite la herencia múltiple, por lo que no puede extender algo que le brinde una implementación útil o más identidad de tipo.


Juan

Me gusta pensar en las interfaces como una forma de caracterizar una clase. Una clase particular de raza de perro, digamos un YorkshireTerrier, puede ser un descendiente de la clase de perro padre, pero también es implemento IFurry, IStubby e 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''s y lanzarlos a mi colección Ocean. Así que ahora puedo alcanzar un conjunto particular de objetos y encontrar objetos que cumplan con los criterios que estoy viendo sin inspeccionar a la clase demasiado de cerca.

Me parece 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, generalmente 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 usted debe codificar a la interfaz. Eso está bien, pero terminas con una gran cantidad de interfaces uno a uno para las clases y eso hace que las cosas se confundan. Entiendo que la idea es que realmente no cuesta nada hacer y ahora puede intercambiar cosas con facilidad. Sin embargo, encuentro que rara vez hago eso. La mayoría de las veces simplemente estoy modificando la clase existente y tengo los mismos problemas que siempre tuve si la interfaz pública de esa clase necesita cambiar, excepto que ahora tengo que cambiarla en dos lugares.

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

La otra parte de esto, sin embargo, es si deberían tener la misma clase base. La pregunta es si necesitan ser tratados de manera amplia como la misma cosa. Ciertamente, ambos son animales, pero ¿se ajusta eso a cómo vamos a usarlos juntos?

Digamos 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 de animales cruzados?

¿Incluso necesitan estar vinculados entre sí? ¿Es suficiente saber que ambos son compatibles con IPet?

A menudo siento el deseo de derivar una jerarquía de clases completa cuando realmente 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, generalmente encuentro 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 Platypus. Ahora, toda mi jerarquía de clases se basa en el caso extraño y tengo un montón de código perdido.

También puede encontrar en algún momento que no todos los gatos son compatibles con IPT (como el que no tiene pelo). Ahora puede mover esa Interfaz a todas las clases derivadas que se ajusten. Encontrarás que un cambio mucho menos importante que los gatos de repente ya no se derivan de PettableBase.


Las interfaces deben ser pequeñas. Realmente pequeño. Si realmente está desglosando sus objetos, es probable que sus interfaces solo contengan algunos métodos y propiedades muy específicos.

Las clases abstractas son atajos. ¿Hay cosas que todos los derivados de PetBase compartan que pueda codificar una vez y que haya terminado? Si es así, es hora de una clase abstracta.

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

Las clases abstractas pueden contener varias interfaces. Su clase abstracta PetBase puede implementar IPet (las mascotas tienen dueños) y la IDigestión (las 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 un MammalPetBase que amplíe PetBase y agregar IMammal. FishBase podría tener PetBase y agregar IFish. IFish tendría ISwim y IUnderwaterBreather como interfaces.

Sí, mi ejemplo es demasiado complicado para el ejemplo simple, pero eso es parte de la gran cosa acerca de cómo las interfaces y las clases abstractas trabajan juntas.


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

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

Las 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 #.


Recomiendo usar la composición en lugar de la herencia siempre que sea posible. Use interfaces pero use objetos miembros para la implementación base. De esa 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 todo el comportamiento mutable está definido en objetos auxiliares.

Entonces, en lugar de IPet o PetBase, podría terminar con una mascota que tenga un parámetro IFurBehavior. El parámetro IFurBehavior se establece mediante el método CreateDog () de PetFactory. Este es el parámetro que se llama para el método shed ().

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

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


También ten en cuenta que no debes dejarte llevar por OO ( ver blog ) y siempre modela los objetos según el comportamiento requerido, si estuvieras diseñando una aplicación en la que el único comportamiento que requieras fuera un nombre genérico y una especie para un animal, entonces solo necesitarías una clase Animal con una propiedad para el nombre, en lugar de millones de clases para cada animal posible en el mundo.


Tengo una regla de oro aproximada

Funcionalidad: probable que sea diferente en todas las partes: Interfaz.

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

Los datos y la funcionalidad realmente funcionan, si se amplían solo con cambios leves: clase ordinaria (concreta)

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

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

Esto es muy rudo y está listo y no está estrictamente definido, pero existe un espectro desde las interfaces donde todo está destinado a ser cambiado a enumeraciones donde todo se arregla un poco como un archivo de solo lectura.


Tomemos su ejemplo de una clase de perros y gatos, 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 tienes una clase abstracta de mamíferos, para ambos:

public abstract class Mammal

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

  • Alimentar
  • Compañero

Todos los cuales 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, el rinoceronte y los hipopótamos no son exactamente animales con 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 perro y un gato; Poner sus implementaciones en una clase abstracta para heredar será una mala idea.

Sus definiciones de perros y gatos ahora deberían verse como:

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

En teoría, puede anularlos desde una clase base más alta, pero esencialmente una interfaz le permite agregar solo las cosas que necesita en una clase sin la necesidad de herencia.

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


Una diferencia importante es que solo puede heredar una clase base, pero puede implementar muchas interfaces. Por lo tanto, solo desea utilizar una clase base si está absolutamente seguro de que no necesitará heredar también una clase base diferente. Además, si encuentra que su interfaz se está volviendo grande, debería comenzar a buscar dividirla en algunas piezas lógicas que definen una funcionalidad independiente, ya que no hay una regla que indique que su clase no puede implementarlas todas (o que puede definir una diferente). interfaz que solo los hereda todos para agruparlos).


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 de lenguaje que están a nuestra disposición.

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

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

Todo esto está muy bien y proporciona una gran reutilización del código y se adhiere al principio DRY (Don''t Repeat Yourself) de desarrollo de software. Las clases de resumen son excelentes para usar cuando tienes una relación "es un".

Por ejemplo: un golden retriever "es un" tipo de perro. Así es un caniche. Ambos pueden ladrar, como todos los perros. Sin embargo, es posible que desee indicar que el parque de caniches es significativamente diferente a la corteza de perro "predeterminada". Por lo tanto, podría tener sentido que implementes 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 excelente manera de mantener su código en 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 un caso especial. Las clases como GoldenRetriever, Boxer, Lab podrían heredar la Corteza "por defecto" (clase de bajo) sin costo alguno solo porque implementan la clase abstracta Perro.

Pero estoy seguro de que ya lo sabías.

Usted está aquí porque quiere comprender por qué podría elegir una interfaz en lugar de una clase abstracta o viceversa. Bueno, una de las razones por las que puede elegir una interfaz en lugar de una clase abstracta es cuando no tiene o desea evitar una implementación predeterminada. Esto generalmente se debe a que los tipos que implementan la interfaz no están relacionados 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 ... 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 clases de natación en 1er grado :)). Además, dado que un pato no es un ser humano o viceversa, esto no es una relación "es una", sino una relación "es capaz" y podemos usar una interfaz para ilustrar eso:

// 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 "puede" hacer algo. Al código no le importa cómo lo hace ... Todo lo que sabe es que puede invocar el método Swim en ese objeto y ese objeto sabrá qué comportamiento toma en tiempo de ejecución según su tipo.

Una vez más, esto ayuda a que tu código se mantenga SECO para que no tengas que escribir varios métodos que llamen al objeto para realizar la misma función central (ShowHowHumanSwims (humano), 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. Simplemente sabe que, dada la interfaz, cada objeto tendrá que haber implementado el método Swim para que sea seguro llamarlo en su propio código y permitir que el comportamiento del método Swim se maneje dentro de su propia clase.

Resumen:

Por lo tanto, mi regla general es usar una clase abstracta cuando desee implementar una funcionalidad "predeterminada" para una jerarquía de clases y / y las clases o tipos con los que está trabajando comparten una relación "is a" (por ejemplo, poodle “es una "Tipo de perro).

Por otro lado, use una interfaz cuando no tenga una relación "es un", sino que tenga tipos que compartan "la capacidad" de hacer algo o tener algo (por ejemplo, pato "no es" un humano. Sin embargo, 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 una o muchas interfaces, pero una clase solo puede heredar de UNA clase abstracta (o cualquier clase). 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 otros idiomas, puede hacerlo solo por la falta de interfaces en estos idiomas).

También recuerde cuando use interfaces para adherirse al principio de segregación de interfaz (ISP). El ISP establece que ningún cliente debe ser obligado a depender de los métodos que no utiliza. Por esta razón, las interfaces deben centrarse en tareas específicas y suelen ser muy pequeñas (por ejemplo, IDisposable, IComparable).

Otra sugerencia es si está desarrollando pequeños bits de funcionalidad concisa, use interfaces. Si está diseñando unidades funcionales grandes, use una clase abstracta.

Espero que esto aclare las cosas para algunas personas!

Además, si puede encontrar ejemplos mejores o quiere señalar algo, ¡hágalo en los comentarios a continuación!


Prefiere interfaces sobre clases abstractas

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

  • Las interfaces son más flexibles, porque una clase puede implementar múltiples interfaces. Como Java no tiene herencia múltiple, el uso de clases abstractas evita que los usuarios utilicen cualquier otra jerarquía de clases. En general, prefiera las interfaces cuando no hay implementaciones o estados predeterminados. Las colecciones de Java ofrecen buenos ejemplos de esto (Mapa, Conjunto, etc.).
  • Las clases abstractas tienen la ventaja de permitir una mejor compatibilidad hacia adelante. Una vez que los clientes usan una interfaz, no puede cambiarla; Si usan una clase abstracta, aún puede agregar comportamiento sin romper 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 . Esto ayudará a los clientes, pero aún así les permitirá una mayor libertad si se desea [1].
    Por supuesto, el tema ha sido discutido extensamente en otra parte [2,3].

[1] Por supuesto, agrega más código, pero si la brevedad es su principal preocupación, ¡probablemente debió haber evitado Java en primer lugar!

[2] Joshua Bloch, Effective Java, artículos 16-18.

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


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


Cuando comencé 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 desarrollando un ejemplo que se usa mucho en esta pregunta en particular, hay muchas cosas que son petable: novias, autos, mantas borrosas ... así que podría haber tenido una clase de Petable que proporcionó este comportamiento común y varias clases heredadas. de eso.

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 que comprendes el diseño orientado a objetos lo suficientemente mejor que yo al principio, normalmente lo harás automáticamente sin siquiera pensarlo. Así que la verdad desnuda de la afirmació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 leer otros significados.

Otra cosa que agregaría es que si una clase es puramente abstracta, sin miembros ni métodos no abstractos y no heredados expuestos a hijos, padres o clientes, ¿por qué es una clase? Podría ser reemplazado, en algunos casos por una interfaz y en otros casos por Null.


He encontrado que un patrón de Interfaz> Abstracto> Concreto 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 aplica la interfaz. Por ejemplo:

public interface IPet{ public boolean hasHair(); public boolean walksUprights(); public boolean hasNipples(); }

Ahora, como todos los mamíferos tienen pelo y pezones (AFAIK, no soy un zoólogo), podemos incluir 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 muchas clases concretas, y no desea mantener el boilerplate solo para programarlo en una interfaz. Si se agregaran nuevos métodos a la interfaz, se romperían todas las clases resultantes, por lo que aún se obtienen las ventajas del enfoque de la interfaz.

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


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 el código; ii) debido a esto, las interfaces no pueden llamar a la pila a una subclase; y iii) solo la clase abstracta puede heredarse en una clase, mientras que múltiples interfaces pueden implementarse en una clase.


Conceptualmente, una interfaz se utiliza para definir de manera formal y semi-formal un conjunto de métodos que proporcionará un objeto. Formalmente significa un conjunto de nombres y firmas de métodos, y 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 Application Programmer Interface ), no pueden contener ninguna implementación y no es posible usar o ejecutar una interfaz. Solo hacen explícito el contrato de cómo debes interactuar con un objeto.

Las clases proporcionan una implementación, pueden declarar que implementan cero, una o más interfaces. Si se pretende heredar una Clase, la convención es prefijar el nombre de la Clase con "Base".

Hay una distinción entre una clase base y una clase base abstracta (ABC). ABCs mezclan interfaz e implementación juntos. Resumen fuera de programación de computadora significa "resumen", que es "Resumen == Interfaz". Una clase base abstracta 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 el idioma en el que esté desarrollando. Las interfaces a menudo se asocian solo con lenguajes de tipo estático como Java o C #, pero Los idiomas tipificados dinámicamente también pueden tener interfaces y clases base abstractas. 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 sean ambas instancias de la misma Clase, puedan declarar que proveen completamente diferenteinterfaces En Python esto solo es posible para los atributos de los objetos, mientras que los métodos son estados 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 lo desee el programador (sin embargo, Ruby no tiene ninguna forma explícita de declarar Interfaces).

En los lenguajes dinámicos, la Interfaz con un objeto a menudo se asume implícitamente, ya sea mediante la introspección de un objeto y preguntándole qué métodos proporciona (mirar antes de saltar) o, preferiblemente, intentando simplemente usar la Interfaz deseada en un objeto y detectando excepciones si el objeto no proporciona esa interfaz (es más fácil pedir perdón que el permiso). Esto puede llevar a "falsos positivos" en los que 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 para anticipar todos los usos posibles de su código.


Depende de sus necesidades. Si IPet es lo suficientemente simple, preferiría implementarlo. De lo contrario, si PetBase implementa una tonelada de funcionalidad que no quiere duplicar, entonces hágalo.

La desventaja de implementar una clase base es el requisito de override(o new) los 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 herencia única de .NET me mata. Un ejemplo ingenuo: digamos que está haciendo un control de usuario, por lo que hereda UserControl. Pero, ahora estás excluido también de heredar PetBase. Esto obliga a reorganizar, como hacer un PetBasemiembro de la clase, en su lugar.


Los comentarios anteriores sobre el uso de clases abstractas para la implementación común están definitivamente en la marca. Un beneficio que no he visto aún es que el uso de interfaces hace que sea mucho más fácil implementar objetos simulados con el fin de realizar pruebas unitarias. Definir IPet y PetBase como describió Jason Cohen 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 realidad).


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

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. Ver el principio de substitución de Liskov.

En resumen, si A "es una" B, entonces A no requiere más que B y entrega nada menos que B, por cada método que exponga.


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

Puede que no tenga tanto sentido lógicamente decir que tanto el perro como el gato "tienen" una mascota, pero evita las trampas comunes de la 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 al hacer las cosas de esta manera. Pero también se debe apreciar que esto ayuda a mantener al perro y al gato separados de la clase de mascotas (ya 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. -posiblemente la clase de mamíferos.

La composición es preferible cuando no se requiere acceso privado y no es necesario referirse a Dog and Cat utilizando referencias / punteros genéricos para mascotas. Las interfaces le brindan esa capacidad de referencia genérica y pueden ayudar a reducir la verbosidad de su código, pero también pueden ofuscar cosas cuando están mal organizadas. La herencia es útil cuando necesita acceso privado para miembros, y al usarla, se compromete a unir sus clases de perros y gatos a su clase de mascotas, lo que representa un alto costo.

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


Por definición, la interfaz proporciona una capa para comunicarse con otro código. Todas las propiedades y métodos públicos de una clase son, de forma predeterminada, implementando una interfaz implícita. También podemos definir una interfaz como un rol, cuando alguna clase necesita desempeñar ese rol, tiene que implementarla dándole diferentes formas de implementación dependiendo de la clase que la implemente. Por lo tanto, cuando se habla de interfaz, se habla de polimorfismo y cuando se habla de clase base, se habla de herencia. Dos conceptos de oops !!!


Por lo general, tampoco implemento hasta que necesito uno. Yo prefiero 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 hereditarias, subo eso y formo una clase base abstracta. No veo la necesidad de ambos, ya que esencialmente tienen el mismo propósito, y tener ambos es un mal olor a código (imho) que la solución ha sido sobre-diseñada.


Un heredero de una clase base debe tener una relación "es una". La interfaz representa una relación "implementa una". Entonces, solo use una clase base cuando sus herederos mantendrán la relación.