Composition over inheritance - where do the additional properties go?

Take this following code from an example HR system. The user has the ability to register an absence and can be of various types, including holiday and illness. This will be the domain model over ORM, for example 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 - don't ask why location is required, suppose it's a specification.

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

The problem is that you cannot change the type of an object from Sickness to Absence. Typically, the advice will be to maintain composition over the 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 are the properties characteristic of the holiday and illness (Location and DoctorsNoteProvided, respectively)?

+7
source share
5 answers

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

You will have some collection of Absences, just replace this item.

Presumably, instead of replacing, you even save the original request and mark it as collapsed, which may be important for audit purposes.

+4
source

This is the wrong place for composition over inheritance. Inheritance is appropriate here. And if you need to change the type of absence, just create a new one and delete the old one.

+2
source

Hmmm, not knowing more about your requirements, I would say that the correct design should not change the Absence object to a Sickness object (or vice versa), but simply delete the one you do not want and create a new type that you make. Somewhere you have to maintain a collection of absence, right?

You are right that classes do not change.

+1
source

I would simulate this with a type hierarchy for AbsenceType or AbsenseReason:

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

I like this model, because now AbsenseReason is the value of the object and does not depend on the absence of the employee who is the object of the object . This, as you said, solves the problem of changing the reason for the absence. Generally speaking, I would approve of this for deleting a record, because there can be many associations to consider and.

What to consider:

  • NHibernate does not support inheritance mappings on components, so you have to provide a custom implementation of IUserType.
  • Consider storing all the data for subtypes of different reasons for the absence along with the record for the employee absence object. Maybe like XML, so you can have collections, etc.
+1
source

So, try porting all the functionality of the type to AbsenceType derivatives. If they require something from the Absence parent class, you can pass them your link. Although I will try to avoid this.

If you manipulated an Absence object through the base class interface, nothing changes, you can save your old code. Now, if you are manipulating certain derivatives, you will have to grab an AbsenceType object from a specific Absence and do the same thing on them - there is still not much change. If you have holiday.DoSomething() , now you have holiday.Type.DoSomething() .

0
source

All Articles