2

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?

1
  • 2
    One problem with that line of thinking is that darn near EVERYTHING could be reduced down to some common root called "Entity" or something. And you end up with situations where a single line of inheritance isn't sufficient, like if you have a User class, which is inheritied the Employee class and the Contractor class, but later on you find that some employees return AS contractors and we need to track them differently from plain employees/contractors, etc etc etc. Basically it is a matter of judgement.
    – GHP
    May 26, 2017 at 13:29

1 Answer 1

5

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

And they all share a set of properties.

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-a Person, a Candidate is-a Person and so on.

e.g.

public class Person
{
   public string Name {get;set;}
}

public class Employee:Person
{
    public string EmployeeId {get;set;
}

public class Candidate:Person
{
    public string CandidateId {get;set;
}

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.

For example, the Payroll system will need to know my name in order to print it on my Payslip; so it makes sense for the SalariedEmployee to have a "Name" property. However the ClockInOut module really couldn't care less; its job is just to record the in and out times for an EmployeeId so to it I am just a TimeClockedEmployee with a unique Id.

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 reasons

  • Ability to keep a sharp and small focus on the Domain being modeled; having common properties - or worse, behaviour - that are never going to be used by a specific Domain model can makes development and maintenance harder.
  • Ability for the specific Domain area being modeled to change without having to affect any other model in the system;
  • Data Security, by avoiding exposing data to parts of the system that do not need it

Handling 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 both Hourly and Salaried employees, but you do not need it for ContractEmployees (who despite what some might say, are still People :) )

So now you have to break your nice inheritance to put duplicate properties on both HourlyEmployee and SalariedEmployee anyway or else deal with the possibility for some of your code to deal with objects that can have a null SSN. Now things are getting messy - does a null SSN represent a Contractor, or a mapping error? Who knows...

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-a Person and so on.

So an HourlyEmployee, SalariedEmployee,ContractEmployee, PotentialEmployee, VisitorNonEmployee,PizzaDeliveryPerson etc, can have (or not) a PersonalInformation structure if needed, that is indeed common to all those that have it, but which is not part of any hierarchy.

In terms of how this maps to the Database via the ORM, the individual backing tables will have columns for the PersonalInformation object, and most ORM systems will support Value Object Mapping to allow you to map that subset of properties into the PersonalInformation object as a Value object on the Entity in question. YMMV depending on the ORM in question.

e.g. This might store personal information about a person.

   public class PersonalInformation
   {
      public string Name {get;set;}
   }

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.

   public interface IEmployee
   {
      string EmployeeId {get;}
   }

This might indicate that an entity holds some personal information:

   public interface IPersonalInformationHolder
   {
      PersonalInformation PersonalInformation {get;}
   }

as seen in

   public class Employee : IEmployee,IPersonalInformationHolder
   {
        public string EmployeeId {get;}
        public PersonalInformation PersonalInformation {get;}
    }

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 an IEmployee to any part of the system that cares about EmployeeId

        public class ClockTimedEmployee: IEmployee
        {
            public string EmployeeId {get;set;}
        }

Another distinct advantage of this approach is that it guarantees that you can never accidentally pass a ClockTimedEmployee into any method that expects an IPersonalInformationHolder; the code simply will not compile. This helps to cut out a whole class of runtime bugs that are hard to avoid with inheritance.

4
  • Would you agree that the Composition over inheritance is exactly what Strategy pattern is all about? May 29, 2017 at 14:37
  • 1
    @ĐuroMandinić - yes, that is correct. Although I am sure someone could implement with abstract base classes as well but that would be going against the spirit of the thing :) May 29, 2017 at 21:48
  • I've seen somewhere an example with abstract base class. The author says that it does not really matter because the abs. class does the role of interface in that case. May 30, 2017 at 9:00
  • 1
    @ĐuroMandinić - indeed it doesn't; abstract classes can do the job just as well, except that for example in C# we tend to use interfaces as they are more idiomatic as abstractions. But they key point to remember is to prefer "Has-A" over "Is-A" and you can't go wrong... May 30, 2017 at 9:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.