Dla mnie ma to sens, ponieważ nadaje konkretną nazwę klasie, zamiast polegać na ogólnej nazwie NamedEntity. Z drugiej strony istnieje wiele takich klas, które po prostu nie mają żadnych dodatkowych właściwości.
Czy są jakieś wady tego podejścia?
Podejście nie jest złe, ale dostępne są lepsze rozwiązania. Krótko mówiąc, interfejs byłby znacznie lepszym rozwiązaniem. Głównym powodem, dla którego interfejsy i dziedziczenie są różne, jest to, że można dziedziczyć tylko z jednej klasy, ale można zaimplementować wiele interfejsów .
Weźmy na przykład pod uwagę, że nazwano byty i poddano inspekcji. Masz kilka podmiotów:
Onenie jest podmiotem kontrolowanym ani podmiotem nazwanym. To proste:
public class One
{ }
Twojest jednostką nazwaną, ale nie kontrolowaną. Zasadniczo masz to teraz:
public class NamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Two : NamedEntity
{ }
Threejest zarówno wpisem nazwanym, jak i kontrolowanym. Tutaj napotykasz problem. Możesz utworzyć AuditedEntityklasę podstawową, ale nie możesz Threedziedziczyć zarówno, jak AuditedEntity i NamedEntity :
public class AuditedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : NamedEntity, AuditedEntity // <-- Compiler error!
{ }
Możesz jednak pomyśleć o obejściu, AuditedEntityodziedzicząc po NamedEntity. Jest to sprytny hack, aby upewnić się, że każda klasa musi dziedziczyć (bezpośrednio) od jednej innej klasy.
public class AuditedEntity : NamedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : AuditedEntity
{ }
To nadal działa. Ale to, co tutaj zrobiłeś, stwierdza, że każda kontrolowana jednostka jest z natury również jednostką nazwaną . Co prowadzi mnie do mojego ostatniego przykładu. Fourjest kontrolowaną jednostką, ale nie jednostką nazwaną. Ale nie możesz pozwolić na Fourdziedziczenie, AuditedEntitytak jak NamedEntityrobiłbyś to z powodu dziedziczenia między AuditedEntity andNamedEntity`.
Korzystając z dziedziczenia, nie ma sposobu na zrobienie obu Threei Fourdziałanie, chyba że zaczniesz duplikować klasy (co otwiera zupełnie nowy zestaw problemów).
Za pomocą interfejsów można to łatwo osiągnąć:
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; }
}
Jedyną niewielką wadą jest to, że nadal musisz wdrożyć interfejs. Ale zyskujesz wszystkie korzyści z posiadania wspólnego typu wielokrotnego użytku, bez żadnych wad, które pojawiają się, gdy potrzebujesz odmian wielu wspólnych typów dla danej jednostki.
Ale twój polimorfizm pozostaje nienaruszony:
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);
Z drugiej strony istnieje wiele takich klas, które po prostu nie mają żadnych dodatkowych właściwości.
Jest to odmiana wzorca interfejsu znacznika , w której implementuje się pusty interfejs wyłącznie po to, aby móc użyć typu interfejsu do sprawdzenia, czy dana klasa jest „oznaczona” tym interfejsem.
Używasz klas odziedziczonych zamiast zaimplementowanych interfejsów, ale cel jest taki sam, więc zamierzam nazywać to „klasą oznaczoną”.
Na pierwszy rzut oka nie ma nic złego w interfejsach / klasach znaczników. Są poprawne pod względem składniowym i technicznym i nie ma nieodłącznych wad ich używania, pod warunkiem, że marker jest ogólnie prawdziwy (w czasie kompilacji) i nie jest warunkowy .
Właśnie tak należy rozróżniać różne wyjątki, nawet jeśli wyjątki te nie mają żadnych dodatkowych właściwości / metod w porównaniu z metodą podstawową.
Nie ma w tym nic złego, ale radziłbym to robić ostrożnie, upewniając się, że nie tylko próbujesz ukryć istniejący błąd architektoniczny źle zaprojektowanym polimorfizmem.
OrderDateInfos, od tych, które są istotne dla innychNamedEntitys