Creating Inherit Classes
Inheritance is how we copy and paste code properly in order to prevent duplication.
Oct 30, 2018 • 11 Minute Read
The Problem This Solves
In creating code, you will often encounter situations where you need to write code for a new scenario that is almost exactly the same, except for a few minor exceptions. The naïve approach (which nonetheless is often employed by experienced folks who should know better) is copy and paste – literally taking the code and then having two copies that you must debug, test, and maintain. Here's a principle:
Inheritance is HOW we copy and paste code properly.
Imagine that when you needed to copy and paste, instead of pasting the actual code, you instead tracked a reference to the original code, so that you didn’t have duplication. When you need to make changes, you used special keywords that indicated your intentions for the overrides – this is all we’re doing with inheritance! Inheritance and polymorphism, in general, are systems by which we make copies and adjustments –systems which give us all of the upsides of the duplication with none of the downsides.
Our Scenario
Consider an application that makes benefits information available for employees, and their spouses, of a large corporation. The spouse can read most of the information that an employee can but cannot change it; that is, they cannot save it to the database, without going through an authorization process. In the current codebase, these differences are reflected with copy and pasted code and some awkward conditional logic which is brittle and scattered.
We have an Employee class which encapsulates most of the benefits logic:
///<summary>
/// The different account types for users
///</summary>
public enumUserTypes
{
///<summary>
/// The user is the benefits-eligible employee
///</summary>
Employee,
///<summary>
/// The user is a spouse of the benefits-eligible employee
///</summary>
Spouse
}
///<summary>
/// Represents the benefits information for the current user.
///</summary>
public class Employee
{
///<summary>
/// The Health Plan Code for the user
///</summary>
public string HealthPlanCode { get; set; }
///<summary>
/// The balance of the retirement plan for the user.
///</summary>
public decimal RetirementBalance { get; set; }
///<summary>
/// The number of PTO days the user has remaining for the current plan year.
///</summary>
public int PTODaysRemaining { get; set; }
///<summary>
/// The type of the current user.
///</summary>
public UserTypes UserType { get; set; }
public void Save()
{
if (this.UserType != UserTypes.Employee)
{
// send the request to Admin
// who will get authorization from the employee for the change
}
else// UserType == UserType.Employee
{
// actual persistence logic goes here
}
}
}
At the top, we have an enum which defines our two user types, Employee and Spouse, which serves as the type for the class’ UserType property. The class has several properties which relay the information about the state of the user’s benefits, either to the employee or their spouse, when they’re logged in.
In the Save method at the bottom, we have a check to make sure that the current user is an employee and not a spouse – we don’t want the spouse making unauthorized changes to the benefits and creating marital strife, as well as paperwork trouble for us. The code we’ve got works – it passes all the unit tests, so it satisfies at least the first measure of software quality: correctness. But these kinds of knotty if-then statements, which eventually multiply into switch statements when you need to add a Dependent user type, for example, are a kind of code smell that something is just not right. Clearly, we need to refactor this code to better reflect the nature of the model.
A Better Way
We’ll begin by distinguishing between Employees and Spouses by different classes, rather than strictly by the user type property. If we’re going to have an Employee class and a Spouse class and share a lot of code between them, our first instinct is to copy and paste that code. Rather than doing that literally, we’ll copy and paste in a nice, object-oriented way, by creating a new Spouse object that inherits from the Employee object:
public class Spouse: Employee
{
}
That's it. At this point, we have a separate object that is a much better reflection of the model, distinguishing that the user is not an Employee of type Spouse, which is not true, but is a user of type Spouse, which – at this point – has all the privileges and power of an employee user type.
We mentioned earlier that we have conditional logic spread throughout the code, maybe in a form like this:
switch (user.UserType)
{
case UserTypes.Employee:
// do employee-oriented stuff
break;
case UserTypes.Spouse:
// do spouse-oriented stuff
break;
}
Now that we're moving towards a proper inherited model, we'll get that refactored – eventually. But for the time being, we want our new classes to reflect their user type automatically. To make this happen, we'll change the UserType property on the base Employee class from an automatically-backed property, to simply returning "Employee" as its user type:
///<summary>
/// The type of the current user.
///</summary>
public virtual UserTypes UserType => UserTypes.Employee;
Because Spouse inherits from Employee, this means that without any changes, Spouses would be treated as Employees. Because of this, we've added the virtual keyword – this marks the property or method as being eligible to be overridden. We'll override the property in our Spouse class like this:
public override UserTypes UserType => UserTypes.Spouse;
Simple. Our scattered conditional logic will continue to work until we can consolidate it. Now, about that Save method – we want to split the two branches into separate calls on separate objects. We start by modifying the Employee Save() call like this:
public void Save()
{
// actual persistence logic goes here
}
In this call, we would do the actual persistence to the data store. We then add the following code to the Spouse class:
public newvoid Save()
{
// send the request to Admin
// who will get authorization from the employee for the change
}
We’ve moved the notification and authorization code for the admin to this class. Note that instead of override here on the child method, I’m now using new, and furthermore I didn’t add the virtual keyword to the base method. This is sometimes the approach you want to take for object-oriented reasons, particularly because you may not have ACCESS to the base method, as when you’re inheriting from a class in a framework you don’t control. When we use this approach, with the new keyword on a method in an inheriting class, this is called hiding.
What's the difference? They both seem to override the Save method, at least in an informal sense. The difference comes in how a call to Save() is resolved. In the following code:
var spouse = new Spouse();
((Employee)spouse).Save();
We're instantiating a Spouse object, but then casting it back to the base Employee class to call Save. In this case, with the hiding approach, the base Employee class' Save method is called, NOT the Spouse's new Save method. If instead, we overrode it with the virtual keyword on the base class, this same code would call the Spouse's overriding method. In short –The difference between overriding and hiding is how method calls are resolved. Keep this difference in mind as you make your modeling choices.
Inheriting from a User Class
Let's close by considering how we could make Save go away altogether for the Spouse class – we don't want it in Intellisense, and "we want the compilation to fail if a developer tries to call Save() on it. In that case, we could create an abstract base class User and inherit from it in both the Employee and Spouse classes:
Because the Save functionality exists only for the Employee, we would place the Save method there, outside of the inheritance chain for the Spouse. Our properties would be placed in the User class and our Save method in the Employee class:
///<summary>
/// The different account types for users
///</summary>
public enumUserTypes
{
///<summary>
/// The user is the benefits-eligible employee
///</summary>
Employee,
///<summary>
/// The user is a spouse of the benefits-eligible employee
///</summary>
Spouse
}
public abstractclassUser
{
///<summary>
/// The Health Plan Code for the user
///</summary>
public string HealthPlanCode { get; set; }
///<summary>
/// The balance of the retirement plan for the user.
///</summary>
public decimal RetirementBalance { get; set; }
///<summary>
/// The number of PTO days the user has remaining for the current plan year.
///</summary>
public int PTODaysRemaining { get; set; }
///<summary>
/// The type of the current user.
///</summary>
public virtual UserTypes UserType { get; }
}
///<summary>
/// Represents the benefits information for the current user.
///</summary>
public class Employee: User
{
public override UserTypes UserType => UserTypes.Employee;
public void Save()
{
// actual persistence logic goes here
}
}
public class Spouse: User
{
public override UserTypes UserType => UserTypes.Spouse;
}
As you can see, the great majority of the code, which is the same for both User types, Employee and Spouse, now goes in User, with just a few exceptions defined in each child class.
Wrap-Up and Takeaways
Here are a few key items to keep in mind with inheriting, overriding and hiding:
-
Inheritance is HOW we copy and paste. When you feel the urge to copy and paste, you should channel that energy into designing a proper inheritance model.
-
Overriding a method allows you to change the behavior of a parent routine – use the virtual keyword to mark an element as eligible for being overridden.
-
Hiding a method means creating a new definition for a parent method and leaves the parent method available for executing via typecasting. Keep these differences in mind.
-
To TRULY hide a method from a related class, refactor that method outside of the inheritance chain for the object.