“开闭原则”:一个模块应该易于扩展,免于修改
问题:请考虑上一章的例子中,如果添加一个新的具体水果类“西瓜”需要做哪些工作。
本章完成以下内容:
1、代码用支持中文的 Delphi 2005 编译并通过,并去除了其中一些无关紧要的地方,如异常处理等 ;
2、重新设计一个情景,分别用“简单工厂模式”和“工厂方法模式”两种方法实现,请体会其中的差别 ;
3、在情景中添加一个子类后,请体会“简单工厂模式”和“工厂方法模式”两种方法不同的处理方式;
4、如果不理解什么是接口、多态、静态函数等概念,这里不作解释,请看第一章或找相关资料;
5、本章的情景和上一章差不多,只是把工厂从“果园”变成了“水果小贩”;同样的三种水果:苹果、葡萄、草莓;每种水果都封装了两个逻辑,在和外部“交易”时会用到这两个逻辑。 最后,请重新回顾“开闭原则”
下面开始吧。。。。。。。
这里是简单工厂模式的实现方法,在这种模式中:
1、一个小贩要负责所有三种水果的交易,这对他来说是很大的挑战噢,不信您看!
2、顾客必须对水果的名字有一个准确地描述,这样水果才会卖给你,这很影响生意呀!
//简单工厂类和水果类单元文件 unit SimpleFactory; interface type 接口_水果 = interface(IInterface) function 提示():string; function 被评价():string; end; 类_苹果 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; 类_葡萄 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; 类_草莓 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; 工厂类_小贩 = class(TObject) public class function 工厂(水果名:string): 接口_水果; end; implementation {***** 类_苹果 *****} function 类_苹果.提示():string; begin result:='削了皮再吃!'; end; function 类_苹果.被评价():string; begin result:='恩,还不错,挺甜!'; end; {*****类_葡萄 *****} function 类_葡萄.提示():string; begin result:='别把核咽下去了!'; end; function 类_葡萄.被评价():string; begin result:='没有核呀???'; end; {***** 类_草莓 *****} function 类_草莓.提示():string; begin result:='别怪我没告诉你,很酸!'; end; function 类_草莓.被评价():string; begin result:='试试?哇,牙快酸掉了!'; end; {***** 工厂类_小贩 *****} class function 工厂类_小贩.工厂(水果名:string): 接口_水果; begin if(水果名='苹果')then result:=类_苹果.Create() else if(水果名='葡萄')then result:=类_葡萄.Create() else if(水果名='草莓')then result:=类_草莓.Create(); end; end. |
//窗体单元文件 unit MainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,SimpleFactory; type TForm1 = class(TForm) RadioButton1: TRadioButton; RadioButton2: TRadioButton; RadioButton3: TRadioButton; procedure RadioButton3Click(Sender: TObject); procedure RadioButton2Click(Sender: TObject); procedure RadioButton1Click(Sender: TObject); private procedure 交易(水果名:string); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.交易(水果名:string); var 我买的水果: 接口_水果; begin 我买的水果:=工厂类_小贩.工厂(水果名); ShowMessage(我买的水果.提示); ShowMessage(我买的水果.被评价); end; procedure TForm1.RadioButton1Click(Sender: TObject); begin 交易('苹果'); end; procedure TForm1.RadioButton2Click(Sender: TObject); begin 交易('葡萄'); end; procedure TForm1.RadioButton3Click(Sender: TObject); begin 交易('草莓'); end; end. |
这里是工厂方法模式的实现方法,在这种模式中
1、每一种水果都对应有一个小贩负责,这样他们做起生意来就轻松多了,呵呵!
2、顾客直接和小贩打交道,他知道您要什么,这样就不会因为说不清那讨厌的水果名字而吃不上说水果了。
//工厂类和水果类单元文件 unit FactoryMethod; interface type 接口_水果 = interface(IInterface) function 提示():string; function 被评价():string; end; 类_苹果 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; 类_葡萄 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; 类_草莓 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; 接口_小贩 = interface(IInterface) function 工厂(): 接口_水果; end; 类_苹果小贩 = class(TInterfacedObject, 接口_小贩) function 工厂(): 接口_水果; end; 类_葡萄小贩 = class(TInterfacedObject, 接口_小贩) function 工厂(): 接口_水果; end; 类_草莓小贩 = class(TInterfacedObject, 接口_小贩) function 工厂(): 接口_水果; end; implementation {****** 类_苹果 ******} function 类_苹果.提示():string; begin result:='削了皮再吃!'; end; function 类_苹果.被评价():string; begin result:='恩,还不错,挺甜!'; end; {****** 类_葡萄 ******} function 类_葡萄.提示():string; begin result:='别把核咽下去了!'; end; function 类_葡萄.被评价():string; begin result:='没有核呀???'; end; {****** 类_草莓 ******} function 类_草莓.提示():string; begin result:='别怪我没告诉你,很酸!'; end; function 类_草莓.被评价():string; begin result:='试试?哇,牙快酸掉了!'; end; {***** 类_苹果小贩 *****} function 类_苹果小贩.工厂(): 接口_水果; begin result:=类_苹果.Create() end; {***** 类_葡萄小贩 *****} function 类_葡萄小贩.工厂(): 接口_水果; begin result:=类_葡萄.Create() end; {***** 类_草莓小贩 *****} function 类_草莓小贩.工厂(): 接口_水果; begin result:=类_草莓.Create() end; end. |
//窗体单元文件 unit MainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,FactoryMethod; type TForm1 = class(TForm) RadioButton1: TRadioButton; RadioButton2: TRadioButton; RadioButton3: TRadioButton; procedure RadioButton3Click(Sender: TObject); procedure RadioButton2Click(Sender: TObject); procedure RadioButton1Click(Sender: TObject); private procedure 交易(小贩:接口_小贩); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.交易(小贩:接口_小贩); var 我买的水果:接口_水果; begin 我买的水果:=小贩.工厂(); ShowMessage(我买的水果.提示); ShowMessage(我买的水果.被评价); end; procedure TForm1.RadioButton1Click(Sender: TObject); begin 交易(类_苹果小贩.Create); end; procedure TForm1.RadioButton2Click(Sender: TObject); begin 交易(类_葡萄小贩.Create); end; procedure TForm1.RadioButton3Click(Sender: TObject); begin 交易(类_草莓小贩.Create); end; end. |
夏天来了,西瓜上市了;
在简单工厂模式中,由于只有一个小贩,为了引进西瓜他只好对自己的工厂进行了修改;
在工厂方法模式中,由于每个小贩负责一种水果,只需要再引进一个卖西瓜的小贩就行了,对其他小贩的销售不会造成影响 。
下面先看看在简单工厂模式中是怎么做的:
1、在工厂类和水果类单元文件中,引入一个新的西瓜类(这里是扩展,不会影响到已有的代码)
//============================================================================= 类_西瓜 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; {****** 类_西瓜 ******} function 类_西瓜.提示():string; begin result:='刚上市的沙瓤大西瓜,2元钱一斤!'; end; function 类_西瓜.被评价():string; begin result:='靠,被骗了,根本没熟!'; end; //============================================================================= |
下面再看看在工厂方法模式中是怎么做的:
1、这一步和在简单工厂模式中做的一样,在工厂类和水果类单元文件中,引入一个新的西瓜类(这里是扩展,不会影响到已有的代码)
//============================================================================= 类_西瓜 = class(TInterfacedObject, 接口_水果) function 提示():string; function 被评价():string; end; {****** 类_西瓜 ******} function 类_西瓜.提示():string; begin result:='刚上市的沙瓤大西瓜,2元钱一斤!'; end; function 类_西瓜.被评价():string; begin result:='靠,被骗了,根本没熟!'; end; //============================================================================= |
//============================================================================= 类_西瓜小贩 = class(TInterfacedObject, 接口_小贩) function 工厂(): 接口_水果; end; {***** 类_西瓜小贩 *****} function 类_西瓜小贩.工厂(): 接口_水果; begin result:=类_西瓜.Create() end; //============================================================================= |
//============================================================================= RadioButton4: TRadioButton; procedure RadioButton4Click(Sender: TObject); procedure TForm1.RadioButton4Click(Sender: TObject); begin 交易(类_西瓜小贩.Create); end; //============================================================================= |
//============================================================================= class function 工厂类_小贩.工厂(水果名:string): 接口_水果; begin if(水果名='苹果')then result:=类_苹果.Create() else if(水果名='葡萄')then result:=类_葡萄.Create() else if(水果名='草莓')then result:=类_草莓.Create() //请注意,下面这条语句是新加上去的,工厂被修改了!!!! else if(水果名='西瓜')then result:=类_西瓜.Create(); end; //============================================================================= |
3、在窗体单元文件中,添加一个新的事件处理过程(这里是扩展,不会影响到已有的代码)
//============================================================================= RadioButton4: TRadioButton; procedure RadioButton4Click(Sender: TObject); procedure TForm1.RadioButton4Click(Sender: TObject); begin 交易('西瓜'); end; //============================================================================= |