Для меня это имеет смысл, так как дает конкретное имя классу, а не полагается на общий NamedEntity. С другой стороны, существует ряд таких классов, которые просто не имеют дополнительных свойств.
Есть ли недостатки этого подхода?
Подход неплох, но есть лучшие решения. Короче говоря, интерфейс был бы намного лучшим решением для этого. Основная причина, по которой интерфейсы и наследование здесь разные, заключается в том, что вы можете наследовать только от одного класса, но вы можете реализовать много интерфейсов .
Например, предположим, что у вас есть именованные сущности и проверенные сущности. У вас есть несколько объектов:
Oneне является проверяемой организацией или именованной организацией. Это просто:
public class One
{ }
Twoявляется именованным объектом, но не объектом аудита. По сути, это то, что у вас есть сейчас:
public class NamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Two : NamedEntity
{ }
Threeявляется как именованной, так и проверенной записью. Здесь вы столкнетесь с проблемой. Вы можете создать AuditedEntityбазовый класс, но вы не можете сделать Threeунаследуют как AuditedEntity и NamedEntity :
public class AuditedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : NamedEntity, AuditedEntity // <-- Compiler error!
{ }
Тем не менее, вы можете подумать об обходном пути, получив AuditedEntityнаследование от NamedEntity. Это умный взлом, чтобы гарантировать, что каждый класс должен наследовать (напрямую) только от одного другого класса.
public class AuditedEntity : NamedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : AuditedEntity
{ }
Это все еще работает. Но то, что вы сделали здесь, гласит, что каждая проверяемая сущность по своей сути также является именованной сущностью . Что подводит меня к моему последнему примеру. Fourявляется объектом аудита, но не именованным объектом. Но вы не можете позволить Fourнаследовать от, AuditedEntityкак вы бы тогда сделали это NamedEntityиз-за наследования между AuditedEntity andNamedEntity`.
Используя наследование, невозможно заставить и то, Threeи другое Fourработать, если только вы не начнете дублировать классы (что открывает целый новый набор проблем).
Используя интерфейсы, этого легко достичь:
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; }
}
Единственным небольшим недостатком здесь является то, что вам все еще нужно реализовать интерфейс. Но вы получаете все преимущества наличия общего многократно используемого типа, без каких-либо недостатков, возникающих при необходимости вариации нескольких общих типов для данного объекта.
Но ваш полиморфизм остается нетронутым:
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);
С другой стороны, существует ряд таких классов, которые просто не имеют дополнительных свойств.
Это вариант шаблона интерфейса маркера , где вы реализуете пустой интерфейс исключительно для того, чтобы иметь возможность использовать тип интерфейса для проверки, помечен ли данный класс этим интерфейсом.
Вы используете унаследованные классы вместо реализованных интерфейсов, но цель та же, поэтому я буду называть это «помеченным классом».
На первый взгляд, нет ничего плохого в маркерных интерфейсах / классах. Они синтаксически и технически действительны, и при их использовании нет никаких недостатков при условии, что маркер универсально верен (во время компиляции) и не является условным .
Именно так вы должны различать разные исключения, даже если эти исключения на самом деле не имеют каких-либо дополнительных свойств / методов по сравнению с базовым методом.
Так что в этом нет ничего плохого, но я бы посоветовал использовать это осторожно, убедившись, что вы не просто пытаетесь скрыть существующую архитектурную ошибку с плохо разработанным полиморфизмом.
OrderDateInfos от тех, которые относятся к другимNamedEntitys