2009年10月6日星期二

Abstract Factory 抽象工厂设计模式 (兼谈与工厂方法 Factory Method的区别)

抽象工厂和工厂方法就像食品厂和食品生产线的区别,抽象工厂可以生产多个抽象产品,而工厂方法只能生产一个。在工厂方法的模式中,只要重写子类中的工厂方法就可以得到不同的产品;在抽象工厂中,改变工厂可以得到一套产品。

套装由同颜色的上衣和裤子组成,假设白色套装工厂生产白色上衣和白色裤子,黑色套装工厂生产黑色上衣和黑色裤子。在工厂内部生产同颜色的上衣和裤子,想制作白色套装,只需要创建一个白色套装工厂,想制作黑色套装,只需要创建一个黑色套装工厂,下面用代码实现。


public static void Main()
{
//白色套装工厂
AbstractSuiteFactory whiteFactory = new ConcreteWhiteSuiteFactory();
Client client1 = new Client(whiteFactory);
client1.Run();
//黑色套装工厂
AbstractSuiteFactory blackFactory = new ConcreteBlackSuiteFactory();
Client client2 = new Client(blackFactory);
client2.Run();
Console.ReadKey();
}
}
//抽象套装工厂
abstract class AbstractSuiteFactory
{
//上衣
public abstract AbstractCoat CreateCoat();
//裤子
public abstract AbstractTrouses CreateTrouses();
}
//具体的,白色套装工厂
class ConcreteWhiteSuiteFactory : AbstractSuiteFactory

