Por ejemplo:
Decir que tengo clases A
, B
, C
. Tengo dos interfaces, llamémoslas IAnimal
y IDog
. IDog
hereda de IAnimal
. A
y B
son IDog
s, mientras C
que no lo es, pero es un IAnimal
.
La parte importante es que IDog
no proporciona funcionalidad adicional. Solo se usa para permitir A
y B
, pero no C
, para pasar como argumento a ciertos métodos.
¿Es esta una mala práctica?
object-oriented
interfaces
Kendall Frey
fuente
fuente
IAnimal
yIDog
son terribles nombres de tautología!MyProject.Data.IAnimal
yMyProject.Data.Animal
son mejores queMyProject.Data.Interfaces.Animal
yMyProject.Data.Implementations.Animal
Interface
oImplementation
, ya sea en un prefijo repetitivo o en un espacio de nombres, es una tautología de cualquier manera y viola DRY.Dog
es todo lo que debería preocuparte.PitBull extends Dog
tampoco necesita redundancia de implementación, la palabraextends
me dice todo lo que necesito saber, lea el enlace que publiqué en mi comentario original.Respuestas:
Interfaz que no tiene ningún miembro o tiene exactamente los mismos miembros con otra interfaz que se hereda de la misma interfaz llamada Interfaz de marcador y la está utilizando como marcador.
No es una mala práctica, pero la interfaz puede reemplazarse por atributos (anotaciones) si el idioma que está utilizando lo admite.
Puedo decir con confianza que no es una mala práctica porque he visto un patrón de "interfaz de marcador" de uso intensivo en Orchard Project
Aquí hay una muestra del proyecto Orchard.
Consulte la interfaz de marcador .
fuente
C
a un método que espera unIDog
.Kennel
clase que almacena una lista deIDog
s.Sí, es una mala práctica en casi todos los idiomas.
Los tipos no están allí para codificar datos; son una ayuda para demostrar que sus datos van a donde deben ir y se comportan como deben comportarse. La única razón para subtipar es si cambia un comportamiento polimórfico (y no siempre entonces).
Como no hay que escribir, lo que un IDog puede hacer está implícito. Un programador piensa una cosa, otro piensa otra y las cosas se rompen. O las cosas que representa aumentan a medida que necesita un control más detallado.
[editar: aclaración]
Lo que quiero decir es que estás haciendo algo de lógica basada en la interfaz. ¿Por qué algunos métodos solo funcionan con perros y otros con cualquier animal? Esa diferencia es un contrato implícito ya que no hay un miembro real que proporcione la diferencia; No hay datos reales en el sistema. La única diferencia es la interfaz (de ahí el comentario de 'no escribir'), y lo que eso significa será diferente entre los programadores. [/editar]
Ciertos idiomas proporcionan mecanismos de rasgos donde esto no es tan malo, pero tienden a centrarse en las capacidades, no en el refinamiento. Tengo poca experiencia con ellos, así que no puedo hablar de las mejores prácticas allí. En C ++ / Java / C #, aunque ... no es bueno.
fuente
Kennel
clase que almacena una lista deIDog
s.Solo conozco bien C #, por lo que no puedo decir esto para la programación OO en general, pero como ejemplo de C #, si necesita clasificar las clases, las opciones obvias son interfaces, propiedades de enumeración o atributos personalizados. Por lo general, elegiría la interfaz sin importar lo que piensen los demás.
fuente
No hay nada de malo en tener una interfaz que herede otra interfaz sin agregar nuevos miembros, si se espera que todas las implementaciones de la última interfaz puedan hacer cosas útiles que algunas implementaciones de la primera no pueden hacer. De hecho, es un buen patrón que, en mi humilde opinión, debería haberse usado más en cosas como .NET Framework, especialmente porque un implementador cuya clase satisfaría naturalmente las restricciones implícitas en la interfaz más derivada puede indicar que simplemente implementando la interfaz más derivada , sin tener que cambiar ninguno de los códigos o declaraciones de implementación de miembros.
Por ejemplo, supongamos que tengo las interfaces:
Se
IImmutableFoo.AsImmutable()
esperaría que se implementara una implementación de ;IMaybeMutableFoo.AsImmutable()
se espera que una implementación de clase mutable devuelva un nuevo objeto profundamente inmutable que contiene una instantánea de los datos. Una rutina que quiere almacenar una instantánea de los datos contenidos en unIMaybeMutableFoo
podría ofrecer sobrecargas:En los casos en que el compilador no sabe que lo que se va a almacenar es un
IImmutableFoo
, llamaráAsImmutable()
. La función debería volver rápidamente si el elemento ya es inmutable, pero una llamada a la función a través de una interfaz todavía lleva tiempo. Si el compilador sabe que el elemento ya es unIImmutableFoo
, puede omitir la llamada de función innecesaria.fuente