Sta usando le interfacce per i tipi di dati come anti-pattern?

9

Supponiamo di avere varie entità nel mio modello (utilizzando EF), ad esempio Utente, Prodotto, Fattura e Ordine.

Sto scrivendo un controllo utente in grado di stampare i riepiloghi degli oggetti entità nella mia applicazione in cui le entità appartengono a un insieme pre-deciso, in questo caso dico che i riepiloghi di Utente e Prodotto possono essere riassunti.

I riepiloghi avranno tutti solo un ID e una descrizione, quindi creo un'interfaccia semplice per questo:

 public interface ISummarizableEntity {     
       public string ID { get; }    
       public string Description { get; } 
 }

Quindi per le entità in questione, creo una classe parziale che implementa questa interfaccia:

public partial class User : ISummarizableEntity
{
    public string ID
    {
        get{ return UserID.ToString(); }
    }

    public string Description 
    {
        get{ return String.Format("{0} {1} is from {2} and is {3} years old", FirstName, LastName, Country, Age); }
    }
}

public partial class Product: ISummarizableEntity
{
    public string ID
    {
        get{ return ProductID.ToString(); }
    }

    public string Description 
    {
        get{ return String.Format("{0} weighs {1}{2} and belongs in the {3} department", ProductName, WeightValue, WeightUnit, Department); }
    }
}

In questo modo il mio controllo utente / vista parziale può semplicemente collegarsi a qualsiasi raccolta di ISummarizableEntity e non è necessario che sia interessato alla fonte. Mi è stato detto che le interfacce non dovrebbero essere usate come tipi di dati, ma non ho avuto più informazioni di quelle. Per quanto posso vedere, sebbene le interfacce descrivano normalmente il comportamento, solo l'uso delle proprietà non è un anti-pattern di per sé in quanto le proprietà sono comunque solo zucchero sintattico per getter / setter.

Potrei creare un datatype e una mappa concreti dalle entità a quello ma non posso vedere il beneficio. Potrei rendere gli oggetti entità ereditari da una classe astratta e quindi definire le proprietà, ma poi sto bloccando le entità per non utilizzarle ulteriormente poiché non possiamo avere ereditarietà multipla. Sono anche disposto ad avere qualsiasi oggetto ISummarizableEntity se volessi (ovviamente rinominerei l'interfaccia)

La soluzione che sto usando nella mia mente è mantenibile, estensibile, verificabile e abbastanza robusta. Riesci a vedere l'anti-pattern qui?

    
posta Si-N 04.03.2015 - 13:45
fonte

3 risposte

16

Le interfacce non descrivono il comportamento. Al contrario, a volte.

Le interfacce descrivono i contratti, ad esempio "se devo offrire questo oggetto a qualsiasi metodo che accetta ISummarizableEntity, questo oggetto deve essere un'entità che è in grado di riepilogare se stesso" - nel tuo caso, che è definito come capace per restituire un ID stringa e una descrizione stringa.

È un perfetto uso delle interfacce. Nessun anti-pattern qui.

    
risposta data 04.03.2015 - 14:22
fonte
5

Hai scelto il percorso migliore per questo progetto perché stai definendo un tipo specifico di comportamento che sarà richiesto a più tipi di oggetti. L'ereditarietà in questo caso implicherebbe una relazione comune tra le classi che in realtà non esiste. In questo caso, la composabilità è preferita rispetto all'ereditarietà.

    
risposta data 04.03.2015 - 14:23
fonte
1

Le interfacce che portano solo le proprietà dovrebbero essere evitate poiché:

  • offusca l'intento: hai solo bisogno di un contenitore di dati
  • incoraggia l'ereditarietà: probabilità che qualcuno possa mescolare le preoccupazioni in futuro
  • impedisce la serializzazione

Qui stai mescolando due dubbi:

  • riepilogo come dati
  • riepilogo come contratto

Un riepilogo è composto da due stringhe: un id e una descrizione. Questo è semplice:

public class Summary {
    private readonly string id;
    private readonly string description;
    public Summary(string id, string description) {
        this.id = id;
        this.description = description;
    }
    public string Id { get { return id; } }
    public string Description { get { return description; } }
}

Ora che hai definito cosa è un sommario, vuoi definire un contratto:

public interface ISummarizableEntity {
    public Summary GenerateSummary();
}

Si noti che l'uso dell'intelligence nei getter è un anti-modello e dovrebbe essere evitato: dovrebbe invece essere collocato nelle funzioni. Ecco come appaiono le implementazioni:

public partial class User : ISummarizableEntity {
    public Summary GenerateSummary() {
        var id = UserID.ToString();
        var description = String.Format("{0} {1} is from {2} and is {3} years old", FirstName, LastName, Country, Age);
        return new Summary(id,description);
    }
}

public partial class Product : ISummarizableEntity {
    public Summary GenerateSummary() {
        var id = ProductID.ToString();
        var description = String.Format("{0} weighs {1}{2} and belongs in the {3} department", ProductName, WeightValue, WeightUnit, Department);
        return new Summary(id,description);
    }
}
    
risposta data 31.01.2016 - 16:25
fonte

Leggi altre domande sui tag