{
//制作白色上衣
public override AbstractCoat CreateCoat()
{
return new WhiteCoat();
}
//制作白色裤子
public override AbstractTrouses CreateTrouses()
{
return new WhiteTrouses();
}
}
//黑色套装工厂
class ConcreteBlackSuiteFactory : AbstractSuiteFactory
{
//制作黑色上衣
public override AbstractCoat CreateCoat()
{
return new BlackCoat();
}
//制作黑色裤子
public override AbstractTrouses CreateTrouses()
{
return new BlackTrouses();
}
}
//抽象上衣
abstract class AbstractCoat
{
}
//抽象裤子
abstract class AbstractTrouses
{ //将上衣和裤子搭配成一套
public abstract void Interact(AbstractCoat a);
}
//具体的白色上衣
class WhiteCoat : AbstractCoat
{
}
//具体的白色裤子
class WhiteTrouses : AbstractTrouses
//搭配上衣和裤子
{ public override void Interact(AbstractCoat a)
{
Console.WriteLine(this.GetType().Name +" interacts with " + a.GetType().Name);
}
}
//具体的黑色上衣
class BlackCoat : AbstractCoat
{
}
//具体的黑色裤子
class BlackTrouses : AbstractTrouses
{
//搭配上衣和裤子
public override void Interact(AbstractCoat a)
{
Console.WriteLine(this.GetType().Name + " interacts with " + a.GetType().Name);
}
}
class Client //在这个类中,其实又用到了工厂方法。
{
//客户类:构造抽象工厂,生产抽象产品。实例化在具体应用时。
private AbstractCoat _abstractCoat;

private AbstractTrouses _abstractTrouses;
// Constructor

public Client(AbstractSuiteFactory factory)
{
_abstractTrouses = factory.CreateTrouses();
_abstractCoat = factory.CreateCoat();
}
public void Run()
{
_abstractTrouses.Interact(_abstractCoat);
}

执行结果:
WhiteTrouses interacts with WhiteCoat
BlackTrouses interacts with BlackCoat

2009年10月5日星期一

Factory Method 工厂方法设计模式

在模板方法的设计模式中,可以制作做中国菜的步骤和顺序的模板类,只要按照这个模板生产子类,并重写模板中的方法,就可以做出合格的中国菜,但这种模板的方式只能生产一种菜,比如中国菜。如果想生产法国菜,因为生产的步骤和方法都不同,所以只能修改模板类,这是我们所不希望的。换句话说,我们希望在模板中不具体规定做那种菜,而是在子类中指定菜的种类。这就需要工厂方法的设计模式。
在下面的例程中,先定义一个抽象的Dishes类,然后具体实现它的2个子类,ChineseDishes和FranceDishes,在子类内,可以重写getDishes,规定做每种菜的手续和顺序。接着,定义一个抽象类DishFactory,它内部定义了一个抽象的方法factoryMethod,返回一个抽象的菜,重写它可以得到具体的菜。然后实现了2个具体的工厂,生产中国菜的工厂,重写了factoryMethod,返回一个中国菜,生产法国菜的工厂,重写factoryMethod,返回一个法国菜。
在具体的应用中,想做中国菜的时候,制作一个中国菜工厂并调用makeDishes()方法,想做法国菜的时候,制作一个法国菜的工厂并调用makeDishes()。

abstract class Dishes
{
public abstract string getDishes();
}

class ChineseDishes : Dishes
{
public override string getDishes()
{
return "Step1 -> Step2 -> Step3 --> Chinese Dishes.\n";
}
}
class FranceDishes : Dishes
{
public override string getDishes()
{
return "StepA -> StepB -> StepC --> France Dishes.\n";
}
}

abstract class DishFactory
{
public abstract Dishes factoryMethod();
public void makeDishes()
{
Dishes dishes = this.factoryMethod();
Console.Write(dishes.getDishes());
}
}

class ChineseDishFactory : DishFactory
{
public override Dishes factoryMethod()
{
return new ChineseDishes();
}
}
class FranceDishFactory : DishFactory
{
public override Dishes factoryMethod()
{
return new FranceDishes();
}
}

public static void Main()
{
DishFactory cdf = new ChineseDishFactory();
cdf.makeDishes();

DishFactory fdf = new FranceDishFactory();
fdf.makeDishes();
}

输出结果:
Step1 -> Step2 -> Step3 --> Chinese Dishes.
StepA -> StepB -> StepC --> France Dishes.

抽象类(abstract class),接口(interface),抽象方法(abstract method),虚拟方法(virtual method)的异同

抽象类(abstract class)是一种特殊的类,它不能被实例化只能被继承,换句话说他只能做其他类的父类。主要目的是为了让所有的子类有共同的父类。抽象类中可以有抽象方法(abstract method),虚拟方法(virtual method) 和普通的方法。抽象方法不能有Body,即它只能在子类中用用override重写。虚拟方法有Body,根据需要,可以在子类总用override重写,也可以不重写而直接用父类中的Body.
接口(interface)不是类,只是一个用interface关键字定义的实体(entity),所以不可以实例化。接口内仅仅定义了一些没有函数体(body)的方法名和参数类型,这一点类似于抽象类中的抽象方法,所以接口有点类似于一个只包含抽象方法的抽象类。接口中的方法都必须是公开的。接口和虚拟类的一个主要的区别是子类可以继承多个接口但是只能有一个抽象的父类。
抽象类和子类之间的本质是相同的,所以轿车,卡车,巴士可以有共同的抽象类机动车。但是人,轿车可以有共同的接口IMovable interface。抽象类中可以有属性字段或者常量,接口中只能有方法名(规定了名称和参数的接口)。如果向接口中增加一个新的方法,需要跟踪所有的实现该接口的类,在类中重写这个新增加的方法。如果向抽象类中增加一个新的方法,可以在抽象类中实现这个方法(virtual method或者 public method),那么所有的子类中都可以自动使用这个方法。
最后一点,抽象方法只能存在于抽象类中,虚拟方法(virtual method)可以存在于抽象类中也可以在普通类中。在具体的应用中,如果不同的实现仅仅是有相同的方法,那么最好共同继承自一个接口;如果属于同一类事物,具有相同的属性则他们应该共同继承自一个抽象类。

2009年10月3日星期六

Template Method模板方法设计模式

生产或者制作什么东西,有时候需要严格的手续和步骤,比如做一般的中国菜,需要放油,放佐料,放主料,加热,出锅。只要制作一个做菜的模板(接口),具体不管做什么菜,只要按照这个模板做(继承接口),就能做出标准的中国菜。没有这个模板,就很难保证做什么菜都能按照这个步骤并且一步不少。


abstract class TemplateCooking
{
public virtual void AddOil() //虚拟函数,如果想放花生油以外的油,可以改写。否则可以不写。
{ Console.Write("放入花生油\n"); }
public abstract void AddCondiments(); //放佐料,抽象函数,必须重写。
public abstract void AddBase(); //放入主料,抽象函数,必须重写。
public virtual void HeatUp() //加热,虚拟函数,如果用其他方式加热可以改写。
{ Console.Write("用煤气灶加热\n"); }
public virtual void OutPut() ////出锅,如果不是放到盘子里,可以改写。
{ Console.Write("盛到盘子中\n"); }
public void Cook() //保证正确的顺序。
{
AddOil(); AddCondiments(); AddBase(); HeatUp(); OutPut();
}
}

//下面制作一个炒青椒肉丝的类。

class CookPepper: TemplateCooking
{
public override void AddOil() { Console.Write("放色拉油\n"); } //改用色拉油。
public override void AddCondiments() { Console.Write("放入花椒\n"); } //这个是必须实现的接口
public override void AddBase() { Console.Write("放入肉丝,青椒\n"); } //这个是必须实现的接口
//其他的加热,出锅 不改变。
}
public static void Main()
{
CookPepper cookPepper = new CookPepper();
cookPepper.Cook(); //保证按部就班准确的做好菜。
}

执行结果:
放色拉油
放入花椒
放入肉丝,青椒
用煤气灶加热
盛到盘子中

所以任何继承自TemplateCooking的类,肯定都能准确的做出中国菜来,不会少步骤也不会乱了顺序,这就是模板设计模式的好处。

Proxy 代理设计模式

本来应该是A干的活,A不自己做,交给B去做。但是在外界(Client)看来都是A做的,B就是A的代理,这就是代理(Proxy)的设计模式。
现实中比如A电脑厂商生产的电脑,售后的维修服务交给B服务中心去做,B以A的名义去给客户修电脑,在用户看来和A直接维修一样。键盘坏了,鼠标坏了,B可以直接给用户修理,但是如果主板坏了,B就要送给A(厂家)去修理,修理好了B再给客户,在客户看来和B自己修理好一样。换句话说,客户只管电脑坏了拿送给B即可,具体谁修理不用关心。
类示例如下

class PcMakerA //电脑厂家
{
repairMainBoard();{}//修理主板
repairMonitor();{}//修理显示器
}

class ServiceCenterB //售后服务代理
{
repairMouse();{} //修鼠标
repariKeyboard();{}//修键盘
repairMainBoard(){}//修主板(自己修不了,让厂家去修:我只是代理)
{
pcMakerA = New PcMakerA();
pcMakerA.repairMainBoard();
}
repairMonitor()//修显示器(自己修不了,让厂家去修:我只是代理)
{
pcMakerA = New PcMakerA();
pcMakerA.repairMonitor();
}
}
class Client //顾客
{
serviceCenter = new ServiceCenterB();
serviceCenter.repairMainBoard();//给我修主板
serviceCenter.repariKeyboard();//给我修键盘
}

2009年10月1日星期四

State 状态设计模式

类(CLASS)在一般情况下是用来表示事物的,比如File,Car等类。如果用来表示状态,就要用到状态设计模式。现实中的事物在不同的状态下会表现不同的动作。例如女孩Girl这个对象,在心情好的时候,你请她看电影,他会说:好呀,在心情不好的时候,他会对你说:不去。所以Gilr.Invite()这个动作,会随着状态的不同做不同的反应。这个情况用下面的例子表示:


public class Girl
{
//代表心情好
private static int STATE_ORDINARY = 0;
//代表心情坏
private static int STATE_IN_BAD_MOOD = 1;
//心情状态,初期为-1
private int state = -1;
//改变心情状态
public void changeState(int state)
{
this.state = state;
}
//对邀请看电影的后的反应动作。
public string invite()
{
if (state == STATE_ORDINARY)
{
return "好呀";
} else if (state == STATE_IN_BAD_MOOD)
{
return "讨厌";
} else
{
return "...";
}
}


有一天女孩喜欢上你了,状态的改变,反应又不一样了。心情好的时候,会说:太高兴了。不好的时候会说:我们去喝咖啡吧。如果用上面的代码,就要修改invite动作的if else ,修改类内部代码显然是我们不希望的。这是为什么要引进状态设计模式的原因。

//状态设计模式下的Girl类

public class StatePatternGirl
{
private State state = null;
private void changeState(State state)
{
this.state = state;
}
public string invite()
{
return this.state.invite();
}
}
//状态的接口,动作在具体状态类中实现。
interface State
{
public string invite();
}

class BadMoodState : State
{
public string invite()
{
return "去喝咖啡吧";
}
}

class OrdinaryState : State
{
public string morningGreet()
{
return "好高兴呀";
}
}

这样,既可以修改状态的动作,又可以保持StatePatternGirl 这个类的一致,这就是状态设计模式。

C#中的回调函数安装到C语言DLL中

例如,C语言编译的DLL中有个定时器,每过一定的时间产生一个TimeChange,但是当TimeChange发生后的具体处理,要在C#中实现。即将C#中的具体回调函数,安装到C编译的DLL中的TimeChange中。看下面的例子。

///pure C DLL
#include "windows.h"
#define DLLEXPORT __declspec(dllexport)

extern "C"
{
DLLEXPORT int __stdcall timeChange(void (*onTimeChange)())
{
return onTimeChange();
}
}

///C#
class DLL
{
[DllImport"ctimechange.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int timeChange(DOnTimeChange cb);

public delegate int DOnTimeChange();

public static void main()
{
DOnTimeChange cb = new DOnTimeChange(OnTimeChange);
timeChange(cb);
}

public static void OnTimeChange()
{
console.write("time changed");
}
}