Состав над наследованием - куда уходят дополнительные свойства? - PullRequest
7 голосов
/ 13 июля 2011

Возьмите этот следующий код из примера системы HR. Пользователь имеет возможность регистрировать отсутствие и может быть разных типов, включая отпуск и болезнь. Это будет модель домена поверх ORM, такая как NHibernate.

public class Absence
{
    public long Id {get;set;}
    public Employee Employee {get;set;}
    public DateTime StartDate {get;set;}        
    public DateTime EndDate {get;set;}

    public virtual void DoSomething()
    { ... }
}

public class Holiday : Absence
{ 
    public string Location {get;set;}

    public override void DoSomething()
    { ... }
}

public class Sickness : Absence
{
    public bool DoctorsNoteProvided {get;set;}

    public override void DoSomething()
    { ... }
}

Это пример - пожалуйста, не спрашивайте, почему требуется местоположение, предположите, что это спецификация.

Пользователь хочет изменить тип - он думал, что работник заболел, но потом вспомнил, что это был выходной. Опять же, вы можете подумать, что это плохой дизайн, но относитесь к нему как к требованию - это представляет проблему, которая неоднократно возникала у меня.

Проблема в том, что вы не можете изменить тип объекта с Болезни на Отсутствие. Как правило, советом было бы отдать предпочтение композиции над наследованием (бригада четырех) и сделать это:

public class Absence
{
    public long Id {get;set;}
    public Employee Employee {get;set;}
    public DateTime StartDate {get;set;}        
    public DateTime EndDate {get;set;}

    public AbsenceType Type {get;set;}

    public void DoSomething()
    {
        Type.DoSomething();
    }
}

Но когда я делаю это, когда переходят свойства, относящиеся к Holiday и Sickness (соответственно Location и DoctorsNoteProvided)?

Ответы [ 5 ]

4 голосов
/ 13 июля 2011

Зачем вам нужно менять тип объекта?

У вас будет какая-то коллекция отсутствий, просто замените предмет, о котором идет речь.

Возможно, вместо того, чтобы заменить вас, даже сохраните исходный запрос и пометьте его как отмененный, что может быть важно для целей аудита.

2 голосов
/ 13 июля 2011

Это не подходящее место для композиции, а не наследования.Здесь наследство уместно.А если вам нужно изменить тип отсутствия, просто создайте новый и удалите старый.

1 голос
/ 14 июля 2011

Я бы смоделировал это, имея иерархию типов для AbsenceType или AbsenseReason:

abstract class AbsenseReason {
}

class HolidayAbsenseReason : AbsenseReason {
    public string Name { get; }
}

Мне нравится эта модель, потому что теперь AbsenseReason является объектом значения и не зависит отОтсутствие сотрудника, которое является объектом .Это, как вы заявили, решает проблему с изменением причины отсутствия.Вообще говоря, я предпочел бы это, а не удалять запись, потому что может также потребоваться учитывать множество ассоциаций.

Что следует учитывать:

  • NHibernate не поддерживает сопоставления наследования для компонентовпоэтому вам нужно будет предоставить собственную реализацию IUserType.
  • Рассмотрите возможность хранения всех данных для различных подтипов причины отсутствия вместе с записью для сущности отсутствия сотрудника.Возможно, в виде XML, чтобы вы могли иметь коллекции и т. Д.
1 голос
/ 13 июля 2011

Хммм, не зная больше о ваших требованиях, я бы сказал, что правильный дизайн - это не заменить объект Absence объектом Sickness (или наоборот), а просто удалить тот, который вам не нужен, и создать новый. типа вы делаете. Где-то у вас должно быть собрание отсутствий, верно?

Вы правы, что классы не меняются.

0 голосов
/ 13 июля 2011

Поэтому попробуйте переместить все функции, специфичные для типа, к AbsenceType производным.Если им требуется что-то из родительского класса Absence, вы можете передать им его ссылку.Хотя я бы попытался этого избежать.

Если вы манипулировали объектом Absence через интерфейс базового класса, ничего не изменилось, вы можете сохранить свой старый код.Теперь, если вы манипулировали определенными производными, то вам придется захватывать AbsenceType объект из определенных Absence и делать с ними все то же самое - все еще не так много, чтобы измениться.Если у вас было holiday.DoSomething(), теперь у вас есть holiday.Type.DoSomething().

...