关于连锁分销管理系统设计模式的讨论 ( 积分: 300 )

K

kino25

Unregistered / Unconfirmed
GUEST, unregistred user!
现在我想设计一个连锁分销管理系统(可以适应多个行业)
开发工具要求:
1、开发工具:Delphi
2、数据库:SQL2000
3、控件:采用成熟稳定的第三方控件(如EH)
需求:
1、总部/连锁分店/办事处,各分店可以独立核算。
2、采用C/S和B/S结合的模式。
3、三层架构,业务/界面分离,当开发另一个行业的产品时,能节省更多的工作量。
4、满足目前的网络条件(服务器端是ADSL或固定IP,客户端是ADSL或拨号上网),尽量减少客户的网络费用。
5、服务器采用SQL2000数据库,客户端采用ACCESS或别的模式,可以实时连接操作数据或离线操作,定时上传汇总。
请各位大富翁讨论设计模式,有意之士亦可共同开发。
cokolk@hotmail.com
 
现在我想设计一个连锁分销管理系统(可以适应多个行业)
开发工具要求:
1、开发工具:Delphi
2、数据库:SQL2000
3、控件:采用成熟稳定的第三方控件(如EH)
需求:
1、总部/连锁分店/办事处,各分店可以独立核算。
2、采用C/S和B/S结合的模式。
3、三层架构,业务/界面分离,当开发另一个行业的产品时,能节省更多的工作量。
4、满足目前的网络条件(服务器端是ADSL或固定IP,客户端是ADSL或拨号上网),尽量减少客户的网络费用。
5、服务器采用SQL2000数据库,客户端采用ACCESS或别的模式,可以实时连接操作数据或离线操作,定时上传汇总。
请各位大富翁讨论设计模式,有意之士亦可共同开发。
cokolk@hotmail.com
 
标题?: Delphi设计模式
关键字:
分类?: 个人专区
密级?: 公开
(评分: , 回复: 0, 阅读: 458) »»
本文在大富翁发表已将近一个月,作为理论性比较强的内容,反映比较热烈并被转载和收藏,由于我的 Blog 已开通,现将全文在 Blog 中重新发布并删除大富翁上的原贴 ,转载请注明出处。下面是正文:
大约半年前看了阎宏老师的《Java与模式》,里面的工厂模式篇基本上还看懂了点,当时用 Delphi 改写了其中几个例程,现在拿出来与大家分享,里面的注释是我对这种设计模式的理解,很片面的,如果有什么错误希望大家批评指正。
工厂模式中又分为简单工厂模式、工厂方法模式和抽象工厂模式 。这里给大家介绍的简单工厂模式是其中最简单的一种。如果大家支持的话我会继续贴出工厂方法模式和抽象工厂模式等后续篇,要看大家的反应程度哦!
学习设计模式要对面向对象的程序设计有一定的理解,特别是多态性 ,如果能看懂下面的例子就没问题了,呵呵!
//水果类,它是一个抽象产品
TFruit = Class(TObject)
...
end;

//苹果类,水果类的具体化
TApple = class(TFruit)
...
end;

function Factory(): TFruit;
var
f:TFruit;
begin
//精髓就是这条语句了,明明创建了TApple对象,
//却将他赋值给TFruit类型的变量
//其实这样做好处大大的,后面就体会到了
f:=TApple.Create();
result:=f;
end


在例程中我用到了接口 ,不明白得可以把它当成一个比抽象类还抽象的抽象类,说白了把它当成一个类就没错。 下面开始吧。。。。。。。
这是说明:
//我们用一个小果园来说明什么是简单工厂
//这个果园里有葡萄、苹果和草莓三种水果
//所有的水果都有生长、耕作和收获三个步骤
//果园的任务就是让我们得到葡萄、苹果和草莓这三种水果对象
//我们利用得到的对象可以完成水果生长、耕作和收获三个步骤
//果园就是我们所说的简单工厂(Factory)
//而葡萄、苹果和草莓这三种水果就是工厂里的产品 (Pruduct)
//完成产品的过程称之为外部使用者(Produce)
//使用简单工厂的好处是:
//1、充分利用了多态性
//不管你种什么,果园返回的对象并不是具体的葡萄、苹果或者草莓
//而是返回一个他们的抽象对象 -- 水果(IFruit)
//2、充分利用了封装性
//内部产品发生变化时外部使用者不会受到影响
//他的缺点是:
//如果增加了新的产品,就必须得修改工厂(Factory)

这是定义简单工厂的单元文件源代码:
//SimpleFactory.pas 定义简单工厂的单元文件
//代码如下==========
unit SimpleFactory;
interface
uses
SysUtils;
type
//水果类,它是一个抽象产品
//仅仅声明了所有对象共有的接口,并不实现他们
IFruit = interface(IInterface)
function Grow: string;
//生长
function Harvest: string;
//收获
function Plant: string;//耕作
end;

//葡萄类,水果类的具体化
TGrape = class(TInterfacedObject, IFruit)
function Grow: string;
function Harvest: string;
function Plant: string;
end;

//苹果类,水果类的具体化
TApple = class(TInterfacedObject, IFruit)
function Grow: string;
function Harvest: string;
function Plant: string;
end;

//草莓类,水果类的具体化
TStrawberry = class(TInterfacedObject, IFruit)
function Grow: string;
function Harvest: string;
function Plant: string;
end;

//果园类,它就是工厂类,负责给出三种水果的实例
TFruitGardener = class(TObject)
public
//1、注意 class 关键字,它定义工厂方法 Factory 是一个静态函数,可以直接使用
//2、注意返回值,他返回的是最抽象的产品 IFruit 水果类
//3、注意他有一个参数,来告诉工厂创建哪一种水果
class function Factory(whichFruit:string): IFruit;
end;

