[Head First设计模式]策略模式
系列文章
引言
该过年了,总让人有点浮躁,公司就省俩人了,唉,如果坐等时间,那实在难熬,只能给自己找点事做,转移一下注意力。进入今天的主题吧策略模式。
策略模式定义
策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
书中鸭子的例子
模拟鸭子的简单应用
Joe上班的公司做了一套相当成功的模拟鸭子游戏SimUDuck,游戏中出现各种鸭子,一边游戏戏水,一边呱呱叫。此系统的内部设计使用了标准的OO技术,设计了一个鸭子超类,并让各种鸭子继承此超类。

让鸭子能飞
去年,公司的竞争力加剧,公司主管认为该是创新的时候了。主管认为,此模拟程序需要会飞的鸭子,将竞争者抛在后面。





改进继承
Joe认识到继承可能不是一个好的解决办法,因为他刚刚拿到来自主管的备忘录,希望以后每六个月更新产品(至于更新办法,他们还没想到)。Joe知道规格会常常改变,每当有新的鸭子子类出现,他就要被迫检视并可能需要覆盖fly()和quack().....这简直是无穷尽的噩梦。所以,他需要一个更清晰的方法,让某些(而不是全部)鸭子类型可飞或可叫。



其实,并非所有的鸭子子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式。虽然Flyable与Quackable可以解决一部分的问题(不会再有会飞的橡皮鸭),但是却造成代码无法复用,这只能算是从一个噩梦跳进另一个噩梦。甚至,在会飞的鸭子中,飞行的动作可能还有多种变化......
现在我们知道使用继承有一些缺失,因为改变鸭子的行为会影响所有种类的鸭子,而这并不恰当。Flyable与Quackable接口一开始似乎还挺不错,解决了问题(只有会飞的鸭子才继承Flyable),但是接口不具有实现代码,所以继承接口无法达到代码的复用。这意味着:无论何时你需要修改某个行为,你被迫得往下追踪并修改每一个有定义此行为的类,一不小心,可能造成新的错误。
第一个设计原则
设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。这个概念很简单,几乎是每个设计模式背后的精神所在,所有的模式都提供了一套方法让系统中的某部分改变不会影响其它部分。
分开变化和不变化的部分
为了要分开变化和不变化的部分,我们准备建立两组类,一个是fly相关,一个是quack相关的,每一组类将实现各自的动作。比如说,我们可能有一个类实现“呱呱叫”,另一个类实现“叽叽叫”,另一个类实现“安静”。

设计鸭子行为
我们利用接口代表每个行为,比方说,IFlyBehavior与IQuackBehaivor,而行为的每个实现都必须实现这些接口之一。所以这次鸭子类不会负责实现fly与quack接口,而是由其他类专门实现IFlyBehavior与IQuackBehaivor,这就称为“行为类”。由行为类实现行为接口,而不是由Duck类实现接口。

这样的做法迥异于以往,以前的做法是:行为是继承自Duck超类的具体实现而来,或是继承某个接口并由子类自行实现而来。这两种做法都是依赖于实现,我们被实现绑的死死,没办法更改行为(除非写更多的代码)。
第二个设计原则
针对接口编程,而不是针对实现编程。
关于接口编程和实现编程:
假设有一个抽象类Animal,有两个具体的实现(Dog与Cat)继承自Animal。

针对实现编程的作法如下:
Dog d = new Dog();
d.bark();
针对接口/超类型编程作法如下:
Animal animal = new Dog();
animal.makeSound();
子类型实例化的动作是“在运行时才指定具体实现的对象”
a = getAnimal();
a.makeSound();
实现鸭子的行为
这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子无关了。而我们可以新增一些行为,不会影响到既有的行为类,也不会影响有使用到飞行行为的鸭子类。
集成鸭子的行为
鸭子现在会将飞行和呱呱叫的动作,委托(delegate)别人处理,而不是使用定义在自己类(或子类)内的方法。
① 首先,在鸭子中加入两个实例变量, 分别为FlyBehavior与QuackBehavior,声明为接口类型(而不是具体类实现类型),每个变量会利用多态的方式在运行时引用正确的行为类型(例如:FlyWithWings、Squeak . . . 等)。
我们也必须将Duck类与其所有子类中的fly() 与quack( ) 移除,因为这些行为已经被搬移到FlyBehavior与QuackBehavior类中了。我们用performFly()和performQuack()取代Duck类中的fly()与quack()。

