¿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 perros y gatos. ¿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.
I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.
. No puedo entender lo que se entiende en el extracto. Podemos crear algunos tipos base y crear un tipo derivado para cualquiera de ellos, de modo que un desarrollador pueda elegir un tipo base. ¿Podría alguien explicar, por favor, qué me estoy perdiendo? Creo que puede ser parte de esta pregunta. ¿O debería publicar otro sobre el extracto específico?Respuestas:
Tomemos su 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 muuuuy demasiado generales). Supongamos que tiene una clase abstracta Mamífero, para ambos:
Esta clase base probablemente tendrá métodos predeterminados como:
Todos los cuales son comportamientos que tienen más o menos la misma implementación entre ambas especies. Para definir esto tendrás:
Ahora supongamos que hay otros mamíferos, que generalmente veremos en un zoológico:
Esto seguirá siendo válido porque en el núcleo de la funcionalidad
Feed()
yMate()
seguirá siendo el mismo.Sin embargo, las jirafas, los rinocerontes y los hipopótamos no son exactamente animales de los que puedes hacer mascotas. Ahí es donde una interfaz será útil:
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 verse así:
Teóricamente, 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 necesidad de herencia.
En consecuencia, debido a que generalmente solo puede heredar de una clase abstracta (en la mayoría de los lenguajes OO estáticamente tipados, es decir, las excepciones incluyen C ++) pero puede implementar múltiples interfaces, le permite construir objetos estrictamente según sea necesario .
fuente
Bueno, Josh Bloch se dijo en Effective Java 2d :
Prefiere interfaces sobre clases abstractas
Algunos puntos principales:
Por otro lado, las interfaces son muy difíciles de evolucionar. Si agrega un método a una interfaz, romperá todas sus implementaciones.
PD .: Compra el libro. Es mucho más detallado.
fuente
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 propósito (único) 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í laIDisposable
interfaz en C #.fuente
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 las interfaces se pueden mezclar.
Pero las clases base son útiles para implementaciones simples y utilidades comunes. Proporcione también una clase base abstracta para ahorrar tiempo y código.
fuente
Interfaces
Clases base
fuente
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 hacer que una clase abstracta (PetBase) implemente esa interfaz. Usando 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 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 de forma 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 sus padres, a menudo se dice que la herencia rompe la encapsulación". Yo creo que esto es cierto.
fuente
Esto es bastante específico de .NET, pero el libro de Pautas de diseño de marcos argumenta que, en general, las clases dan más flexibilidad en un marco en evolución. Una vez que se envía una interfaz, no tiene la oportunidad de cambiarla sin romper el código que usó esa interfaz. Sin embargo, con una clase, puede modificarla y no romper el código que la vincula. Mientras realice las modificaciones correctas, lo que incluye agregar nuevas funcionalidades, podrá ampliar y desarrollar su código.
Krzysztof Cwalina dice en la página 81:
Dicho esto, ciertamente hay un lugar para las interfaces. Como pauta general, siempre proporcione una implementación de clase base abstracta de una interfaz, si no es para nada más, como un ejemplo de una forma de implementar la interfaz. En el mejor de los casos, esa clase base ahorrará mucho trabajo.
fuente
Juan
Me gusta pensar en las interfaces como una forma de caracterizar una clase. Una clase de raza de perro en particular, digamos un YorkshireTerrier, puede ser descendiente de la clase de perro padre, pero también implementa IFurry, IStubby e IYippieDog. Entonces, la clase define qué es la clase, pero la interfaz nos dice cosas al respecto.
La ventaja de esto es que me permite, por ejemplo, reunir todos los IYippieDog y lanzarlos a mi colección Ocean. Así que ahora puedo alcanzar un conjunto particular de objetos y encontrar unos que cumplan con los criterios que estoy viendo 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, 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 que debe codificar la interfaz. Eso está bien, pero terminas con muchas interfaces uno a uno para las clases y eso hace que las cosas sean confusas. Entiendo que la idea es que realmente no cuesta nada hacer y ahora puede cambiar las cosas con facilidad. Sin embargo, encuentro que rara vez hago eso. La mayoría de las veces solo estoy modificando la clase existente en el lugar y tengo exactamente los mismos problemas que siempre tuve si la interfaz pública de esa clase necesita un cambio, excepto que ahora tengo que cambiarla en dos lugares.
Entonces, si piensas como yo, definitivamente dirías que Cat and Dog son IPettable. Es una caracterización que los une a ambos.
Sin embargo, la otra parte de esto es si 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 en cómo los vamos a usar juntos.
Digamos que quiero reunir todas las clases de animales y ponerlas en mi contenedor Ark.
¿O necesitan ser mamíferos? ¿Quizás necesitamos algún tipo de fábrica de ordeño de animales cruzados?
¿Necesitan siquiera estar unidos entre sí? ¿Es suficiente saber que ambos son IPettable?
A menudo siento el deseo de derivar una jerarquía de clases completa cuando realmente solo necesito una clase. Lo hago con anticipación, algún día podría necesitarlo y generalmente nunca lo hago. Incluso cuando lo hago, generalmente encuentro que tengo que hacer mucho para arreglarlo. Eso es porque la primera clase que estoy creando no es el Perro, no tengo tanta suerte, es el Ornitorrinco. Ahora toda mi jerarquía de clases se basa en un caso extraño y tengo mucho código desperdiciado.
También puede encontrar en algún momento que no todos los gatos son IPettable (como ese sin pelo). Ahora puede mover esa interfaz a todas las clases derivadas que se ajusten. Encontrará un cambio mucho menos importante que, de repente, los gatos ya no se derivan de PettableBase.
fuente
Aquí está la definición básica y simple de interfaz y clase base:
salud
fuente
Recomiendo usar composición en lugar de 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 comportarse de cierta manera. Si desea cambiar el comportamiento, cree un nuevo método de fábrica (o fábrica abstracta) que cree diferentes tipos de subobjetos.
En algunos casos, puede encontrar que sus objetos primarios no necesitan interfaces en absoluto, si todo el comportamiento mutable se define en objetos auxiliares.
Entonces, en lugar de IPet o PetBase, puede 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 comportamientos muy básicos en todo el sistema.
Recomiendo este patrón incluso en lenguajes de herencia múltiple.
fuente
Se explica 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 una o más interfaces.
Clases abstractas que uso como base para otra cosa.
El siguiente es un extracto del artículo mencionado en JavaWorld.com, autor Tony Sintes, 20/04/01
Algunos dicen que debería 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.
fuente
También tenga en cuenta que no debe dejarse llevar por el OO ( vea el blog ) y siempre modele objetos según el comportamiento requerido, si estaba diseñando una aplicación donde el único comportamiento que requería era un nombre genérico y una especie para un animal, entonces solo necesitaría Una clase de animal con una propiedad para el nombre, en lugar de millones de clases para cada animal posible en el mundo.
fuente
Tengo una regla general aproximada
Funcionalidad: es probable que sea diferente en todas las partes: interfaz.
Los datos y la funcionalidad, las partes serán en su mayoría iguales, partes diferentes: clase abstracta.
Los datos y la funcionalidad realmente funcionan, si se extienden solo con ligeros cambios: clase ordinaria (concreta)
Datos y funcionalidad, sin cambios planeados: clase ordinaria (concreta) con modificador final.
Datos, y tal vez funcionalidad: solo lectura: miembros de enumeración.
Esto es muy aproximado y está 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.
fuente
Las interfaces deben ser pequeñas. Realmente pequeño. Si realmente está desglosando sus objetos, entonces sus interfaces probablemente solo contendrán algunos métodos y propiedades muy específicos.
Las clases abstractas son atajos. ¿Hay cosas que comparten todos los derivados de PetBase que pueda codificar una vez y terminar? Si es así, entonces es hora de una clase abstracta.
Las clases abstractas también son limitantes. Si bien le brindan un excelente acceso directo para producir objetos secundarios, cualquier objeto dado solo puede implementar una clase abstracta. Muchas veces, considero 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) e IDigestion (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 extiende PetBase y agregar IMammal. FishBase podría tener PetBase y agregar IFish. IFish tendría ISwim e 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 funcionan juntas.
fuente
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 pueden ser mal utilizadas, o algunas veces es difícil entender por qué elegiría usar una función sobre otra. Con los años, una característica con la que he visto luchar a muchos desarrolladores es cuándo elegir usar una interfaz o elegir usar una clase abstracta. Ambos tienen sus ventajas y desventajas y el momento y lugar correctos para usar cada uno. ¿Pero cómo decidimos?
Ambos prevén la reutilización de funcionalidades comunes entre 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 "base" 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 (No repetir) del desarrollo de software. Las clases abstractas son excelentes para usar cuando tienes una relación "es un".
Por ejemplo: un golden retriever "es un" tipo de perro. Entonces 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 de la corteza del perro "por defecto". Por lo tanto, podría tener sentido implementar algo de la siguiente manera:
Como puede ver, esta sería una excelente 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 puede confiar en el Ladrido predeterminado en lugar de una implementación de caso especial. Las clases como GoldenRetriever, Boxer, Lab podrían heredar la corteza "predeterminada" (clase de bajo) sin cargo solo porque implementan la clase abstracta Dog.
Pero estoy seguro de que ya lo sabías.
Estás aquí porque quieres entender por qué quieres 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 generalmente se debe a que los tipos que implementan la interfaz no están relacionados en una relación "es un". En realidad, no tienen que estar relacionados, excepto por el hecho de que cada tipo "es capaz" o tiene "la habilidad" de hacer algo o tener algo.
¿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 habilidad" de nadar (dado que el humano pasó sus lecciones de natación en 1er grado :)). Además, dado que un pato no es humano o viceversa, esta no es una relación "es una", sino una relación "es capaz" y podemos usar una interfaz para ilustrar que:
El uso de interfaces como el código anterior le permitirá pasar un objeto a un método que "pueda" hacer algo. Al código no le importa cómo lo hace ... 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 en función de su tipo.
Una vez más, esto ayuda a que su código permanezca SECO para que no tenga que escribir varios métodos que están llamando al objeto para preformar la misma función central (ShowHowHumanSwims (humano), ShowHowDuckSwims (pato), etc.)
El uso de una interfaz aquí permite que los métodos de llamada no tengan que preocuparse sobre 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 / o las clases o tipos con los que está trabajando comparten una relación "es una" (por ejemplo, poodle "es un "Tipo de perro).
Por otro lado, use una interfaz cuando no tenga una relación "es un" pero tenga tipos que compartan "la capacidad" de hacer o tener algo (por ejemplo, Duck "no es" un humano. Sin embargo, el pato y el humano comparten "La capacidad" de nadar).
Otra diferencia a tener en cuenta entre las clases abstractas y las interfaces es que una clase puede implementar una o varias interfaces, pero una clase solo puede heredar de UNA clase abstracta (o de cualquier clase). Sí, puede anidar clases y tener una jerarquía de herencia (que muchos programas hacen 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 puede hacerlo, generalmente solo por la falta de interfaces en estos idiomas).
Recuerde también cuando utilice interfaces para cumplir con el Principio de segregación de interfaz (ISP). El ISP afirma que ningún cliente debería verse obligado a depender de métodos que no utiliza. Por esta razón, las interfaces deben centrarse en tareas específicas y, por lo general, son muy pequeñas (por ejemplo, IDisposable, IComparable).
Otro consejo es que si está desarrollando funcionalidades pequeñas y concisas, 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 pensar en mejores ejemplos o desea señalar algo, ¡hágalo en los comentarios a continuación!
fuente
El caso de las clases base sobre las interfaces se explicó bien en las Pautas de codificación de .NET principales:
fuente
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 también una clase base diferente. Además, si encuentra que su interfaz se está agrandando, debe comenzar a dividirla en algunas piezas lógicas que definan la 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 simplemente los hereda a todos para agruparlos).
fuente
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 muy utilizado en esta pregunta en particular, hay muchas cosas que son petables: novias, automóviles, mantas borrosas ..., por lo que podría haber tenido una clase 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 comprender el diseño orientado a objetos lo suficientemente mejor que yo al principio, normalmente lo hará automáticamente sin siquiera pensarlo. Por lo tanto, la verdad de la afirmación "código para una interfaz, no una clase abstracta" se vuelve tan obvia que tiene dificultades para creer que alguien se molestaría en decirlo, y comenzar a tratar de leer otros significados en él.
Otra cosa que agregaría es que si una clase es puramente abstracta, sin miembros o 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.
fuente
Prefiere interfaces sobre clases abstractas
Justificación, los puntos principales a considerar [dos ya mencionados aquí] son:
Por supuesto, el tema se ha discutido extensamente en otra parte [2,3].
[1] Agrega 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 ...
fuente
Los comentarios anteriores sobre el uso de clases abstractas para una implementación común definitivamente están en la marca. Un beneficio que no he visto mencionado aún es que el uso de interfaces hace que sea mucho más fácil implementar objetos simulados con el propósito de realizar pruebas unitarias. Definir IPet y PetBase como lo describió Jason Cohen le permite burlarse de 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).
fuente
No use una clase base a menos que sepa lo que significa y que se aplica en este caso. Si corresponde, úselo; de lo contrario, use 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 sustituibilidad de Liskov
En resumen, si A "es un" B, entonces A no requiere más que B y ofrece no menos de B, por cada método que expone.
fuente
Otra opción a tener en cuenta es usar la relación "tiene-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 el uso de la herencia "is-a".
Puede que no tenga tanto sentido lógico decir que tanto el perro como el gato "tienen" una mascota, pero evita los errores comunes de herencia múltiple:
Sí, este ejemplo muestra que hay mucha duplicación de código y falta de elegancia para hacer las cosas de esta manera. Pero también se debe apreciar que esto ayuda a mantener al Perro y al Gato desconectados de la clase Mascota (en el sentido de 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 Mamífero.
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 usarlo se compromete a acoplar sus clases de perros y gatos a su clase de mascotas, lo cual es un costo elevado.
Entre la herencia, la composición y las interfaces, no hay una sola forma que siempre sea correcta, y ayuda a considerar cómo se pueden utilizar las tres opciones en armonía. De las tres, la herencia suele ser la opción que debe usarse con menos frecuencia.
fuente
Conceptualmente, una interfaz se utiliza para definir formal y semiformalmente 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 interfaz de programación de aplicaciones ), no pueden contener ninguna implementación y no es posible usar o ejecutar una interfaz. Solo hacen explícito el contrato de cómo debe interactuar con un objeto.
Las clases proporcionan una implementación y pueden declarar que implementan cero, una o más interfaces. Si se pretende que una clase se herede, 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). ABC combina interfaz e implementación juntas. Resumen fuera de la programación de computadoras significa "resumen", es decir, "abstract == 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 en qué lenguaje se está desarrollando. Las interfaces a menudo se asocian solo con lenguajes estáticamente tipados como Java o C #, pero Los lenguajes de tipo dinámico 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. En un lenguaje dinámico, es posible que dos objetos que son instancias de la misma clase puedan declarar que proporcionan interfaces completamente diferentes . En Python esto solo es posible para atributos de objeto, mientras que los métodos son estado compartido 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 introspectando un objeto y preguntándole qué métodos proporciona ( mire antes de saltar ) o preferiblemente simplemente intentando usar la interfaz deseada en un objeto y capturando excepciones si el objeto no proporciona esa interfaz (es más fácil 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 de antemano para anticipar todos los usos posibles de su código.
fuente
Depende de tus requerimientos. Si IPet es lo suficientemente simple, preferiría implementar eso. De lo contrario, si PetBase implementa una tonelada de funcionalidad que no desea duplicar, entonces hágalo.
La desventaja de implementar una clase base es el requisito de
override
(onew
) 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: supongamos que está haciendo un control de usuario, por lo que hereda
UserControl
. Pero, ahora estás bloqueado de heredar tambiénPetBase
. Esto te obliga a reorganizar, como hacer unPetBase
miembro de la clase, en su lugar.fuente
Por lo general, tampoco lo implemento 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 heredadas, lo muevo hacia arriba y hago una clase base abstracta. No veo la necesidad de ambos, ya que esencialmente sirven para el mismo propósito, y tener ambos es un mal olor de código (en mi humilde opinión) de que la solución ha sido sobredimensionada.
fuente
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 más arriba en la pila a la subclase; y iii) solo se puede heredar una clase abstracta en una clase, mientras que se pueden implementar múltiples interfaces en una clase.
fuente
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 implementan por defecto una interfaz implícita. También podemos definir una interfaz como un rol, cuando alguna clase necesita desempeñar ese rol, debe implementarlo dándole diferentes formas de implementación dependiendo de la clase que lo implemente. Por lo tanto, cuando habla de interfaz, habla de polimorfismo y cuando habla de clase base, habla de herencia. Dos conceptos de ¡Uy!
fuente
Descubrí que un patrón de Interfaz> Resumen> Concreto funciona en el siguiente caso de uso:
La clase abstracta define los atributos compartidos predeterminados de las clases concretas, pero impone la interfaz. Por ejemplo:
Ahora, dado que todos los mamíferos tienen cabello y pezones (AFAIK, no soy zoólogo), podemos incluir esto en la clase base abstracta
Y luego las clases concretas simplemente definen que caminan erguidos.
Este diseño es agradable cuando hay muchas clases concretas, y no desea mantener la repetitiva solo para programar en una interfaz. Si se agregaran nuevos métodos a la interfaz, se romperían todas las clases resultantes, por lo que aún obtendrá 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 está siendo empleado.
fuente
Un heredero de una clase base debe tener una relación "es una". La interfaz representa una relación "implementa una". Así que solo use una clase base cuando sus herederos mantendrán una relación.
fuente
Use interfaces para hacer cumplir un contrato a través de familias de clases no relacionadas. Por ejemplo, puede tener métodos de acceso comunes para las clases que representan colecciones, pero 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 en una galería. Además, puede implementar múltiples interfaces, lo que le permite combinar (y significar) las capacidades de la clase.
Use la herencia cuando las clases tengan una relación común y, por lo tanto, tengan una firma estructural y de comportamiento similar, es decir, el automóvil, la motocicleta, el camión y el SUV son todos los tipos de vehículos de carretera que pueden contener varias ruedas, una velocidad máxima
fuente