//声明一个异常,这不是重点
NoThisFruitException = class(Exception)
end;

implementation
{ ********** TGrape ********** }
function TGrape.Grow: string;
begin
result:='葡萄正在生长......';
end;

function TGrape.Harvest: string;
begin
result:='葡萄可以收获了......';
end;

function TGrape.Plant: string;
begin
result:='葡萄已经种好了......';
end;

{ ********** TApple ********** }
function TApple.Grow: string;
begin
result:='苹果正在生长......';
end;

function TApple.Harvest: string;
begin
result:='苹果可以收获了......';
end;

function TApple.Plant: string;
begin
result:='苹果已经种好了......';
end;

{ ********** TStrawberry ********** }
function TStrawberry.Grow: string;
begin
result:='草莓正在生长......';
end;

function TStrawberry.Harvest: string;
begin
result:='草莓可以收获了......';
end;

function TStrawberry.Plant: string;
begin
result:='草莓已经种好了......';
end;

{ ********** TFruitGardener ********** }
class function TFruitGardener.Factory(whichFruit:string): IFruit;
begin
//精髓就是这条语句了 result:= TApple.Create()
//不明白赶紧去复习复习什么是多态性
if(LowerCase(whichFruit)='apple')then
result:=TApple.Create()
else
if(LowerCase(whichFruit)='grape')then
result:=TGrape.Create()
else
if(LowerCase(whichFruit)='strawberry')then
result:=TStrawberry.Create()
else
Raise NoThisFruitException.Create('这种水果还没有被种植!');
end;

end.

窗体界面:
//MainForm.pas 窗体文件,这里说明怎样使用简单工厂
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,SimpleFactory, StdCtrls;
type
TForm1 = class(TForm)
RadioButton1: TRadioButton;
RadioButton2: TRadioButton;
RadioButton3: TRadioButton;
RadioButton4: TRadioButton;
procedure RadioButton1Click(Sender: TObject);
procedure RadioButton2Click(Sender: TObject);
procedure RadioButton3Click(Sender: TObject);
procedure RadioButton4Click(Sender: TObject);
public
procedure Produce(fruitName:string);
end;

var
Form1: TForm1;
implementation
{ ********** TForm1 ********** }
//这就是生产过程
//IFruit 类型的临时变量 f 自己知道种的是哪种水果,有趣吧
//想要什么尽管来种,果园大丰收啦!
procedure TForm1.Produce(fruitName:string);
var
f: IFruit;
begin
try
f:=TFruitGardener.Factory(fruitName);
ShowMessage(f.Plant());
ShowMessage(f.Grow());
ShowMessage(f.Harvest());
except
on e:NoThisFruitExceptiondo
Messagedlg(e.Message,mtInformation,[mbOK],0);
end;
end;

{$R *.dfm}

procedure TForm1.RadioButton1Click(Sender: TObject);
begin
Produce('apple');
end;

procedure TForm1.RadioButton2Click(Sender: TObject);
begin
Produce('grape');
end;

procedure TForm1.RadioButton3Click(Sender: TObject);
begin
Produce('strawberry');
end;

procedure TForm1.RadioButton4Click(Sender: TObject);
begin
Produce('other');
end;

end.


工厂模式的目的就是,把创建对象的责任和使用对象的责任分开,工厂负责统一创建具体产品(苹果、葡萄和草莓),然后再把这些产品转化为他们的抽象产品(水果)返回给外部使用者,作为使用者关心的仅仅是抽象产品预留的接口,而不关心他们是怎么创建的。这样,即使因为某些原因导致创建产品的过程发生变化,也不会影响到外部使用者,在一定程度上保证了程序的可维护性。
如果把具体产品类(TApple、TFrabe、TStrawberry)暴露到外部,如果内部的代码发生了变动,外部也会受到影响,工厂就失去了他的意义。

“开闭原则”:一个模块应该易于扩展,免于修改
问题:请考虑上一章的例子中,如果添加一个新的具体水果类“西瓜”需要做哪些工作。
本章完成以下内容:
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;
//=============================================================================

2、在工厂类和水果类单元文件中,修改小贩的工厂方法(这里是修改,已经违反了“开闭原则”)
//=============================================================================
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;
//=============================================================================


下面再看看在工厂方法模式中是怎么做的:
1、这一步和在简单工厂模式中做的一样,在工厂类和水果类单元文件中,引入一个新的西瓜类(这里是扩展,不会影响到已有的代码)
//=============================================================================
类_西瓜 = class(TInterfacedObject, 接口_水果)
function 提示():string;
function 被评价():string;
end;

{****** 类_西瓜 ******}
function 类_西瓜.提示():string;
begin
result:='刚上市的沙瓤大西瓜,2元钱一斤!';
end;

function 类_西瓜.被评价():string;
begin
result:='靠,被骗了,根本没熟!';
end;
//=============================================================================

2、区别就在这里了,在工厂类和水果类单元文件中,引入一个新的西瓜小贩类(这里是扩展,不会影响到已有的代码)
//=============================================================================
类_西瓜小贩 = class(TInterfacedObject, 接口_小贩)
function 工厂(): 接口_水果;
end;

{***** 类_西瓜小贩 *****}
function 类_西瓜小贩.工厂(): 接口_水果;
begin
result:=类_西瓜.Create()
end;
//=============================================================================

3、在窗体单元文件中,添加一个新的事件处理过程(这里是扩展,不会影响到已有的代码)
//=============================================================================
RadioButton4: TRadioButton;
procedure RadioButton4Click(Sender: TObject);
procedure TForm1.RadioButton4Click(Sender: TObject);
begin
交易(类_西瓜小贩.Create);
end;
//=============================================================================
 

Similar threads

S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
961
SUNSTONE的Delphi笔记
S
顶部