代码测试
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public abstract class Duck
{
//为行为接口类型声明两个引用变量,所有鸭子子类都继承它们。
public IFlyBehavior flyBehavior;
//每只鸭子都引用实现QuackBehavior接口的对象。
public IQuackBehavior quackBehavior;
public Duck() { }
public abstract void Display();
public void PerformFly()
{
//委托给行为类
flyBehavior.Fly();
}
public void PerformQuack()
{
//鸭子将呱呱叫行为委托给quackBehavior引用的对象。
quackBehavior.Quack();
}
public void Swim()
{
Console.WriteLine("会游泳.....");
}
}
}
Duck
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public interface IFlyBehavior
{
//所有飞行行为必须实现的接口。
void Fly();
}
}
IFlyBehavior
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public interface IQuackBehavior
{
void Quack();
}
}
IQuackBehavior
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public class FlyWithWings : IFlyBehavior
{
#region IFlyBehavior 成员 public void Fly()
{
Console.WriteLine("会飞......");
} #endregion
}
}
FlyWithWings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public class FlyNoWay : IFlyBehavior
{
#region IFlyBehavior 成员 public void Fly()
{
Console.WriteLine("不会飞......");
} #endregion
}
}
FlyNoWay
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public class MuteQuack : IQuackBehavior
{
#region IQuackBehavior 成员 public void Quack()
{
Console.WriteLine("不会叫");
} #endregion
}
}
MuteQuack
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public class Quack : IQuackBehavior
{
#region IQuackBehavior 成员 void IQuackBehavior.Quack()
{
Console.WriteLine("呱呱叫......");
} #endregion
}
}
Quack
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public class Squeak : IQuackBehavior
{
#region IQuackBehavior 成员 public void Quack()
{
Console.WriteLine("吱吱叫.......");
} #endregion
}
}
Squeak
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
/// <summary>
/// MallardDuck从Duck继承,具有flyBehavior 和quackBehavior 实例变量。
/// </summary>
public class MallardDuck : Duck
{
public MallardDuck()
{
//FlyWithWings作为IFlyBehavior类型
base.flyBehavior = new FlyWithWings();
//Quack类处理呱呱叫。
base.quackBehavior = new Quack();
}
public override void Display()
{
Console.WriteLine("绿头鸭......");
}
}
}
MallardDuck
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
public class WoodDuck : Duck
{
public WoodDuck()
{
flyBehavior = new FlyNoWay();
quackBehavior = new MuteQuack();
}
public override void Display()
{
Console.WriteLine("木头鸭子.....");
}
}
}
WoodDuck
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.设计模式
{
class Program
{
static void Main(string[] args)
{
Duck mallard = new MallardDuck();
mallard.Display();
//调用MallardDuck继承来的perform方法,进而将绿头鸭的行为委托给quack和fly的行为类来处理。
mallard.PerformFly();
mallard.PerformQuack();
mallard.Swim();
Duck wood = new WoodDuck();
wood.Display();
wood.PerformFly();
wood.PerformQuack();
wood.Swim();
Console.Read(); }
}
}
结果
总结
所有鸭子从Duck继承,飞行行为实现FlyBehavior接口,呱呱叫行为实现QuackBehavior接口。

