È una cattiva pratica usare solo un'interfaccia per la categorizzazione?

12

Ad esempio:

Dire che ho classi A , B , C . Ho due interfacce, chiamiamole IAnimal e IDog . IDog eredita da IAnimal . A e B sono IDog s, mentre C non lo è, ma è un IAnimal .

La parte importante è che IDog non fornisce funzionalità aggiuntive. Viene utilizzato solo per consentire A e B , ma non C , da passare come argomento a determinati metodi.

Questa cattiva pratica è?

    
posta Kendall Frey 14.06.2012 - 00:40
fonte

4 risposte

13

Interfaccia che non ha alcun membro o ha esattamente gli stessi membri con un'altra interfaccia ereditata dalla stessa interfaccia chiamata Marker Interface e la stai usando come marker.

Non è una cattiva pratica, ma l'interfaccia può essere sostituita da attributi (annotazioni) se la lingua che stai utilizzando la supporta.

Posso dire con certezza che non è una cattiva pratica perché ho visto un uso intenso del pattern "Marker Interface" in Progetto Orchard

Ecco esempio dal progetto 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 {}

Fai riferimento a Interfaccia Marker .

    
risposta data 14.06.2012 - 00:54
fonte
4

Sì, è una cattiva pratica in quasi tutte le lingue.

I tipi non sono lì per codificare i dati; sono un aiuto per dimostrare che i tuoi dati vanno dove deve andare e si comporta come deve comportarsi. L'unica ragione per sottomettere è se un comportamento polimorfico cambia (e non sempre allora).

Dato che non si digita, ciò che un IDog può fare è implicito. Un programmatore pensa una cosa, un'altra pensa un'altra e le cose si rompono. Oppure le cose che rappresenta aumentano in quanto hai bisogno di un controllo più preciso.

[modifica: chiarimento]

Ciò che intendo è che stai facendo una qualche logica basata sull'interfaccia. Perché alcuni metodi funzionano solo con cani e altri animali? Questa differenza è un contratto implicito poiché non esiste un membro effettivo che fornisce la differenza; nessun dato effettivo nel sistema. L'unica differenza è l'interfaccia (da cui il commento "no typing"), e ciò che significa sarà diverso tra i programmatori. [/ Modifica]

Alcune lingue forniscono meccanismi di tratto dove ciò non è male, ma tendono a concentrarsi sulle capacità, non sul perfezionamento. Ho poca esperienza con loro quindi non posso parlare con le migliori pratiche lì. In C ++ / Java / C # però ... non va bene.

    
risposta data 14.06.2012 - 00:55
fonte
2

So solo C # bene, quindi non posso dire questo per la programmazione OO in generale, ma come esempio C #, se hai bisogno di categorizzare le classi le opzioni ovvie sono le interfacce, la proprietà enum o gli attributi personalizzati. Solitamente sceglierei l'interfaccia indipendentemente da ciò che pensano gli altri.

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.");
    }
}
    
risposta data 14.06.2012 - 09:42
fonte
-1

Non c'è niente di sbagliato nell'avere un'interfaccia che erediti un'altra interfaccia senza aggiungere nuovi membri, se ci si aspetta che tutte le implementazioni di quest'ultima interfaccia siano in grado di utilmente fare cose che alcune implementazioni del precedente non può In effetti, è un buon modello che IMHO avrebbe dovuto essere usato più in cose come il .net Framework, specialmente dal momento che un implementatore la cui classe avrebbe naturalmente soddisfatto i vincoli implicati dall'interfaccia più derivata può indicare che semplicemente implementando l'interfaccia più derivata , senza dover modificare alcun codice o dichiarazione di implementazione dei membri.

Ad esempio, supponiamo di avere le interfacce:

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

Ci si aspetta che l'implementazione di IImmutableFoo.AsImmutable() ritorni da sola; una classe mutabile che implementa IMaybeMutableFoo.AsImmutable() dovrebbe restituire un nuovo oggetto profondamente immutabile contenente un'istantanea dei dati. Una routine che vuole memorizzare un'istantanea dei dati contenuti in IMaybeMutableFoo potrebbe offrire sovraccarichi:

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
}

Nei casi in cui il compilatore non sappia che la cosa da memorizzare è un IImmutableFoo , chiamerà AsImmutable() . La funzione dovrebbe tornare rapidamente se l'elemento è già immutabile, ma una chiamata di funzione attraverso un'interfaccia richiede ancora tempo. Se il compilatore sa che l'elemento è già un IImmutableFoo , può saltare l'inutile chiamata di funzione.

    
risposta data 09.08.2012 - 00:45
fonte

Leggi altre domande sui tag