¿Es una mala práctica usar una interfaz solo para la categorización?

12

Por ejemplo:

Digamos 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 que C no lo es, pero es un IAnimal .

La parte importante es que IDog no proporciona ninguna funcionalidad adicional. Solo se utiliza para permitir que A y B , pero no C , se pasen como argumento a ciertos métodos.

¿Es esta mala práctica?

    
pregunta Kendall Frey 14.06.2012 - 00:40

4 respuestas

13

La 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 intenso en Proyecto Orchard

Aquí está muestra del proyecto Orchard.

public interface ISingletonDependency : IDependency {}

/// <summary>
/// Base interface for services that may *only* be instantiated in a unit of work.
/// This interface is used to guarantee they are not accidentally referenced by a singleton dependency.
/// </summary>
public interface IUnitOfWorkDependency : IDependency {}

/// <summary>
/// Base interface for services that are instantiated per usage.
/// </summary>
public interface ITransientDependency : IDependency {}

Consulte Interfaz de marcador .

    
respondido por el Freshblood 14.06.2012 - 00:54
4

Sí, es una mala práctica en casi todos los idiomas.

Los tipos no están ahí 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 sub-escribir es si un comportamiento polimórfico cambia (y no siempre entonces).

Como no hay escritura, lo que puede hacer un IDog está implícito. Un programador piensa una cosa, otra piensa otra y las cosas se desintegran. O las cosas que representa aumentan a medida que necesita un control más preciso.

[editar: aclaración]

Lo que quiero decir es que estás haciendo alguna 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 significa diferirá entre los programadores. [/ editar]

Ciertos lenguajes proporcionan mecanismos de rasgos donde esto no es tan malo, pero estos 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.

    
respondido por el Telastyn 14.06.2012 - 00:55
2

Sólo conozco bien C #, así que no puedo decir esto para la programación OO en general, pero como ejemplo de C #, si necesita categorizar las clases, las opciones obvias son interfaces, propiedades de enumeración o atributos personalizados. Normalmente elegiría la interfaz sin importar lo que piensen los demás.

if(animal is IDog)
{
}
if(animal.Type == AnimalType.Dog)
{
}
if(animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
{
}


var dogs1 = animals.OfType<IDog>();
var dogs2 = animals.Where(a => a.Type == AnimalType.Dog);
var dogs3 = animals.Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any());


var dogsFromDb1 = session.Query<IDog>();
var dogsFromDb2 = session.Query<Animal>().Where(a => a.Type == AnimalType.Dog);
var dogsFromDb3 = session.Query<Animal>().Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute),false).Any());


public void DoSomething(IDog dog)
{
}
public void DoSomething(Animal animal)
{
    if(animal.Type != AnimalType.Dog)
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
public void DoSomething(Animal animal)
{
    if(!animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
    
respondido por el Mika Kolari 14.06.2012 - 09:42
-1

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 útil las cosas que algunas implementaciones de la primera podría no. De hecho, es un buen patrón que IMHO debería haberse usado más en cosas como el .net Framework, especialmente porque un implementador cuya clase naturalmente satisfaría las restricciones que implica la interfaz más derivada puede indicar eso simplemente implementando la interfaz más derivada , sin tener que cambiar ninguno de los códigos o declaraciones de implementación del miembro.

Por ejemplo, supongamos que tengo las interfaces:

interface IMaybeMutableFoo {
  Bar Thing {get;set;} // And other properties as well
  bool IsMutable;
  IImmutableFoo AsImmutable();
}
interface IImmutableFoo : IMaybeMutableFoo {};

Se esperaría que una implementación de IImmutableFoo.AsImmutable() se devuelva sola; se esperaría que una clase mutable que implementa IMaybeMutableFoo.AsImmutable() devuelva un nuevo objeto profundamente inmutable que contenga una instantánea de los datos. Una rutina que desee almacenar una instantánea de los datos almacenados en un IMaybeMutableFoo podría ofrecer sobrecargas:

public void StoreData(IImmutableFoo ThingToStore)
{
  // Storing the reference will suffice, since ThingToStore is known to be immutable
}
public void StoreData(IMaybeMutableFoo ThingToStore)
{
  StoreData(ThingToStore.AsImmutable()); // Call the more-specific overload
}

En los casos en que el compilador no sabe que lo que se va a almacenar es un IImmutableFoo , llamará a AsImmutable() . La función debería regresar rápidamente si el elemento ya es inmutable, pero una llamada a la función a través de una interfaz aún toma tiempo. Si el compilador sabe que el elemento ya es un IImmutableFoo , puede omitir la llamada de función innecesaria.

    
respondido por el supercat 09.08.2012 - 00:45

Lea otras preguntas en las etiquetas