Для меня это имеет смысл, так как дает конкретное имя классу, а не полагается на общий 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 and
NamedEntity`.
Используя наследование, невозможно заставить и то, 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);
С другой стороны, существует ряд таких классов, которые просто не имеют дополнительных свойств.
Это вариант шаблона интерфейса маркера , где вы реализуете пустой интерфейс исключительно для того, чтобы иметь возможность использовать тип интерфейса для проверки, помечен ли данный класс этим интерфейсом.
Вы используете унаследованные классы вместо реализованных интерфейсов, но цель та же, поэтому я буду называть это «помеченным классом».
На первый взгляд, нет ничего плохого в маркерных интерфейсах / классах. Они синтаксически и технически действительны, и при их использовании нет никаких недостатков при условии, что маркер универсально верен (во время компиляции) и не является условным .
Именно так вы должны различать разные исключения, даже если эти исключения на самом деле не имеют каких-либо дополнительных свойств / методов по сравнению с базовым методом.
Так что в этом нет ничего плохого, но я бы посоветовал использовать это осторожно, убедившись, что вы не просто пытаетесь скрыть существующую архитектурную ошибку с плохо разработанным полиморфизмом.
OrderDateInfo
s от тех, которые относятся к другимNamedEntity
s