Introduction to Design Patterns in Delphi
This discussion paper was presented by James Heyworth to the Canberra PC Users Group Delphi SIG on 11/11/1996.
Please send comments and suggestions to james@obsof.com.
Contents
Introduction
How Delphi Helps You Define Patterns
Delphi Examples of Design Patterns
Pattern: Singleton
Pattern: Adapter
Pattern: Template Method
Pattern: Builder
Pattern: Abstract Factory
Pattern: Factory Method
Appendix: Key Elements of Delphi Class Definitions
Bibliography
Download Paper and Source Code Examples
Introduction
Design patterns are frequently recurring structures and relationships in object-oriented design. Getting to know them can help you design better, more reusable code and also help you learn to design more complex systems.
Much of the ground-breaking work on design patterns was presented in the book Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson and Vlissides. You might also have heard of the authors referred to as "the Gang of Four". If you haven''''t read this book before and you''''re designing objects, it''''s an excellent primer to help structure your design. To get the most out of these examples, I recommend reading the book as well.
This paper takes some sample patterns from Design Patterns and discusses their implementation in Delphi.
Another good source of pattern concepts is the book Object Models: Strategies, Patterns and Applications by Peter Coad. Coad''''s examples are more business oriented and he emphasises learning strategies to identify patterns in your own work.
How Delphi Helps You Define Patterns
Delphi implements a fully object-oriented language with many practical refinements that simplify development.
A summary of Delphi class concepts can be found in the following appendix.
The most important class attributes from a pattern perspective are the basic inheritance of classes; virtual and abstract methods; and use of protected and public scope. These give you the tools to create patterns that can be reused and extended, and let you isolate varying functionality from base attributes that are unchanging.
Delphi is a great example of an extensible application, through its component architecture, IDE interfaces and tool interfaces. These interfaces define many virtual and abstract constructors and operations.
Delphi Examples of Design Patterns
I should note from the outset, there may be alternative or better ways to implement these patterns and I welcome your suggestions on ways to improve the design. The following patterns from the book Design Patterns are discussed and illustrated in Delphi to give you a starting point for implementing your own Delphi patterns.
Pattern Name
Definition
Singleton
"Ensure a class has only one instance, and provide a global point of access to it."
Adapter
"Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn''''t otherwise because of incompatible interfaces."
Template Method
"Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm''''s structure."
Builder
"Separate the construction of a complex object from its representation so that the same construction process can create different representations."
Abstract Factory
"Provide an interface for creating families of related or dependant objects without specifying their concrete classes."
Factory Method
"Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclasses."
Note: These definitions are taken from Design Patterns.
Pattern: Singleton
Definition
"Ensure a class has only one instance, and provide a global point of access to it."
This is one of the easiest patterns to implement.
Applications in Delphi
There are several examples of this sort of class in the Delphi VCL, such as TApplication, TScreen or TClipboard. The pattern is useful whenever you want a single global object in your application. Other uses might include a global exception handler, application security, or a single point of interface to another application.
Implementation Example
To implement a class of this type, override the constructor and destructor of the class to refer to a global (interface) variable of the class.
Abort the constructor if the variable is assigned, otherwise create the instance and assign the variable.
In the destructor, clear the variable if it refers to the instance being destroyed.
Note: To make the creation and destruction of the single instance automatic, include its creation in the initialization section of the unit. To destroy the instance, include its destruction in an ExitProc (Delphi 1) or in the finalization section of the unit (Delphi 2).
The following Delphi 1 example illustrates two singleton classes, one derived from TComponent and another derived from TObject.
unit Singletn;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs;
type
TCSingleton = class(TComponent)
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
TOSingleton = class(TObject)
public
constructor Create;
destructor Destroy; override;
end;
var
Global_CSingleton: TCSingleton;
Global_OSingleton: TOSingleton;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents(''''Design Patterns'''', [TCSingleton]);
end;
{ TCSingleton }
constructor TCSingleton.Create(AOwner: TComponent);
begin
if Global_CSingleton <> nil then
{NB could show a message or raise a different exception here}
Abort
else begin
inherited Create(AOwner);
Global_CSingleton := Self;
end;
end;
destructor TCSingleton.Destroy;
begin
if Global_CSingleton = Self then
Global_CSingleton := nil;
inherited Destroy;
end;
{ TOSingleton }
constructor TOSingleton.Create;
begin
if Global_OSingleton <> nil then
{NB could show a message or raise a different exception here}
Abort
else
Global_OSingleton := Self;
end;
destructor TOSingleton.Destroy;
begin
if Global_OSingleton = Self then
Global_OSingleton := nil;
inherited Destroy;
end;
procedure FreeGlobalObjects; far;
begin
if Global_CSingleton <> nil then
Global_CSingleton.Free;
if Global_OSingleton <> nil then
Global_OSingleton.Free;
end;
begin
AddExitProc(FreeGlobalObjects);
end.
Pattern: Adapter
Definition
"Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn''''t otherwise because of incompatible interfaces."
Applications in Delphi
A typical example of this is the wrapper Delphi generates when you import a VBX or OCX. Delphi generates a new class which translates the interface of the external control into a Pascal compatible interface. Another typical case is when you want to build a single interface to old and new systems.
Note Delphi does not allow class adaption through multiple inheritance in the way described in Design Patterns. Instead, the adapter needs to refer to a specific instance of the old class.
Implementation Example
The following example is a simple (read only) case of a new customer class, an adapter class and an old customer class. The adapter illustrates handling the year 2000 problem, translating an old customer record containing two digit years into a new date format. The client using this wrapper only knows about the new customer class. Translation between classes is handled by the use of virtual access methods for the properties. The old customer class and adapter class are hidden in the implementation of the unit.
unit Adapter;
interface
uses SysUtils, Classes;
type
{ The new class }
TNewCustomer = class
private
FCustomerID: Longint;
FFirstName: string;
FLastName: string;
FDOB: TDateTime;
protected
function GetCustomerID: Longint; virtual;
function GetFirstName: string; virtual;
function GetLastName: string; virtual;
function GetDOB: TDateTime; virtual;
public
constructor Create(CustID: Longint); virtual;
property CustomerID: Longint read GetCustomerID;
property FirstName: string read GetFirstName;
property LastName: string read GetLastName;
property DOB: TDateTime read GetDOB;
end;
{ An interface method }
{ Lets us hide details of TOldCustomer from the client }
function GetCustomer(CustomerID: Longint): TNewCustomer;
implementation
const
Last_OldCustomer_At_Year_2000 = 15722;
Last_OldCustomer_In_Database = 30000;
{ The new class }
constructor TNewCustomer.Create(CustID: Longint);
begin
FCustomerID := CustID;
FFirstName := ''''A'''';
FLastName := ''''New_Customer'''';
FDOB := Now;
end;
function TNewCustomer.GetCustomerID: Longint;
begin
Result := FCustomerID;
end;
function TNewCustomer.GetFirstName: string;
begin
Result := FFirstName;
end;
function TNewCustomer.GetLastName: string;
begin
Result := FLastName;
end;
function TNewCustomer.GetDOB: TDateTime;
begin
Result := FDOB;
end;
type
{ The old class }
TOldDOB = record[1] [2] [3] [4] 下一页
[办公软件]在Excel中插入时间/日期TODAY和NOW函数 [网络安全]激活型触发式AutoRun.inf病毒和rose病毒的清除方案
[Web开发]asp.net c#其它语句之break、continue、goto实例介… [Web开发]一大堆常用的超强的正则表达式及RegularExpressio…
[平面设计]制作动态GIF图像的好帮手─Coffee GIf Animator [平面设计]功能超强的GIF动画制作软件─Ulead Gif Animator
[平面设计]AutoCAD常用快捷命令(外加中文说明) [平面设计]AutoCAD常用快捷命令(字母类,外加中文说明)
[平面设计]AutoCAD快捷命令介绍 [平面设计]多种方法解决AutoCAD打印时出现错误