“有一个”(has a)可能比“是一个”(is a)更好
有一个关系相当有趣:每一鸭子都有一个FlyBehavior且有一个QuackBehavior,让鸭子将飞行和呱呱叫委托它们代为处理。
如果将两个类结合起来使用(如同本例),这就是组合(Composition)。这种作法和继承不同的地方在于:鸭子的行为不是继承而来,而是和适当的行为对象组合而来。
第三个设计原则
多用组合,少用继承
使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态地改变行为。
优缺点
[Head First设计模式]策略模式的更多相关文章
- 15. 星际争霸之php设计模式--策略模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- [.net 面向对象程序设计深入](24)实战设计模式——策略模式(行为型)
[.net 面向对象程序设计深入](24)实战设计模式——策略模式(行为型) 1,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它 ...
- linkin大话设计模式--策略模式
linkin大话设计模式--策略模式 Strategy [ˈstrætədʒi] 策略 策略模式用于封装系列的算法,这些算法通常被封装在一个称为Context的类中,客户端程序可以自由的选择任何一种 ...
- [.net 面向对象程序设计深入](26)实战设计模式——策略模式 Strategy (行为型)
[.net 面向对象程序设计深入](26)实战设计模式——策略模式 Strategy (行为型) 1,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模 ...
- 设计模式-策略模式(Strategy Model)
1.概述 在开发过程中常常会遇到类似问题,实现一个功能的时候往往有多种算法/方法(策略),我们可以根据环境的不同来使用不同的算法或策略来实现这一功能. 如在人物比较排序的实现中,我们有 ...
- java设计模式 策略模式Strategy
本章讲述java设计模式中,策略模式相关的知识点. 1.策略模式定义 策略模式,又叫算法簇模式,就是定义了不同的算法族,并且之间可以互相替换,此模式让算法的变化独立于使用算法的客户.策略模式属于对象的 ...
- javascript 设计模式-----策略模式
在<javascript设计模式>中,作者并没有向我们介绍策略模式,然而它却是一种在开发中十分常见的设计模式.最常见的就是当我们遇到一个复杂的表单验证的时候,常常需要编写一大段的if和el ...
- JAVA 设计模式 策略模式
用途 Title 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 策略模式是一种行为型模式. 结构
- PHP设计模式-策略模式 转
策略模式(Strategy Pattern) 策略模式是对象的行为模式,用意是对一组算法的封装.动态的选择需要的算法并使用. 策略模式指的是程序中涉及决策控制的一种模式.策略模式功能非常强大,因为这个 ...
随机推荐
- android onActivityResult无效或先执行或无回传问题
我的问题属于第一种情况,更改后下确有回传结果. (http://www.cnblogs.com/tt_mc/p/3586834.html) Android Activity的加载模式和onActivi ...
- Fiddler进行模拟Post提交数据,总为null解决方式
Fiddler模拟post提交时总是为空,解决办法 如果是表单提交则要在header加上 ContentType:application/x-www-form-urlencoded 如果是要post提 ...
- LLVM 笔记(四)—— three-phase 设计的收益
ilocker:关注 Android 安全(新手) QQ: 2597294287 采用 three-phase 的设计方式,便于编译器支持多种语言和多种目标平台. 如果在优化器阶段采用通用的 IR ( ...
- 图像柔光效果(SoftGlow)的原理及其实现。
图像柔光效果在很多商业软件中都有实现,比如美图秀秀,光影魔术手等.其能针对原始图像产生一副新的比较平滑感觉光线比较柔和的效果,给人一种朦胧美,如下面几幅图所示: ...
- HDU 1729 Stone Game【SG函数】
以下转载至:长春理工大学赵小舟博弈论ppt 题目大意: 1.有n个盒子,每个盒子都有它的容量s 2.在游戏开始时,每个盒子里都有一些石子 3.双方轮流进行游戏,向一个盒子投入n个石子,其中n不能大于当 ...
- JS控制div跳转到指定的位置的解决方案总结
总结一下自己在写这个需求遇到的问题,相信大家应该是经常遇到的.即要求滚轮滚动到指定的位置.先看下基本的解决方案. 1.给链接a加个#的方式来实现跳转.(锚点方法)这里直接贴下代码: html页面: & ...
- 【原】python中文文本挖掘资料集合
这些网址是我在学习python中文文本挖掘时觉得比较好的网站,记录一下,后期也会不定期添加: 1.http://www.52nlp.cn/python-%E7%BD%91%E9%A1%B5%E7% ...
- 格雷码原理与Verilog实现
格雷码原理 格雷码是一个叫弗兰克*格雷的人在1953年发明的,最初用于通信.格雷码是一种循环二进制码或者叫作反射二进制码.格雷码的特点是从一个数变为相邻的一个数时,只有一个数据位发生跳变,由于这种特点 ...
- python爬某个网站的图片
# _*_ coding: gbk _*_ import urllib import urllib2 import re class Spider: def getImage(self,html): ...
- bzoj1584
1584: [Usaco2009 Mar]Cleaning Up 打扫卫生 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 467 Solved: 31 ...