帕里的设计模式手册
:京工之鸟 翻译
设计模式------用DELPHI表述(帕里的设计模式手册)[翻译作品]
什么是设计模式?
一种设计模式是针对于某一类设计问题的,已被证实确实可靠有效的一种设计的通用思
路和解决方案;它包含了一些用来在特定场景下解决一般设计问题的类和相互通信的对
象。它所归纳的用以解决某一类问题的方案的有效性都是已经被很多的实际设计所证明
了的。同时,它也凝聚了许多经验丰富的程序员的设计经验。一个熟悉这些模式的设计
者不需要再去发现它们,而能够立即将它们应用于设计问题中。设计模式使人们可以更
加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系
统开发者更加容易理解其设计思路。设计模式帮助你做出有利于系统复用的选择,避免
设计损害了系统复用性。通过提供一个显式类和对象作用关系以及它们之间潜在联系的
说明规范,设计模式甚至能够提高已有系统的文档管理和系统维护的有效性。简而言之,
设计模式可以帮助设计者更快更好地完成系统设计。设计模式并不描述链表和h a s h表
那样的设计,尽管它们可以用类来封装,也可复用,也不包括那些复杂的、特定领域内
的对整个应用或子系统的设计。
以下的设计模式来源于著名的DELPHI建模工具ModelMaker的用户手册:
封皮(Wrapper)模式:[也称转接卡(Adapter)模式]
把一个类的接口转换为调用它的客户行为所需要的接口形式;
中介者(Mediator)模式:[译者:实际上我更希望把它翻译为指挥官模式]
为被引用的类创建事件句柄,用以耦合他们;
单体(Singleton)模式:
确保一个类只创建一个实例,并且提供一个全局入口点来调用它;
装饰(Decorator)模式:
动态地给一个对象添加一些额外的职责;
锁定(Lock)模式:
提供一个机制临时的锁定一个类的某些外性;
访问者(Visitor)模式:
通过一个类来表述一个作用于某对象结构中的各元素的操作;
观察者(Observer)模式:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的
对象都得到通知;
我将写一个设计数据感应元件的实例来阐明两个最常见的设计模式,另外,下面所列的
几种在设计模式一书中所提及的模式,我也将给出一个相同的可行的例子。
抽象工厂(Abstract Factory)模式
生成器(Builder)模式
工厂方法(Factory Method)模式
封皮(Wrapper)模式
意图
封皮(Wrapper)模式把一个类的接口修改成客户调用所需要的另一种接口,这样就使原本
因接口不兼容的类而无法一起使用的类能够在一起配合使用。
动机
在Delphi的Object Pascal语言中,多态性是基于类的类型而不是基于相同的接口来实
现的。这就意味着:为了让两个类能够支持同样的接口,它们必须有同样的祖先类这样
才能在其他客户对象调用的时候实现多态性。但是有的时候我们希望两个已存在而互相
之间又没有关系的类能够在一起工作,封皮(Wrapper)模式就能够让你用另一个类来封装
某一个类的部分或全部接口,这就像在DELPHI里用uses部分来实现多继承关系一样。
让我们来看一个例子,有一个类TSample从TObject继承下来,但是我们希望能把TSample
这个类放到Delphi的控件面板(component palette)上,然而我们知道,如果一个类能
够放置到控件面板上,这个类必须从TComponent派生下来。现在我们不想在改变TSample
的祖先类TObject的情况下(比如我们并没有TSample的源代码)把TSample放到控件面板
上,我们可以构建一个继承自TComponent的并且'uses'或包含了TSample 类的TSampleWapper
类,因为TSampleWapper类继承自TComponent,所以这个类能够放置到控件面板上。现在
我们在TSampleWapper类中封装出TSample类的接口,这样TSampleWrapper类通过调用
TSample类相应的方法和属性来,实质上就可以去反映、代表他所封装的TSample类的行为
和属性;
使用封皮(Wrapper)模式的另一个理由是它可以使我们更好的遵循Demeter规范。这个
规范告诉我们的一个最基本的东西就是:不要有越过一层以上的对象引用;
现在,假设类TSample和TSampleWrapper的接口定义如下:
type
TSample = class (TObject)
private
FSomeValue: Integer;
public
function SomeAction(const Data: string): Boolean;
property SomeValue: Integer read FSomeValue write FSomeValue;
end;
TSampleWrapper = class(TComponent)
private
FSample: TSample;
public
property Sample: TSample read FSample;
end;
根据Demeter规范,像SampleWrapper.Sample这样的调用非常棒,但SampleWrapper.
Sample.SomeAction 这样的调用就不是什么好习惯了。我们应该在TSampleWrapper 里定
义一个SomeAction 方法来调用Sample.SomeAction,这样看上去就好多了。
当然,实际上,像ListBox.Canvas.Brush.Color这样违反规则的代码,虽然用了三层的引
用,但也是存在的。
实现
现在,我们将用上面假设的那两个类来示范封皮(Wrapper)模式的具体实现。在范例
TSampleWrapper类中,它通过Sample属性包含了一个TSample类。ModelMaker的封皮
(Wrapper)模式可以帮助我们在TSampleWrapper类的接口申明中创建SomeAction方法
和SomeValue属性来完全实现需要被封装的成员的封装;
TSampleWrapper = class (TComponent)
private
FSample: TSample;
protected
function GetSomeValue: Integer;
procedure SetSomeValue(Value: Integer);
public
function SomeAction(const Data: string): Boolean;
property Sample: TSample read FSample;
property SomeValue: Integer read GetSomeValue write SetSomeValue;
end;
像上面这样定义以后,我们就可以直接调用TSampleWrapper类的SomeAction方法,而不
需要再像SampleWrapper.Sample.SomeAction这样来调用了。这个定义的具体实现如下:
(请注意,下面的代码是完整的,并且已经可以直接编译的)
function TSampleWrapper.GetSomeValue: Integer;
begin
Result := Sample.SomeValue;
end;
procedure TSampleWrapper.SetSomeValue(Value: Integer);
begin
Sample.SomeValue := Value;
end;
function TSampleWrapper.SomeAction(const Data: string): Boolean;
begin
Result := Sample.SomeAction(Data);
end;
在这个例子里,你可以看到到一点封皮(Wrapper)模式的功能。总得来说归纳如下:
1,所有重新封装以后的类成员,都具有和被重新封装前同样的外性(比如名字、数据类型);
2,重新封装以后的属性通过读写方法来存取原有的属性;
3,用属性重新封装过以后的数据域也同样通过读写方法来存取原来的数据域;
4,用事件重新封装过的事件,用读写方法来存取封装前的事件,而非用事件句柄来实现;
5,用方法重新封转过的方法,直接通过调用并传递参数给原有方法,然后直接返回所得值来实现。
抽象工厂(abstract factory)模式
出处
本模式源于设计模式一书,但模式的DELPHI实现是由Shaun Parry 完成的。
意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
动机
[详细的动机说明请参见设计模式一书],考虑一个复合类,它在不同的应用程序中创
建时都将包含有相同的成员但又各有不同。就像你有一份文档,用不同的打印机打印出
来看上去会各有一点不同,但无论用什么打印机打印,最后打出来的文档内容应该都是
相同的。抽象工厂真正的内涵是你可以传递具有相同接口的一个类(也就是那个工厂)
给构造器,这个接口是被构造器所用的,但接口的具体实现方法,却由不同的工厂来确
定。
在上面的类流程图中,你可以看到Client类的函数CreateProduct是如何调用抽象工厂
(AbstractFactory)类的CreateProduct函数来创建一个AbstractProduct类的对象。因
为在Client类和ConcreteProduct类之间并没有任何直接的牵连,所以ConcreteProduct
可以是任何一个AbstractProduct的子类。
实现
下面是一个用来创建上面带有三个编辑框(edit boxe)的面板(panel)的例子,在这个例
子中,我们准备用专门创建的一个叫PanelMaker的类的CreatePanel方法来创建我们的
面板。
Type
TPanelMaker = class (TObject)
public
function CreatePanel( aOwner : TWinControl;
Factory : TAbstractFactory ): TPanel;
end;
implementation
function TPanelMaker.CreatePanel(aOwner : TWinControl; Factory : TAbstractFactory): TPanel;
var
TempPanel : TPanel;
TempEdit1 : TEdit;
TempEdit2 : TEdit;
TempEdit3 : TEdit;
begin
TempPanel := Factory.MakePanel( aOwner );
TempEdit1 := Factory.MakeEdit( TempPanel, 'Test1', 10, 10 );
TempEdit2 := Factory.MakeEdit( TempPanel, 'Test2', TempEdit1.Top + TempEdit1.Height + 10,
TempEdit1.Left );
TempEdit3 := Factory.MakeEdit( TempPanel, 'Test3', TempEdit2.Top + TempEdit2.Height + 10,
TempEdit2.Left );
CreatePanel := TempPanel;
end;
请注意,我们并没有直接用TPanel.Create来创建我们的面板,而是用了一个抽象工厂的
方法MakePanel来代替它,这样,如果我们要改动的时候,将变得更加容易。
抽象工厂的具体定义如下:
type
TAbstractFactory = class (TObject)
protected
FPanel : TPanel;
public
function MakeEdit( aOwner : TWinControl;vText : string;vTop : Integer;vLeft : Integer ): TEdit; virtual;
function MakePanel( aOwner : TWinControl ) : TPanel; virtual;
end;
implementation
function TAbstractFactory.MakeEdit( aOwner : TWinControl;vText : string;vTop : Integer;vLeft : Integer ): TEdit;
var
TempEdit : TEdit;
begin
TempEdit := TEdit.Create( aOwner );
TempEdit.Parent := aOwner;
TempEdit.Text := vText;
TempEdit.Top := vTop;
TempEdit.Left := vLeft;
MakeEdit := TempEdit;
end;
function TAbstractFactory.MakePanel( aOwner : TWinControl ) : TPanel;
var
TempPanel : TPanel;
begin
TempPanel := TPanel.Create( aOwner );
TempPanel.Parent := aOwner;
MakePanel := TempPanel;
end;
这个定义是用来创建标准编辑框和标准面板的,但是,如果我们想要创建一个特殊类型
的编辑框,比如说,我们希望那个编辑框只允许大写字母。
Type
TUppercaseEdit = class (TEdit)
protected
FOnChange : TNotifyEvent;
procedure Change; override;
function GetUppercase : string;
procedure SetUppercase( Value : string );
public
property Text : string read GetUppercase write SetUppercase;
end;
现在你可以创建一个用这个编辑框来代替标准编辑框的抽象工厂类的子类。
function TUppercaseFactory.MakeEdit( aOwner : TWinControl;vText : string;vTop : Integer;vLeft : Integer ): TEdit;
var
TempEdit : TUppercaseEdit;
begin
TempEdit := TUppercaseEdit.Create( aOwner );
TempEdit.Parent := aOwner;
TempEdit.Text := vText;
TempEdit.Top := vTop;
TempEdit.Left := vLeft;
MakeEdit := TempEdit;
end;
就像这样,你并不需要对PanelMaker.CreatePanel方法有任何改动。
ModelMaker 6.0 专门为delphi设计的建模工具,可以直接生成delphi代码。3.44M
http://www.24suns.com/freedelphi/newblack/vcl/downloads/Tools/ModelMaker.v6.0.WinAll.Retail.Incl.KeyFileMaker.rar
各位实在不好意思,我用IDA和w32dsm去反编译MIDEXD6.dll却不行,用trw却不能
直接跟踪dll文件的运行。好象该文件对反编译进行了处理。
目前只有一个方法就是过期后照样使用,如果过期后,在启动delphi6时
先将下面一写到一个reg文件中,导入注册表
REGEDIT4
[HKEY_CURRENT_USER\Software\ModelMaker\MideX]
[HKEY_CURRENT_USER\Software\ModelMaker\MideX\1.0]
[HKEY_CURRENT_USER\Software\ModelMaker\MideX\1.0\Options]
"SaveDelay"="50"
[HKEY_CURRENT_USER\Software\Borland\Delphi\6.0\Experts]
"MideX"="d:\\ModelMaker\\MideX\\1.0\\MIDEXD6.dll"
注意安装路径为你安装时的路径
然后将系统日期改为你安装时的日期后,启动delphi就可以使用了