Для мене це має сенс, оскільки воно дає конкретну назву класу, а не покладатися на загальну 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