Click here to Skip to main content
Click here to Skip to main content

Parameterized Factory Method in Delphi

, 28 Dec 2012
Rate this:
Please Sign up or sign in to vote.
Factory Method is a creational design pattern, whose intent (according to Design Patterns: Elements of Reusable Object-Oriented Software) is to:“Define an interface for creating an object, but let subclasses decide which class to instantiate.

Factory Method is a creational design pattern, whose intent (according to Design Patterns: Elements of Reusable Object-Oriented Software) is to:

“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”

Do you want to know more about design patterns? Check reference [1] at the bottom for extra reading. 

In this post we will consider a variation of the pattern referred as parameterized factory method. This variation just refers to the first part of the intent, that is, “define an interface for creating an object”. Why is that? What about the rest of the intent? Well, there are times in which sub-classing is not possible or not suitable; nonetheless, it is useful to define a factory method to encapsulate the creation of an object.

Let’s proceed with the example we used in our previous post (template design pattern); that is: Design an application that allows drawing different styles of houses (ei. country house, city house) using ASCII art.

The GUI of our app contains some VCL components as you can see in the images below. Clicking the buttons will result in an ASCII house printed out in the memo area. Depending on the Draw… button being clicked, the house will look differently: it can be a country house or a city house.

Factory Pattern Example (Delphi) – Country House

Figure1 A Country House (Draw Country House button was clicked)

Factory Pattern Example (Delphi) – City House

Figure 2 A City House (Draw City House button was clicked)

Behind the scenes, assume that we have implemented the following class hierarchy:

Factory Pattern Example (Delphi) – Class diagram


THouse is an abstract class that defines several methods for displaying the different parts of the house [2] or the house as a whole [3].

TCountryHouse and TCityHouse are two concrete (non-abstract) classes that actually override some of the methods of its superclass, allowing this way different look-and-feel; that is, a house in the country and a house in the city look differently.

When a button is clicked, the corresponding OnClick event is triggered. We have to provide logic (source code) to define what the button is going to do in response to the OnClick event. Using fancy words, we have to implement the event handler for the OnClick event. I have created the following event handler which is going to be shared by both buttons [4]:

//OnClick event handler for the buttons
 procedure TfrmMain.btnDrawGenericHouseClick(Sender: TObject);
 var
   House: THouse;
 begin
   //If the "Sender" object is not a button do nothing
   if not(Sender is TButton) then Exit;
 
  //The concrete house object being created depends
   //on the button being clicked
   if TButton(Sender).Name = 'btnDrawCountryHouse' then
     House:= TCountryHouse.Create
   else if TButton(Sender).Name = 'btnDrawCityHouse' then
     House:= TCityHouse.Create;
 
  with House do
   begin
     //cmbHasChimney is a combo box (TComboBox) that allows
     //to decide whether to add a chimney or not to the house
     case cmbHasChimney.ItemIndex of
       0: HasChimney:= True;
       1: HasChimney:= False;
     end;
     //"memFrame" is a memo (TMemo) used to print out the house.
     memFrame.Text:= BuildIt;
     Free;
   end;
 end;

This works of course, but it lacks flexibility. What happens if we want to add a third button, or a fourth, or a five in order to add new types of houses? In that case, you will have to rework the code above and the fragment in blue will grow up big as new house types are added. You will end coding something like this:

if TButton(Sender).Name = 'btnDrawCountryHouse' then
     House:= TCountryHouse.Create
else if TButton(Sender).Name = 'btnDrawCityHouse' then
     House:= TCityHouse.Create
 else if TButton(Sender).Name = 'btnDrawDogHouse' then
     House:= TDogHouse.Create
 else if TButton(Sender).Name = 'btnDrawTreeHouse' then
     House:= TTreeHouse.Create
 else if TButton(Sender).Name = 'btnDrawGhostHouse' then
     House:= TGhostHouse.Create;

