组合优于继承——额外的属性去哪里了?

发布于 2024-11-20 00:38:55 字数 1182 浏览 6 评论 0原文

从示例 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();
    }
}

但是当我执行此操作时,特定于假期和疾病的属性何时消失(分别为 Location 和 DoctorsNoteProvided)?

Take this following code from an example HR system. The user has the ability to log an absence and can be of various types including holiday and sickness. This would be a domain model over an ORM such as 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()
    { ... }
}

This is an example - please don't question why location would be required, assume it is a specification.

The user wants to change the type - he thought the employee was off sick but then remembered it was a holiday. Again, you may think this is a bad design but treat it like a requirement - this represents a problem that has come up many times for me.

The problem is that you cannot change the type of an object from Sickness to Absence. Generally, the advice would be to Favour Composition Over Inheritance (Gang of Four) and do this:

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();
    }
}

But when I do this, when do the properties specific to Holiday and Sickness go (Location and DoctorsNoteProvided respectively)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

苍景流年 2024-11-27 00:38:55

为什么需要改变对象的类型?

您将拥有某种缺勤集合,只需替换有问题的项目即可。

可以想象,您甚至可以保留原始请求并将其标记为已取代,而不是替换您,这对于审计跟踪目的可能很重要。

Why do you need to change the type of an object?

You will have some kind of collection of Absences, just replace the item in question.

Conceivably rather than replacing you even keep the original request and mark it as superceded, that might be important for audit trail purposes.

撩心不撩汉 2024-11-27 00:38:55

这不是组合优于继承的正确位置。这里继承是合适的。如果您需要更改缺勤类型,只需创建一个新缺勤类型并删除旧缺勤类型即可。

It's not the right place for Composition over Inheritance. Here the inheritance is appropriate. And if you need to change the type of absence just create a new one and delete old.

娇女薄笑 2024-11-27 00:38:55

嗯,在不了解更多关于您的要求的情况下,我想说正确的设计不是将缺勤对象更改为疾病对象(反之亦然),而是删除您不想要的对象并创建一个新的类型你做。您一定在某个地方维护着缺勤集合,对吧?

你是对的,班级不会改变。

Hmmm, without knowing more about your requirements, I would say the right design is not to change an Absence object to a Sickness object (or vice versa) but to just delete the one you don't want and create a new one of the type you do. Somewhere you must be maintaining a collection of absences, right?

You are correct that classes don't change.

话少心凉 2024-11-27 00:38:55

我将通过为 AbsenceType 或 AbsenseReason 建立一个类型层次结构来对此进行建模:

abstract class AbsenseReason {
}

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

我喜欢这个模型,因为现在 AbsenseReason 是一个值对象,并且独立于员工 Absence,而员工 Absence 是一个实体对象。正如您所说,这解决了更改缺勤原因的问题。一般来说,我更喜欢这样做而不是删除记录,因为可能还有很多关联需要考虑。

需要考虑的事项:

  • NHibernate 不支持组件上的继承映射,因此您必须提供 IUserType 的自定义实现。
  • 考虑将不同缺勤原因子类型的所有数据与员工缺勤实体的记录一起存储。可能是 XML,这样您就可以拥有集合等。

I would model this by having a type hierarchy for an AbsenceType, or AbsenseReason:

abstract class AbsenseReason {
}

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

I like this model because now AbsenseReason is a value object and is independent of an employee Absence, which is an entity object. This, as you stated, solves the issue with changing the absence reason. Generally speaking, I would favor this over deleting a record, because there may be many associations to consider as well.

Things to consider:

  • NHibernate does not support inheritance mappings on components so you will have to provide a custom implementation of IUserType.
  • Consider storing all the data for the different absence reason sub types together with the record for the employee absence entity. Possibly as XML so that you can have collections, etc.
别闹i 2024-11-27 00:38:55

因此,请尝试将所有类型特定功能移至 AbsenceType 派生类。如果他们需要父类 Absence 中的某些内容,您可以将其引用传递给他们。尽管我会尽力避免这种情况。

如果您通过基类接口操作 Absence 对象,则不会发生任何变化,您可以保留旧代码。现在,如果您操纵特定的导数,那么您将必须从特定的 Absence 中获取 AbsenceType 对象,并对它们执行所有相同的操作 - 仍然没有太多改变。如果您有 holiday.DoSomething(),现在您就有 holiday.Type.DoSomething()

So try to move all type specific functionality to AbsenceType derivatives. If they require something from parent class Absence, you could pass them its reference. Though I would try to avoid that.

If you manipulated Absence object via base class interface, nothing changes, you can keep your old code. Now, if you manipulated specific derivatives, then you will have to grab AbsenceType object from specific Absence and do all the same things on them - still not much to change. If you had holiday.DoSomething(), now you have holiday.Type.DoSomething().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文