Per me, questo ha senso in quanto dà un nome concreto alla classe, invece di fare affidamento sul generico NamedEntity. D'altra parte, esiste un numero di tali classi che semplicemente non hanno proprietà aggiuntive.
Ci sono aspetti negativi di questo approccio?
L'approccio non è male, ma ci sono soluzioni migliori disponibili. In breve, un'interfaccia sarebbe una soluzione molto migliore per questo. Il motivo principale per cui le interfacce e l'ereditarietà sono diverse qui è perché puoi ereditare solo da una classe, ma puoi implementare molte interfacce .
Ad esempio, considera di avere entità nominate e entità controllate. Hai diverse entità:
Onenon è un'entità controllata né un'entità denominata. È semplice:
public class One
{ }
Twoè un'entità denominata ma non un'entità controllata. Questo è essenzialmente quello che hai ora:
public class NamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Two : NamedEntity
{ }
Threeè sia una voce denominata che controllata. Qui è dove ti imbatti in un problema. Puoi creare una AuditedEntityclasse base, ma non puoi Threeereditare entrambi AuditedEntity e NamedEntity :
public class AuditedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : NamedEntity, AuditedEntity // <-- Compiler error!
{ }
Tuttavia, potresti pensare a una soluzione alternativa AuditedEntityereditando da NamedEntity. Questo è un trucco intelligente per garantire che ogni classe abbia solo bisogno di ereditare (direttamente) da un'altra classe.
public class AuditedEntity : NamedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : AuditedEntity
{ }
Funziona ancora. Ma ciò che hai fatto qui è dichiarato che ogni entità controllata è intrinsecamente anche un'entità denominata . Il che mi porta al mio ultimo esempio. Fourè un'entità controllata ma non un'entità denominata. Ma non puoi lasciarti Fourereditare da AuditedEntitycome lo faresti anche a NamedEntitycausa dell'eredità tra AuditedEntity andNamedEntity`.
Usando l'ereditarietà, non c'è modo di fare entrambe le cose Threee Fourfunzionare a meno che non si inizi a duplicare le classi (il che apre una nuova serie di problemi).
Utilizzando le interfacce, questo può essere facilmente raggiunto:
public interface INamedEntity
{
int Id { get; set; }
string Name { get; set; }
}
public interface IAuditedEntity
{
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
public class One
{ }
public class Two : INamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Three : INamedEntity, IAuditedEntity
{
public int Id { get; set; }
public string Name { get; set; }
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
public class Four : IAuditedEntity
{
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
L'unico inconveniente minore qui è che devi ancora implementare l'interfaccia. Ma ottieni tutti i vantaggi dall'avere un tipo riutilizzabile comune, senza nessuno degli svantaggi che emergono quando hai bisogno di variazioni su più tipi comuni per una data entità.
Ma il tuo polimorfismo rimane intatto:
var one = new One();
var two = new Two();
var three = new Three();
var four = new Four();
public void HandleNamedEntity(INamedEntity namedEntity) {}
public void HandleAuditedEntity(IAuditedEntity auditedEntity) {}
HandleNamedEntity(one); //Error - not a named entity
HandleNamedEntity(two);
HandleNamedEntity(three);
HandleNamedEntity(four); //Error - not a named entity
HandleAuditedEntity(one); //Error - not an audited entity
HandleAuditedEntity(two); //Error - not an audited entity
HandleAuditedEntity(three);
HandleAuditedEntity(four);
D'altra parte, esiste un numero di tali classi che semplicemente non hanno proprietà aggiuntive.
Questa è una variante del modello di interfaccia marcatore , in cui si implementa un'interfaccia vuota puramente per poter utilizzare il tipo di interfaccia per verificare se una determinata classe è "contrassegnata" con questa interfaccia.
Stai usando classi ereditate invece di interfacce implementate, ma l'obiettivo è lo stesso, quindi mi riferirò ad esso come una "classe contrassegnata".
A prima vista, non c'è niente di sbagliato nelle interfacce / classi dei marker. Sono sintatticamente e tecnicamente validi e non ci sono inconvenienti intrinseci nell'utilizzarli, a condizione che il marker sia universalmente vero (al momento della compilazione) e non condizionale .
Questo è esattamente il modo in cui dovresti distinguere tra diverse eccezioni, anche quando tali eccezioni non hanno effettivamente proprietà / metodi aggiuntivi rispetto al metodo di base.
Quindi non c'è nulla di intrinsecamente sbagliato nel farlo, ma consiglierei di usarlo con cautela, assicurandomi che non stai solo cercando di nascondere un errore architettonico esistente con un polimorfismo mal progettato.
OrderDateInfos da quelli che sono rilevanti per altriNamedEntitys