重温工厂模式、装饰(Decorator)模式、组合(component)模式
工厂模式分三种,简单工厂(也叫静态工厂)模式、工厂方法(也叫动态工厂)模式、抽象工厂模式。
虽然看了许多设计模式的资料,也问了不少同行的朋友,但对工厂模式的理解感觉不够,今天又带着几个问题重温了一下:
1、为什么要使用工厂模式?
2、它能带来什么样的好处?
3、什么情况下适合作用工厂模式?
在网上搜索一下,发现有许多使用工厂模式的例子,但我觉得绝对多数都是在滥用,并没有真正理解工厂模式的设计用意,最后还是从《JAVA与模式》这本书找到比较满意的答案:
1、使用的工厂模式的原因是:
工厂模式将产品的创建和产品的使用隔离,当产品种族比较复杂,层次比较多,工厂起到了对产品统一管理、调配的作用。
2、使用工厂模式的好处有:
a.将产品的创建和使用隔离控制
b.统一管理产品,更具灵活性
c.允许动态的增加产品种类,而不需要修改原来的代码
3、当系统需要把产品的创建和使用分开,或者需要对产品的创建过程进行统一管理、调配,工厂模式是个很好的选择,并不是一提到产品的创建就想用工厂模式,否则就是滥用。
设计模式–装饰(Decorator)模式
这段时间,因为项目要评审,忙着准备资料,有好几天没到这里了。有一个模式我一直想好好的写,但总找不到从哪下手,可能是今天忙完了事情,突然想起一个例子可以实现,于是赶紧跑到这里写了下来,和大家分享一下。
装饰模式
意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵
活。
适用性
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
应用案例:
using System;
namespace OOD.Decorator
{
//装饰模式实例应用
//这里以一个宠物店为例子说明
//作者:盛天 时间:2006-06-15
/// 抽象产品
public interface IPet
{
string Name{get;set;} //宠物的名字
void Play(); //宠物玩耍
}
//小猫
public class Cat : IPet
{
#region IPet 成员
protected string name;
public string Name
{
get{return name;}
set{name = value;}
}
public Cat (string strName)
{
Name = strName;
}
public void Play()
{
System.Console.WriteLine(”A cat is playing a ball”);
System.Console.WriteLine(”It’s name is ” + Name);
}
#endregion
}
//小狗
public class Dog : IPet
{
#region IPet 成员
protected string name;
public string Name
{
get{return name;}
set{name = value;}
}
public Dog (string strName)
{
Name = strName;
}
public void Play()
{
System.Console.WriteLine(”A dog is playing a bone”);
System.Console.WriteLine(”It’s name is ” + Name);
}
#endregion
}
//新开一家宠物店,专为小宠物们服务
public interface IPetShop
{
IPet pet{get;set;}
void Service();
}
//宠物们玩累了,这里有一个专供宠物洗澡的地方
public class BathHouse : IPetShop
{
protected IPet ppet;
public IPet pet
{
get{return ppet;}
set{ppet = value;}
}
//好了,开始把他们放进来洗白白了
public void Service()
{
System.Console.WriteLine(”Now ” + pet.Name + ” is fatigued,take a bath for it”);
}
}
//宠物们玩累了,这里有一个专供宠物喂食的地方
public class FeedHouse : IPetShop
{
protected IPet ppet;
public IPet pet
{
get{return ppet;}
set{ppet = value;}
}
//噢,小可爱们,来吃吧
public void Service()
{
System.Console.WriteLine(”Now ” + pet.Name + ” is fatigued,The time is to feed.”);
}
}
//调用程序
public class Main
{
public Main()
{
//一只叫做MiMi的小猫
Cat mimi = new Cat(”MiMi”);
//还有一只叫做BenBen的小狗
Dog benben = new Dog(”BenBen”);
//它们在玩耍
mimi.Play();
benben.Play();
//一小时后
System.Console.WriteLine(”One hour later”);
//这时mimi饿了,准备去吃东西,去哪吃呢?那还用问,当然是新开的宠物店, 听说那里有许多好吃的东西
IPetShop petshop;
petshop = new FeedHouse();
petshop.pet = mimi; //MiMi进来食堂(Feedhouse)啦
petshop.Service(); //食堂为MiMi准备了好吃的东西
//另外一边,BenBen玩耍是满头大汗,这么热的天,得去洗个澡。
petshop = new BathHouse();
petshop.pet = benben; //BenBen来到了澡堂(BathHouse)
petshop.Service(); //澡堂的服务员给BenBen洗了个澡,现在舒服极了。
petshop = null;
mimi = null;
benben = null;
}
}
}
结语:装饰模式最大的特点是动态的改变对象的功能,如上例中,创建完一只宠物猫后,我们可以根据实际情况的不同,让它选择拥有洗澡或是吃东西的功能,装饰模式在实际应用中是非常实用的。
设计模式–组合(component)模式
将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
意图
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。
适用性
你想表示对象的部分-整体层次结构。
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
案例应用
下面是一个WEB的树型导航菜单的实例
using System;
namespace OOD.Tree
{
public interface INode
{
string Name
{
get;
set;
}
string Link
{
get;
set;
}
void Add(INode n);
void Remove(INode n);
void Display();
}
public class Leaf : INode
{
protected string name;
protected string link;
public Leaf(string n,string l)
{
this.Name = n;
this.Link = l;
}
#region INode 成员
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public string Link
{
get
{
return link;
}
set
{
link = value;
}
}
public void Add(INode n)
{
Console.WriteLine(”Leaf Con’t Add node”);
}
public void Remove(INode n)
{
Console.WriteLine(”Leaf Con’t Remove node”);
}
public void Display()
{
//这里使用ASP.NET的Response对象输出结果
System.Web.HttpContext.Current.Response.Write(”<li><a href=” + this.Link + “>” + this.Name + “</a></li>\n”);
}
#endregion
}
public class Folder : INode
{
protected string name;
protected string link;
protected System.Collections.ArrayList NodeList = new System.Collections.ArrayList();
public Folder(string n)
{
this.Name = n;
this.Link = “”;
}
#region INode 成员
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public string Link
{
get
{
return link;
}
set
{
link = value;
}
}
public void Add(INode n)
{
NodeList.Add(n);
}
public void Remove(INode n)
{
NodeList.Remove(n);
}
public void Display()
{
////这里使用ASP.NET的Response对象输出结果
System.Web.HttpContext.Current.Response.Write(”<li>” + name + “</li>\n”);
System.Web.HttpContext.Current.Response.Write(”<ul>\n”);
foreach(INode node in NodeList)
{
node.Display();
}
System.Web.HttpContext.Current.Response.Write(”</ul>\n”);
}
#endregion
}
}
客户端调用代码:
Response.Clear();
OOD.Tree.Folder root = new OOD.Tree.Folder(”管理中心”);
OOD.Tree.Folder member = new OOD.Tree.Folder(”会员管理”);
member.Add(new OOD.Tree.Leaf(”修改个人资料”,”01.htm”));
member.Add(new OOD.Tree.Leaf(”收短信息”,”02.htm”));
root.Add(member);
OOD.Tree.Folder product = new OOD.Tree.Folder(”产品管理”);
product.Add(new OOD.Tree.Leaf(”产品分类”,”11.htm”));
product.Add(new OOD.Tree.Leaf(”产品列表”,”12.htm”));
product.Add(new OOD.Tree.Leaf(”订单查询”,”13.htm”));
root.Add(product);
root.Add(new OOD.Tree.Leaf(”退出系统”,”00.htm”));
root.Display();
输出结果:
如果结合javascript就可以做出一个漂亮的树型导航菜单,有兴趣的朋友可以自己动手试试。
设计模式–抽象工厂模式
我们都知道,软件设计一般遵守“开闭原则”,意思是说我们设计出来的程序模块,在功能扩展性方面是开放的,可以随时进行扩展;而在程序模块的修改性上是关闭的,不允许修改原来的程序模块。经验告诉我们,修改原来的一些程序模块,往往会扯及其他模块,这是非常危险的,在一个复杂的系统中,你很可能不知道有多少个模块和将要修改的模块是关系的。但有些时候,我们不得不修改原来的程序设计,在这里我们提供了一个解决问题的办法,就是今天要说的抽象工厂设计模式。
适用性
一个系统要独立于它的产品的创建、组合和表示时。
一个系统要由多个产品系列中的一个来配置时。
当你要强调一系列相关的产品对象的设计以便进行联合使用时。
当你提供一个产品类库,而只想显示它们的接口而不是实现时。
案例应用
做电子商务网站经验遇到这样的问题,刚开始的时候只考虑了有两种会员:普通会员和VIP会员,随着网站的发展,需要添加更多种类型的会员。为了不修改原有的代码,我们使用抽象工厂设计模式,代码如下:
// AbstractFactory.cs //
// 我们先定义两种会员:普通会员和VIP会员 //
//////////////////////////////////////////////////
using System;
namespace OOD.AbstractFactory
{
/// <summary>
/// 抽象会员接口
/// </summary>
public interface IMember
{
int UserID
{
get;
set;
}
string UserName
{
get;
set;
}
string UserPWD
{
get;
set;
}
string UserRight
{
get;
set;
}
bool Login();
}
/// <summary>
/// 抽象工厂接口
/// </summary>
public interface IMemberFactory
{
IMember CreateMember(string username,string userpwd);
}
/// <summary>
/// 具体会员:普通会员
/// </summary>
public class NormalMember : IMember
{
private int userid;
private string username;
private string userpwd;
private string userright;
#region IMember 成员
public int UserID
{
get
{
// TODO: 添加 NormalMember.UserID getter 实现
return userid;
}
set
{
// TODO: 添加 NormalMember.UserID setter 实现
userid = value;
}
}
public string UserName
{
get
{
return username;
}
set
{
username = value;
}
}
public string UserPWD
{
get
{
return userpwd;
}
set
{
userpwd = value;
}
}
public string UserRight
{
get
{
return userright;
}
set
{
userright = value;
}
}
public NormalMember()
{
;
}
public bool Login()
{
//根据数据库检查UserName和UserPWD,如果身份正确,则保存UserID和UserRight
UserID = 01;
UserRight = “拥有普通会员权限”;
return true;
//如果不正确返回false;
//return false;
}
#endregion
}
/// <summary>
/// 具体会员:VIP会员
/// </summary>
public class VIPMember : IMember
{
private int userid;
private string username;
private string userpwd;
private string userright;
#region IMember 成员
public int UserID
{
get
{
// TODO: 添加 NormalMember.UserID getter 实现
return userid;
}
set
{
// TODO: 添加 NormalMember.UserID setter 实现
userid = value;
}
}
public string UserName
{
get
{
return username;
}
set
{
username = value;
}
}
public string UserPWD
{
get
{
return userpwd;
}
set
{
userpwd = value;
}
}
public string UserRight
{
get
{
return userright;
}
set
{
userright = value;
}
}
public VIPMember()
{
;
}
public bool Login()
{
//根据数据库检查UserName和UserPWD,如果身份正确,则保存UserID和UserRight
UserID = 02;
UserRight = “拥有VIP会员权限”;
return true;
//如果不正确返回false;
//return false;
}
#endregion
}
/// <summary>
/// 具体工厂:普通会员生产工厂
/// </summary>
public class NormalMemberFactory : IMemberFactory
{
#region IMemberFactory 成员
public IMember CreateMember(string username,string userpwd)
{
NormalMember objMember = new NormalMember();
objMember.UserName = username;
objMember.UserPWD = userpwd;
objMember.Login();
return objMember;
}
#endregion
}
/// <summary>
/// 具体工厂:VIP会员生产工厂
/// </summary>
public class VIPMemberFactory : IMemberFactory
{
#region IMemberFactory 成员
public IMember CreateMember(string username,string userpwd)
{
VIPMember objMember = new VIPMember();
objMember.UserName = username;
objMember.UserPWD = userpwd;
objMember.Login();
return objMember;
}
#endregion
}
}
客户端调用代码:
Response.Clear();
IMember objMember = new NormalMemberFactory().CreateMember(”",”");
Response.Write(”<P>已经创建了一个普通会员,权限为:” + objMember.UserRight);
IMember objMember = new VIPMemberFactory().CreateMember(”",”");
Response.Write(”<P>已经创建了一个VIP会员,权限为:” + objMember.UserRight);
执行结果如下:
已经创建了一个普通会员,权限为:拥有普通会员权限
已经创建了一个VIP会员,权限为:拥有VIP会员权限
这时我们如果需要添加一种会员:“外星人会员”,要求不能修改原代码,于是我们另外创建了一个文件:newMember.cs
using System;
namespace OOD.AbstractFactory
{
/// <summary>
/// 增加一具体会员:外星人会员
/// </summary>
public class ExtraMember : IMember
{
private int userid;
private string username;
private string userpwd;
private string userright;
#region IMember 成员
public int UserID
{
get
{
// TODO: 添加 NormalMember.UserID getter 实现
return userid;
}
set
{
// TODO: 添加 NormalMember.UserID setter 实现
userid = value;
}
}
public string UserName
{
get
{
return username;
}
set
{
username = value;
}
}
public string UserPWD
{
get
{
return userpwd;
}
set
{
userpwd = value;
}
}
public string UserRight
{
get
{
return userright;
}
set
{
userright = value;
}
}
public ExtraMember()
{
;
}
public bool Login()
{
//根据数据库检查UserName和UserPWD,如果身份正确,则保存UserID和UserRight
UserID = 03;
UserRight = “在地球观光的权限”;
return true;
//如果不正确返回false;
//return false;
}
#endregion
}
/// <summary>
/// 具体工厂:外星人会员生产工厂
/// </summary>
public class ExtraMemberFactory : IMemberFactory
{
#region IMemberFactory 成员
public IMember CreateMember(string username,string userpwd)
{
ExtraMember objMember = new ExtraMember();
objMember.UserName = username;
objMember.UserPWD = userpwd;
objMember.Login();
return objMember;
}
#endregion
}
}
调用新的会员方法是这样:
IMember objMember = new ExtraMemberFactory().CreateMember(”",”");
Response.Clear();
Response.Write(”<P>已经创建了一个外星人会员,权限为:” + objMember.UserRight);
运行结果:
已经创建了一个外星人会员,权限为:在地球观光的权限
设计模式–单件模式
作用
保证一个类只有一个实例,并提供一个访问它的全局访问点。
适合于
当一个类只能有一个实例,且客户可以从一个众所周知的访问点进行访问;
当这个唯一的实例应该是可以通过子类化可以扩展的,并且客户无需要修改代码就能使用这个已扩展的实例时。
案例应用
很多情况下,我们需要一个Singleton窗体,比如,任务管理器就可以是windows应用系统中的一个Singleton,于是我们这样做:在窗体类的实现中添加一个静态指向单件的成员,提供一个静态的CreateInstance方法,当第一次调用此方法时创建单件。另外还要处理Closing事件,以使点击窗体右上角的“×”时,隐藏窗体。每次需要实现一个单件窗体时都要这么做,于是我决定写一个基类ISingletonDisplayer,一个窗体不用作任何改变,只需要将原来的基类Form改为ISingletonDisplayer就变成一个Singleton窗体,这就很方便了。
public class ISingletonDisplayer : Form //从Form继承
{
private static ISingletonDisplayer singleton = null ;
private static bool toClean = false ;
protected ISingletonDisplayer()
{
this.Closing += new System.ComponentModel.CancelEventHandler(ISingletonDisplayer_Closing);
}
#region static for singleton
#region GetSingleton
public static ISingletonDisplayer GetSingleton()
{
if(ISingletonDisplayer.singleton != null)
{
ISingletonDisplayer.singleton.Visible = true ;
}
return ISingletonDisplayer.singleton ;
}
#endregion
#region ReCreateSingleton
//formOnUI存在是因为窗体只能在主线程(UI线程)中创建。
public static void ReCreateSingleton(Type targetType ,object[] args , Form formOnUI)
{
Type supType = typeof(ISingletonDisplayer) ;
if(! supType.IsAssignableFrom(targetType))
{
throw new Exception(”Target type is not derived from ISingletonDisplayer !”) ;
}
if(formOnUI.InvokeRequired)
{
object[] paras = {targetType ,args ,formOnUI} ;
formOnUI.Invoke(new CBackCreateForm(ISingletonDisplayer.ReCreateSingleton) ,paras) ;
}
else
{
try
{
ISingletonDisplayer.toClean = false ;
ISingletonDisplayer.singleton = (ISingletonDisplayer)Activator.CreateInstance(targetType ,args) ;
ISingletonDisplayer.singleton.Visible = false ;
}
catch(Exception ee)
{
throw ee ;
}
}
}
#endregion
#region DestroySingleton
public static void DestroySingleton()
{
ISingletonDisplayer.toClean = true ;
if(ISingletonDisplayer.singleton != null)
{
ISingletonDisplayer.singleton.Close() ;
ISingletonDisplayer.singleton = null ;
}
}
#endregion
#endregion
#region ISingletonDisplayer_Closing
private void ISingletonDisplayer_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if(! ISingletonDisplayer.toClean)
{
this.Visible = false ;
e.Cancel = true ;
return ;
}
}
#endregion
}
internal delegate void CBackCreateForm(Type targetType ,object[] args , Form formOnUI) ;
* 使用方法:
* 如果一个Displayer需要以Singleton模式呈现,那么可以先以常规的方式设计Displayer,设计过程中不需要涉及任何与单件
* 相关的东西。设计完后,只要将Displayer的基类由Form改为ISingletonDisplayer即可。接下来即可以单件的模式使用Displayer了。
*
* 如:
* public class AppServersInfoForm2 : ISingletonDisplayer{}
* object[] args = {obj} ;
* ISingletonDisplayer.ReCreateSingleton(typeof(AppServersInfoForm2) ,args ,formOnUI) ;
这样每个单件都有自己的静态实例,就不会相互干扰了。
)告知,即刻删除。