策略者模式
2010-05-13 21:03 三皮开发时 阅读(565) 评论(2) 编辑 收藏 举报1.起因
-
Joe上班的公司做了一套相当成功的模拟鸭子游戏,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫,系统的内部设计使用了标准的OO技术,设计了一个鸭子超类,并让各种鸭子继承此超类
-
代码是这样写的
-
-
2.改进(扩展)
-
公司竞争压力加剧,在头脑风暴会议之后,主管认为该是创新的时候了,他们需要展示一些“真正”让人印象深刻的东西来振奋人心,主管确定需要会飞的鸭子来将竞争者抛在脑后,joe拍胸脯告诉主管,我只需要一个星期就可以搞定,毕竟joe是一个oo程序员嘛~
-
-
joe说:我只需要在Duck类中加上fly()方法,然后所有鸭子都会继承Fly()就噢了,展示OO的才华哇~
-
第二天,主管在股东会议上,看到了一下展示,有很多“橡皮鸭子”在屏幕上飞来飞去,橡皮鸭也会飞? 主管对joe说你是在开玩笑么?你可能要开始逛逛Monster.com(美国最大的求职网)了...
-
Joe他体会到了一件事:当涉及“维护”时,为了“复用”(reuse)目的而使用继承、结局并不完美。
-
Joe后来想到:我可以把橡皮鸭类中的fly()方法覆盖掉,就像Quack()的做法一样...Joe拿到主管的备忘录,希望以后每六个月更新产品(至于更新的方法,他们还没想到)。Joe知道规格会常常改变,每当有新的鸭子子类出现,他就要被迫检查并可能需要覆盖fly()和quack()...这简直是无穷无尽的噩梦。由此Joe想到可以把fly()和quack()从超类中取出来,各放进一个接口中
-
-
这真是一个超笨的主意,你没发现这么一来重复的代码会变多吗?如果你认为覆盖几个方法就算是差劲,那么对于48个Duck子类都要稍微修改以下飞行的行为,你又怎么说?并非所有的子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式,虽然用接口能接口不会再有会飞的橡皮鸭,但是却造成了代码无法复用,这只能算是从一个噩梦跳进了另一个噩梦,甚至,在会飞的鸭子中,飞行的动作可能还有多种变化....
-
这种情况要遵循以下几个设计原则:
-
1.应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
-
2.找出针对接口编程,而不是针对实现编程。
-
3.多用组合,少用继承
-
-
3.如何解决
-
-
-
看明白了吧
-
IFly接口:
public interface IFly
{
/// <summary>
/// 定义鸭子fly的方法
/// </summary>
void Fly();
} -
IQuack接口:
-
public interface IQuack
{
void Quack();
} -
行为类不会飞的鸭子继承fly接口
class 不会飞的鸭子:IFly
{
public void Fly()
{
Console.WriteLine("I’m not fly!");
}
}
行为类会飞的鸭子继承fly接口
{
public void Fly()
{
Console.WriteLine("i‘m flying!");
}
}
-
同理,行为类呱呱叫鸭子类继承IQuack接口
class 呱呱叫的鸭子:IQuack
{
public void Quack()
{
Console.WriteLine("gua gua~");
}
}
行为类不会叫鸭子类继承IQuack接口
{
public void Quack()
{
Console.WriteLine("I‘m not Quack!");
}
}
行为类吱吱叫鸭子类继承IQuack接口
{
public void Quack()
{
Console.WriteLine("zhi~ zhi~");
}
}
Duck类!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
public IFly fly;
public IQuack quack;
public void Swin()
{
Console.WriteLine("我会游泳");
}
public void PerformQuack()
{
quack.Quack();
}
public void PerformFly()
{
fly.Fly();
}
public void SetFlyInterface(IFly fb)
{
fly = fb;
}
public void SetIQuackInterface(IQuack qb)
{
quack = qb;
}
}
现在,有个绿头鸭,它的特征是:会飞,也会呱呱叫,如何实现?
有个红头鸭,它的特征是:会飞,会吱吱叫,如何实现?
有个橡皮鸭,它的特征是:不会飞也不会叫,如何实现?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 绿头鸭的特性是: 会飞,会呱呱叫
/// </summary>
class MallardDuck:Duck
{
//构造,这个很重要,在多态生成对象时,自动实现接口多态
public MallardDuck()
{
fly = new 会飞的鸭子();//继承了Duck,所以能访问共有变量 fly
quack = new 呱呱叫的鸭子();
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 红头鸭的特征是:会飞,会吱吱叫
/// </summary>
class RedHeadDuck:Duck
{
//构造
public RedHeadDuck()
{
fly = new 会飞的鸭子();
quack = new 吱吱叫的鸭子();
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 橡皮鸭不会飞也不会叫
/// </summary>
class RubberDuck:Duck
{
//构造
public RubberDuck()
{
fly = new 不会飞的鸭子();//继承了Duck,所以能访问共有变量 fly
quack = new 不会叫的鸭子();
}
}
具体实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
Duck mallardDuck = new MallardDuck();
Console.WriteLine("绿头鸭的特征:");
mallardDuck.PerformFly();
mallardDuck.PerformQuack();
Console.WriteLine();
Duck redDuck = new RedHeadDuck();
Console.WriteLine("红头鸭的特征:");
redDuck.PerformFly();
redDuck.PerformQuack();
Console.WriteLine();
Duck rubberDuck = new RubberDuck();
Console.WriteLine("橡皮鸭的特征:");
rubberDuck.PerformFly();
rubberDuck.PerformQuack();
Console.WriteLine();
}
这样就实现了要求,那现在,经过改造,橡皮鸭有“像火箭一样的飞行”的行为,如何实现?
写个行为类继承IFly,实现火箭飞行的方法
{
public void Fly()
{
Console.WriteLine("像火箭样的飞行");
}
}
在Main函数中继续写入
rubberDuck.SetFlyInterface(new 火箭样的飞行());//Duck rubberDuck = new RubberDuck(); 更换接口,默认的接口行为是:不会叫,也不会飞,现,更改飞行接口
rubberDuck.PerformFly();
rubberDuck.PerformQuack();
Run!
4.结论
策略者模式定义:定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。(这也就是为什么图3中为何把“行为”摸去改为算法的原因)
策略者模式的优点:
1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。
策略者模式的缺点:
1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
ps:GRASP(General Responsibility Assignment Software Pattern)是通用职责软件分配模式。GRASP的核心是自己干自己能干的事,自己只干自己的 事,也就是职责的分配和实现高内聚。用来解决面向对象设计的一些问题。此外GRASP原则还阐述了几大原则,以后会继续写博道来。