![| Embarcadero RAD Studio Delphi C++Builder Blogs This Is How To Use The Factory Method Pattern in Delphi](https://i0.wp.com/blogs.embarcadero.com/wp-content/uploads/2022/09/pexels-andrea-piacquadio-3769135-7392041-scaled.jpg?resize=1140%2C694&ssl=1)
![Factory Method Pattern in Delphi | Embarcadero RAD Studio Delphi C++Builder Blogs Factory Method Pattern in Delphi](https://i0.wp.com/blogs.embarcadero.com/wp-content/uploads/2022/09/Implementation-of-AI-solution-for-legacy-software-Step-by-step-guide-and-framework-2-4727821.jpg?resize=300%2C195&ssl=1)
Do you want to know what the Factory Method Pattern is and what it can do for you? When you are writing simple or complex applications, you might need to create multiple classes inheriting the same class or implementing the same interface. This is easier when we have a small number of classes at the same level of inheritance. However, even with the best app builder software, when more and more classes are introduced, it becomes more difficult to maintain the hierarchy and to support the creation of objects at many different places.
To solve this problem, the Factory Method Pattern [1] was introduced.
The Factory Pattern has two types.
- The Factory Method Pattern
- The Abstract Factory Pattern
In this article, we are going to have a look at the Factory Method design pattern. We will explain what it actually is, what problems it solves, what pros and cons it has, and how we can implement it in Delphi.
Table of Contents
What is the Factory Method Pattern?
The Factory Method Pattern solves problems of object creation and maintenance by separating the creation of objects from the concrete implementation of classes. This is done by creating a Factory class that contains methods for instantiate classes that implement the same interface or are inherited from the same class.
Below you can see an easily understandable example with code.
Is there a Delphi example of the Factory Method Pattern?
Ok, let’s show you an example of the Factory Method Pattern.
Bill works as a programmer at an automated bakery that sells cheesecakes. His job is to write code that helps to create the type of cheesecake that a customer chooses. Easy job! Because, currently, there are only two types of cheesecakes that the bakery offers. Strawberry and Blueberry.
Bill started by writing classes for two types of Cheesecakes as you can see below and a function to create an instance of one of these two depending on the input received from the user. Bill wrote this function in the project file to make code easier to write and called it just there.
Unit Name: StrawberryCheeseCake.pas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
unit StrawberryCheeseCake; interface type TStrawberryCheeseCake = class(TObject) public procedure MakeCrust; procedure AddLayer; procedure AddFlavour; procedure Bake; End; implementation procedure TStrawberryCheeseCake.AddFlavour; begin WriteLn('Added Strawberry Flavour...'); end; procedure TStrawberryCheeseCake.AddLayer; begin WriteLn('Added Layers on Strawberry Cheesecake...'); end; procedure TStrawberryCheeseCake.Bake; begin WriteLn('Here''s your Strawberry Cheesecake...'); end; procedure TStrawberryCheeseCake.MakeCrust; begin WriteLn('Making Crust for your Strawberry Cheesecake...'); end; end. |
Unit Name: BlueBerryCheeseCake.pas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
unit BlueberryCheeseCake; interface type TBlueberryCheeseCake = class(TObject) public procedure MakeCrust; procedure AddLayer; procedure AddFlavour; procedure Bake; End; implementation procedure TBlueberryCheeseCake.AddFlavour; begin WriteLn('Added Blueberry Flavour...'); end; procedure TBlueberryCheeseCake.AddLayer; begin WriteLn('Added Layers on Blueberry Cheesecake...'); end; procedure TBlueberryCheeseCake.Bake; begin WriteLn('Here''s your Blueberry Cheesecake...'); end; procedure TBlueberryCheeseCake.MakeCrust; begin WriteLn('Making Crust for your Blueberry Cheesecake...'); end; end. |
Why is the Factory Method Pattern useful?
To illustrate why the Factory Method Pattern is needed, let’s continue with our example.
Imagine Bill needed to write the main function to get input from the user and instantiate one of these classes depending on that input.
The code can be found below.
File Name: CheeseCakeShop.dpr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
program CheeseCakeShop; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, StrawberryCheesecake, BlueberryCheesecake; type TCheeseCakeFlavour = (ckfStrawberry, ckfBlueberry); procedure PrepareCheeseCake; var Flavor: TCheeseCakeFlavour; FlavorStr: String; Cheesecake: TObject; begin Writeln('What Flavor of Cheesecake do you want?'); Readln(FlavorStr); Flavor := ckfStrawberry; Cheesecake := Nil; if FlavorStr = 'Strawberry' then Flavor := ckfStrawberry else if FlavorStr = 'Blueberry' then Flavor := ckfBlueberry; try case Flavor of ckfStrawberry: begin Cheesecake := TStrawberryCheeseCake.Create; (Cheesecake as TStrawberryCheeseCake).MakeCrust; (Cheesecake as TStrawberryCheeseCake).AddLayer; (Cheesecake as TStrawberryCheeseCake).AddFlavor; (Cheesecake as TStrawberryCheeseCake).Bake; ReadLn; end; ckfBlueberry: begin Cheesecake := TBlueberryCheeseCake.Create; (Cheesecake as TBlueberryCheeseCake).MakeCrust; (Cheesecake as TBlueberryCheeseCake).AddLayer; (Cheesecake as TBlueberryCheeseCake).AddFlavor; (Cheesecake as TBlueberryCheeseCake).Bake; ReadLn; end; end; finally Cheesecake.Free; end; end; begin try PrepareCheeseCake; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
When Bill compiled and ran his code, he found out that it worked like a charm and he could see an output that varied depending on the input supplied.
Input: Strawberry
![strawberrycake | Embarcadero RAD Studio Delphi C++Builder Blogs This Is How To Use The Factory Method Pattern in Delphi Strawberry cheesecake example](https://i0.wp.com/blogs.embarcadero.com/wp-content/uploads/2022/09/strawberrycake-6714122.jpg?resize=677%2C289&ssl=1)
Input: Blueberry
![blueberrycake | Embarcadero RAD Studio Delphi C++Builder Blogs This Is How To Use The Factory Method Pattern in Delphi Blueberry cheesecake example](https://i0.wp.com/blogs.embarcadero.com/wp-content/uploads/2022/09/blueberrycake-3539439.jpg?resize=620%2C199&ssl=1)
Wow! Amazing!
Bill was very happy that his code was working correctly and the bakery where he was working at that moment started gaining popularity. It was growing and more people were recruited to perform specific tasks.
One of such new positions is CCFO – Chief Cheesecake Flavor Officer. This new person’s name is Jake and he looked creative. His responsibility is to create new flavors of Cheesecakes. Sounds interesting! Right?
He is doing his job well from the very first day and created a new flavor of Cheesecake which is “Mango” cheesecake. Beautiful!
One day, Jake came to Bill and asked: “Hey Bill, can you make changes to your code so that we can create this new Mango cheesecake and give it to our customers?”
Bill thought a little bit and said: “Sure. Why not?”
Bill knew exactly what to do.
He went ahead and introduced the following changes to his code
- Created a new class called “MangoCheeseCake” similar to the other two classes.
- Added a new flavor called “ckfMango” type to the CheeseCakeFlavour enumeration
- Added a MangoCheeseCake unit to the uses list of the project file and
- Introduced changes to the “PrepareCheeseCake()” function to dispatch Mango cheesecake if a customer selects it.
He built his project and found that his code works like a charm and became happy. Starting from the following day, customers at the bakery could get Mango Cheesecake if they chose it on the menu.
Jake was happy but Bill was happier that morning.
As Jake is creative, he couldn’t stop thinking about new flavors and experiments.
Just before the day ended, Jake came to Bill to speak about a new flavor of Cheesecake “Coffee” that he created and asked to change the code so that his new cheesecake could be sold if a customer selects it. Bill agreed but he had a rather sad face because he knew that he would need to perform the same repetitive steps again to make the new flavor available.
Going home, Bill couldn’t stop thinking about all the new flavors that Jake would create in the future and the same repetitive changes that he would need to introduce to that code.
While thinking about it, he also remembered the words of one of his computer programming professors from college.
He used to say: “If you write the same code once, encapsulate it. If you write the same code twice, add another layer of encapsulation. If you write the same code thrice, stop being a programmer.”
In a separate article, know more about the brilliance of DNA manipulation in this article about Delphi’s DNABaser.
What problem does the Factory Method pattern solve?
Bill couldn’t stop thinking about all the ways he could avoid this redundant code writing and make his code easier to write, change and maintain.
As he finished his dinner he recollected the Factory Method – Design Pattern that the same professor had explained.
He remembered that the Factory Method – Design Pattern is used to separate creation from implementation and can be a solution to the code redundancy problem.
The next morning, Bill came back to the bakery and took a look at his code. Since he recollected all the programming principles that the professor had explained in the college, he found out that he had a new approach to his code.
Now, just when he looked at his code his eyes captured all the units in the uses list of the project file and he realized that to add any new flavor of Cheesecake he would need to update the list of uses and even make changes to the project file. He thought: “My code is so tightly coupled and I didn’t even think about it for so long. I must do something about it now.”
He also studied detailed steps to implement the Factory Method – Design Pattern and came up with the following steps to implement the same in his Cheesecake project.
- First of all, as all Cheesecake flavor classes were similar and had the same set of methods, he decided to create an interface that would have the same set of methods and implement all Cheesecake classes from this interface. This would ensure that new flavor classes have the same set of methods and also simplify implementation.
- Then, he needed to create a Factory class that would have all the methods to create objects of all flavor classes
- He also had to create a factory Generator class which would create a factory object depending on the input supplied
- It was also necessary to create an object of the Factory Generator class in the project file and just call one function.
- Done.
Below you can see the code changes that he introduced.
- He added an interface as it is indicated below.
Unit Name: CheeseCakeInterface.pas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
unit CheeseCakeInterface; interface type ICheeseCake = Interface procedure MakeCrust; procedure AddLayer; procedure AddFlavor; procedure Bake; End; implementation end. |
- He changed flavor classes to implement the above-described interface ICheeseCake
E.g. Class TStrawberryCheeseCake
1 2 3 4 5 6 7 8 9 10 |
… type TStrawberryCheeseCake = class(TInterfacedObject, ICheeseCake) public procedure MakeCrust; procedure AddLayer; procedure AddFlavor; procedure Bake; End; … |
- He created a factory class that has methods to create flavor class objects and now the interface depends not on classes but on interfaces.
Unit Name: CheeseCakeFactory.pas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
unit CheeseCakeFactory; interface uses CheeseCakeInterface; type TCheeseCakeFactory = class public function CreateStrawberryCheesecake: ICheeseCake; function CreateBlueberryCheesecake: ICheeseCake; function CreateMangoCheesecake: ICheeseCake; end; implementation uses StrawberryCheeseCake, BlueberryCheeseCake, MangoCheeseCake; function TCheeseCakeFactory.CreateBlueberryCheesecake: ICheeseCake; begin Result := TBlueberryCheeseCake.Create; end; function TCheeseCakeFactory.CreateMangoCheesecake: ICheeseCake; begin Result := TMangoCheeseCake.Create; end; function TCheeseCakeFactory.CreateStrawberryCheesecake: ICheeseCake; begin Result := TStrawberryCheeseCake.Create; end; end. |
- He created a factory generator class that has one method which asks for input from a customer, created a factory object, called the factory method to create an object of that flavor class that a customer specifies and called all necessary methods to prepare Cheesecake of that flavor.
Unit Name: CheeseCakeFactoryGenerator.pas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
unit CheeseCakeFactoryGenerator; interface type TCheeseCakeFlavour = (ckfStrawberry, ckfBlueberry, ckfMango); procedure PrepareCheeseCake; implementation uses CheeseCakeInterface, CheeseCakeFactory; procedure PrepareCheeseCake; var Flavor: TCheeseCakeFlavour; FlavorStr: String; Cheesecake: ICheeseCake; CheeseCakeFactory: TCheeseCakeFactory; begin Writeln('Which Flavour of Cheesecake do you want?'); Readln(FlavorStr); Flavor := ckfStrawberry; Cheesecake := Nil; if FlavorStr = 'Strawberry' then Flavor := ckfStrawberry else if FlavorStr = 'Blueberry' then Flavor := ckfBlueberry else if FlavorStr = 'Mango' then Flavor := ckfMango; CheeseCakeFactory := TCheeseCakeFactory.Create; try case Flavor of ckfStrawberry: Cheesecake := CheeseCakeFactory.CreateStrawberryCheesecake; ckfBlueberry: Cheesecake := CheeseCakeFactory.CreateBlueberryCheesecake; ckfMango: Cheesecake := CheeseCakeFactory.CreateMangoCheesecake; end; Cheesecake.MakeCrust; Cheesecake.AddLayer; Cheesecake.AddFlavor; Cheesecake.Bake; ReadLn; finally CheeseCakeFactory.Free; end; end; end. |
- He removed all the complexities from the project file and just called one method “PrepareCheeseCake” from CheeseCakeFactoryGenerator and it was done.
Unit Name: CheeseCakeShop.dpr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
program CheeseCakeShop; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CheeseCakeFactoryGenerator; begin try CheeseCakeFactoryGenerator.PrepareCheeseCake; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
Well done! There was a lot of code refactoring.
The code looked a little bit more complicated than before. But Bill felt a fraction of satisfaction after implementing all the coding principles he had learned at the college. However, he was still unsure if this was going to help him.
After all this, Bill remembered a discussion with Jake about the new flavor of cheesecake which would be “Coffee”.
He looked at the code again and started to think about changes he would need to make in the new code structure to implement this new flavor. He came up with the steps that are enumerated below.
- To create a new flavor class TCoffeeCheeseCake implementing an interface “ICheeseCake”
- To create a new method in CheeseCakeFactory to create and return a Coffee flavor class object
- To make changes in the PrepareCheeseCake method to call a new method from CheeseCakeFactory
As soon as he introduced the above-mentioned changes to his code, he realized that adding a new flavor became much easier now and no changes to the project file would be required.
Wow! This is amazing!
Now, Bill doesn’t about any new flavors Jake will decide to offer.
Bill has become creative too!!
What are the pros of the Factory Method Pattern?
- Tight coupling is avoided by separating concrete implementation and creation
- The Single Responsibility Principle is followed by moving object creation code to one place, making the code easier to support
- Open/Closed Principle: New classes can be easily introduced without breaking existing implementation
What are the cons of the Factory Method Pattern?
- The code may become more complicated as you need to introduce many new classes for the implementation
(But as you progress with the introduction of new classes, you will notice that the process of maintaining and changing classes is much easier than it was before.)
How can I try out the Factory Method pattern?
You can download a free copy of RAD Studio with Delphi today and try out the above code – plus a whole lot more! RAD Studio is extremely easy to use and can create applications which can run on Windows, macOS, iOS and Android with a single code base. It has tons of advanced features designed to make you super-productive and creating apps for your colleagues and customers faster than almost any competing technologies thanks to its robust VCL and FMX frameworks and low-code or no code approach to rapid application development.
References
[1] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. pp. 107ff. ISBN 0-201-63361-2.
This article was written by Embarcadero Tech Partner Softacom. Softacom specialize in all sorts of software development focused on Delphi. Read more about their services on the Softacom website.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition
I would call this a Simple Factory pattern, where the decision is made based on user input. In a Factory Method Pattern, no single class makes the decision. Instead, the superclass defers the decision to each subclass. I discuss this in my book on Python Programming with Design Patterns and in earlier books using Java and C#.
Thank you, for your explanation of factory method pattern.
But there might a memoryleak, because the CheeseCakes are never freed.
Also, the factory should decide depending on arguments, which cheesecake to be created.
I prefer the following solution:
unit CheeseCakeFactoryGenerator;
interface
procedure PrepareCheeseCake;
implementation
uses
CheeseCakeInterface,
CheeseCakeFactory;
procedure PrepareCheeseCake;
var
Flavor: TCheeseCakeFlavour;
FlavorStr: String;
Cheesecake: ICheeseCake;
CheeseCakeFactory: TCheeseCakeFactory;
begin
Writeln(‘Which Flavour of Cheesecake do you want?’);
Readln(FlavorStr);
Flavor := ckfStrawberry;
Cheesecake := Nil;
if FlavorStr = ‘Strawberry’ then
Flavor := ckfStrawberry
else if FlavorStr = ‘Blueberry’ then
Flavor := ckfBlueberry
else if FlavorStr = ‘Mango’ then
Flavor := ckfMango;
CheeseCakeFactory := TCheeseCakeFactory.Create;
try
Cheesecake := CheeseCakeFactory.CreateCheesecake(Flavor);
Cheesecake.MakeCrust;
Cheesecake.AddLayer;
Cheesecake.AddFlavor;
Cheesecake.Bake;
ReadLn;
finally
CheeseCake.Free;
CheeseCakeFactory.Free;
end;
end;
end.
——————————————————————–
unit CheeseCakeFactory;
interface
uses
CheeseCakeInterface;
type
TCheeseCakeFlavour = (ckfStrawberry, ckfBlueberry, ckfMango);
type
TCheeseCakeFactory = class
public
function CreateStrawberryCheesecake: ICheeseCake;
function CreateBlueberryCheesecake: ICheeseCake;
function CreateMangoCheesecake: ICheeseCake;
end;
implementation
uses
StrawberryCheeseCake,
BlueberryCheeseCake,
MangoCheeseCake;
function TCheeseCakeFactory.CreateCheesecake(aFlavor:TCheeseCakeFlavour): ICheeseCake;
begin
case aFlavor of
ckfBlueberry:
Result := TBlueberryCheeseCake.Create;
ckfMango:
Result := TMangoCheeseCake.Create;
ckfStrawberry:
Result := TStrawberryCheeseCake.Create;
end;
end.
Thanks Wolfgang!
“If you write the same code once, encapsulate it. If you write the same code twice, add another layer of encapsulation. If you write the same code thrice, stop being a programmer”
This simple phrase points out loads of things we can do and we really must think before starts coding