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.