SOLID Principles in Delphi [2] — The Open/Closed principle

Marco
Marco
Mar 25 · 3 min read

The second principle of SOLID is the Open/Closed principle: ‘Classes and other entities should be open for extension but closed for modification’. This article is about how in Delphi you keep a class closed for modification, but open for extension.

Let me emphasise one part; achieving this goal via inheritance or overriding classes is a bad idea in my opinion. If you want to read more about this, check out what Bertrand Mayer and Robert C. Martin have to say on this subject.

Okay, let’s take the following example:

type
TShip = class
// This is the actual ship
end;
TPartList = class
// This class holds all the needed parts for building a ship
end;
TShipLayout = class
// This class provides the blueprint of the ship to build
function GetBlueprint: TList<TPoint>; // just as an example
end;
// This is the factory class to build a ship
TShipBuilder = class
private
FParts: TPartList;
FShipLayout: TShipLayout;
public
procedure LoadParts(APartList: TPartList);
procedure LoadShipLayout(ALayout: TShipLayout);
function BuildShip: TShip;
end;

As you can see, these are simple classes, without interfaces and without the possibility to extend them easily. The ShipBuilder class is however already (partly) closed for modification; the internal operation is separated from the outside world because the local variables are made private. In Delphi however, you can still access these variables when you access this class from the same file. Therefore, a simple improvement is to make the private a strict private.

However, the inner workings of this class itself is still open for modification. One can easily call on the BuildShip function before even providing the necessary parts and ship layout.

But how do we ensure that these classes are open for extension, but remain closed for modification?

The answer? Interfaces!

Why interfaces? This has to do with another principle: program against interfaces and not against implementations. The example above is the implementation of the class TShipBuilder. As soon as you start working with this implementation, you are automatically ‘stuck’ with the TPartList and TShipLayout implementations as well. Let’s see how we can improve this. The first step is to create several interfaces and modify the TShipBuilder class to implement those new interfaces. The second step is to use dependency injection to open this class for extension, but close it for modification. If you do that, you will end up with something like this:

type
TShip = class
// This is the actual ship class
end;
IPartList = interface
// Necessary info to provide the parts
end;
TPartList = class(TInterfacedObject, IPartList)
// This class holds all the needed parts for building a ship
end;
IShipLayout = interface
// Necessary info to provide the blueprint
function GetBlueprint: TList<TPoint>;
end;
TShipLayout = class(TInterfacedObject, IShipLayout)
// This class implements the blueprint function of the ship to build
function GetBlueprint: TList<TPoint>;
end;
TShipBuilder = class
strict private
FParts: IPartList;
FShipLayout: IShipLayout;
procedure LoadParts;
procedure LoadShipLayout;
public
constructor Create(APartList: IPartList; ALayout: IShipLayout);
function BuildShip: TShip;
end;

So, what exactly did we do? As you can see, we now have a TShipBuilder class which requires the two interfaces via a constructor. Both the LoadParts and LoadShipLayout procedures are made strict private, which ensures that the class is closed for modification, so we use these procedures only within the BuildShip function for example.

Additionally, our class is open for extension. It is possible to extend this ShipLayout functionality for example by just creating another implementation of the IShipLayout interface, as long as we implement the GetBlueprint function. We just have to make sure that we will provide a class with the IShipLayout interface implemented when calling the constructor of the TShipBuilder class.

As with most examples I’ve showed earlier, be aware that you shouldn’t put both the interfaces and classes all in the same file. It’s just for the sake of readability that these are in one place right now.

So, to summarise: to use the Open/Closed principle in Delphi you need interfaces to make a class open for extensions and make use of the (strict) private keyword to keep the class from modifications. Use as little inheritance as possible, because with inheritance, you risk opening the class for modifications. And remember: always program against interfaces instead of implementations.

Become a GDK Delphi hero! Subcribe here to our Delphi mailing list!

Originally published at https://gdksoftware.com.

Geek Culture

Proud to geek out.

Sign up for Geek Culture Hits

By Geek Culture

Subscribe to receive top 10 most read stories of Geek Culture — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Geek Culture

A new tech publication by Start it up (https://medium.com/swlh).

Marco

Written by

Marco

Co-owner of GDK Software, Delphi developer

Geek Culture

A new tech publication by Start it up (https://medium.com/swlh).

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store