When I was starting in OO programming, mostly Symfony and Doctrine ORM, I have been advised at least one time to apply inheritance mapping for a case as follows:
For instance, a HR system which deals with job candidates, employees, former employees, part time employees etc..
There would be an entity class User, and since there are other kinds of users, like above mentioned, there would be a few more child classes e.g. Employee, etc..
The difference between users is that each type has a set of specific properties. And they all share a set of properties.
So, the shared properties (e.g. ID, Name, Surname) would be in User parent class and the rest, specific ones would be in particular child class.
Looking at Doctrine docs, a neat way to map this, I guess, would be the Single Table Inheritance
I am not giving example of any behaviours for the classes, at this point, and that is on purpose. The case perhaps implies inevitable different implementations of a certain behaviour, which would justify the polymorphism.
I'd like to be sure about how to deal with specific properties first.
Would you agree that Inheritance mapping is justified in this case?
Does the presence of shared and specific properties for a group of real world objects inevitably implies polymorphism?
Best Answer
I would recommend avoiding that inheritance here especially when you are talking about letting that inheritance bleed down into your database - unless its an Object-Oriented database.
But a typical RDBMS is not object-oriented, and anyone who has to write queries against a table structure that's designed in favour of supporting inheritance for your ORM is not going to thank you for it :)
I'd recommend Composition over Inheritance in virtually every case like this, and here is why.
The key part here is this statement
That tends to imply that these set of properties should be implemented using Inheritance from some kind of common Base Class i.e. an
Employee
is-aPerson
, aCandidate
is-aPerson
and so on.e.g.
But you're better off tackling this from the perspective of the Domain itself.
Model the Domain Objects to be extremely specific
Although at face value it might seem that this is the best way, and that these properties really are common to all kinds of people, are you really sure?
Certainly all Employees have things like "Name" as do Candidates, but who needs to know that information really depends upon who is asking when we're talking about modeling a Domain with Object-Oriented Programming.
If we've inherited from some common Person class, you have that information carried around the place whether you want or need it.
Now
Name
is not that big a deal but what about things like Religion, etc. That kind of data should only be accessible to modules that really need it for several reasonsHandling future changes - example
New and as yet undiscovered business processes may require more information that isn't a fit for a base class at all yet is required by more than one
Employee
-type object. e.g. Let's say new legislation says you need the Social Security number for bothHourly
andSalaried
employees, but you do not need it forContractEmployees
(who despite what some might say, are stillPeople
:) )Better approach
In the long run, it's more flexible to model this using Composition whereby we say an HourlyEmployee has-a
Person
, a Candidate has-aPerson
and so on.So an
HourlyEmployee
,SalariedEmployee
,ContractEmployee
,PotentialEmployee
,VisitorNonEmployee
,PizzaDeliveryPerson
etc, can have (or not) aPersonalInformation
structure if needed, that is indeed common to all those that have it, but which is not part of any hierarchy.e.g. This might store personal information about a person.
If you really need to pass around Polymorphic objects, then Interfaces are just as useful here as base classes; in fact, because you can implement multiple interfaces, I would say they are more useful.
This might indicate that an entity holds some personal information:
as seen in
Note how the
ClockTimedEmployee
doesn't have any personal information; because we'll never ask it for your name. That will be done by some other part of the system e.g. the Reporting Module. And it can still be passed around as anIEmployee
to any part of the system that cares aboutEmployeeId
Another distinct advantage of this approach is that it guarantees that you can never accidentally pass a
ClockTimedEmployee
into any method that expects anIPersonalInformationHolder
; the code simply will not compile. This helps to cut out a whole class of runtime bugs that are hard to avoid with inheritance.