That is the only part changing within the method. Why don’t we take that chunk of code and place it somewhere else? For that, we can create a factory method. The factory method will take one parameter to decide the concrete house type to instantiate [4]. The return type of the factory method will be THouse, because THouse enfolds all the other house subtypes. What about this?

function TfrmMain.MakeHouse(Sender: TObject): THouse;
 begin
   if TButton(Sender).Name = 'btnDrawCountryHouse' then
     Result:= TCountryHouse.Create
   else if TButton(Sender).Name = 'btnDrawCityHouse' then
     Result:= TCityHouse.Create
 else if TButton(Sender).Name = 'btnDrawDogHouse' then
     Result:= TDogHouse.Create
 else if TButton(Sender).Name = 'btnDrawTreeHouse' then
     Result:= TTreeHouse.Create
 else if TButton(Sender).Name = 'btnDrawGhostHouse' then
     Result:= TGhostHouse.Create;
 end; 

MakeHouse is a parameterized factory method. It is called factory because its only purpose in life is to create an object. It is adorned with the parameterized term, because it takes a parameter (Sender) to decide the concrete class to instantiate.

What does this mean to our previous code (event handler method)? Well, see it by yourself:

//OnClick event handler for the buttons
 procedure TfrmMain.btnDrawGenericHouseClick(Sender: TObject);
 var
   House: THouse;
 begin
   //If the "Sender" object is not a button do nothing
   if not(Sender is TButton) then Exit;
 
  //The concrete house object being created depends
   //on the button being clicked
   House:= MakeHouse(Sender); //See our factory method in action
 
  with House do
   begin
     //cmbHasChimney is a combo box (TComboBox) that allows
     //to decide whether to add a chimney or not to the house
     case cmbHasChimney.ItemIndex of
       0: HasChimney:= True;
       1: HasChimney:= False;
     end;
     //"memFrame" is a memo (TMemo) used to print out the house.
     memFrame.Text:= BuildIt;
     Free;
   end;
 end;

Now, as you can see, the event handler will remain the same no matter how many different subtypes of houses we have (add). Everything about the creation of a house object is isolated in the factory method.

One final comment: Delphi is a hybrid programming language, allowing both structured and object oriented programming. In Delphi, we can have functions outside all classes’ scope. That’s not very Object Oriented. Isn’t? Besides, you can’t do that in languages like Java, Python, Ruby or C#.

In general, you should have a Factory class containing the factory method. In this particular Delphi example you might argue that we are just adding code for nothing. Yes, I give you that. But there are cases, in which having a hierarchy of factory classes comes naturally and the subfactories decide the concrete object to be created. We’ll cover that scenario in future posts Smile | :)

Notes 

[1] Books on Design Patterns:

Design Patterns: Elements of Reusable Object-Oriented Software

Head First Design Patterns

Object Models: Strategies, Patterns, and Applications (2nd Edition)

[2] BuildFloor(), BuildWalls(), BuildRoof(), BuildChimney(). These methods draw out the floor, walls, roof and chimney (if any) respectively.

[3] BuildIt(): this method calls the methods in [2] and as a result it draws the whole house.

[4] You can define the event handler for the buttons at design time using Delphi’s Object Inspector

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

yanniel
Software Developer Digital Rapids
Canada Canada
My name is Yanniel Alvarez Alfonso. I was born in San Antonio de los Baños, Havana Province, Cuba on October 24th, 1982.
 
I majored in Information Technology Engineering at José Antonio Echeverría Polytechnic Institute (CUJAE) in Havana City, Cuba (July 2006). After that, I got a Masters Degree in Applied Computer Science at the same University (May 2009).
 
I used to work as a professor of Information Technology at CUJAE. Right now, I work as a Software Developer in Toronto, Canada. I moved to Canada under the Skilled Worker Program on February 26th, 2010.
 
This is my personal blog: Yanniel's notes; in which I write about miscellaneous topics.
 
The link at the end of this sentence compiles an index of all the articles I have written so far about Delphi Programming.

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 29 Dec 2012
Article Copyright 2012 by yanniel
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid