(声明:本系列所用的模式都来自GOF23中,本系列并不是讲23种经典设计模式,而是如何去使用这些模式)
前面的客户端程序中,遍历部门时,有个对部门还是人员的判断,然后按照不同的类型输出不同的结果,这样增加了客户端的复杂度,如果出现另一种情况,那么就要修改客户端,因为客户端程序大部分都不是出现在一个调用中,所以这样的修改十分可怕,如果输出有所变化,比如多加几个字,或者显示员工福利工资,那么就不得不修改客户代码所有出现的输出的地方.那么我们如何来封装这个变化呢?
1.分析:
我们的意图,我们要将Output这样的输出命令进行封装.使得他可以独立变化.要完成这样的设计,我们就需要将调用方与被调用方分离,将命令封装起来.
Gof23中的Command(命令):1)将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;2)对请求排队或记录请求日志,以及支持可撤消的操作。
我们在这里只用到他的第一个意图就可以完成我们的设计.第二个意图将在后续的文章中继续完善设计他.
2.类图:
代码:
Command:
Code
public interface ICommand
{
void Excute(); //接受命令者执行命令
}
public class OutputCommand :ICommand
{
private IComposite _pc; //维护一个命令的接受者
public OutputCommand(IComposite pc)
{
this._pc = pc;
}
public void Excute()
{
if(_pc.GetType()==typeof( PersonComposite)) //PC为部门时输出内容
{
Console.Write("部门" + ((PersonComposite)_pc).Name + "\r\n");
}
else //PC为人员的输出内容
{
Console.Write(((AbstractPerson)_pc).PersonName + "应得工资为" + ((AbstractPerson)_pc).GetShouldpaid().ToString() + "\r\n");
}
}
}
Invoker:
Code
class Invoker
{
public static void ExecuteCommand(ICommand command) //通用的方法,使得Comand的执行封装于此
{
command.Excute();
}
}
Invoker的作用是将调用具体的Command的知性方法进行封装,客户代码调用Invoker,从而减少了客户代码的修改.
客户代码:
Code
class Program
{
static void Main(string[] args)
{
Builder b = new Builder(); //实例化具体Builder
Director d = new Director(); //实例化Composite的创建
PersonComposite pc = d.GetPersonComposite(b); //用d对象的固定方法创建PersonComposite
Print(pc); //调用输出
Console.Read();
}
public static void Print(PersonComposite pc)
{
Listterator list = new Listterator(pc); //实例化一个遍历器,用来遍历Composite的子节点
while (list.Next()) //遍历下一个节点
{
IComposite com = list.Current();
ICommand cmd= new OutputCommand(com);
Invoker.ExecuteCommand(cmd); //将执行的输出封装起来
if (com is PersonComposite)//如果是部门,则递归
{
Print((PersonComposite)com);
}
}
}
}
大家可以看到我们要用什么样的Command,是由自己来初始化的,这样就可以通过配置文件,或者工厂方法来进行运行时的多态.这里就不再赘述.
执行结果:与上一篇相同,不再重复粘贴.
这样我们就将客户代码的复杂性和变化封装了起来,即时要改变输出内容或者整个命令,那么只要衍生一个ICommand的子类就可以了.客户代吗的变动降到了最低,如果才用工厂方法和配置来创建Icommand的子类,就更加松耦合,更加能够应对变化了.