《Head first设计模式》学习笔记 – 策略模式,

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

假设有一个模拟鸭子的游戏,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。这个游戏的内部设计了一个鸭子超类Duck,并让各种鸭子继承此超类。

public abstract class Duck {

public void quack(){

// 所有的鸭子都会呱呱叫,由Duck类负责实现

}

public void swim(){

// 所有的鸭子都会游泳,由Duck类负责实现

}

public abstract void display(); // 每个鸭子的子类负责实现自己的display

}

现在要增加一个功能,让鸭子能飞。

实现方法1:在Duck类里加上fly()方法

只需要在Duck类中加上fly()方法,所有的鸭子都会继承fly()。

但是问题来了,并非所有的子类都会飞,比如橡皮鸭子就不能飞。在超类中加上fly(),就会导致所有的子类都具备fly(),连那些不该具备fly()的子类也无法免除。

对代码所做的局部修改,影响层面可不只是局部!

实现方法2:把fly()从超类中取出来

我们可以把fly()从超类中取出来,放入一个“Flyable接口”中。这么一来,只有会飞的鸭子才能实现此接口。同样的方式,也可以用来设计一个“Quackable接口”,因为不是所有的鸭子都会叫。

这也是个超笨的主意,这样一来重复的代码会变多。如果有48个Duck的子类都要稍微修改一下飞行的行为,需要修改48个Duck的子类的代码。

虽然Flyable与Quackable可以解决“一部分”问题(不会再有会飞的橡皮鸭),但是却造成代码无法复用。

设计原则:找出应用中可能出现变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

分开变化和不会变化的部分

就我们目前所知,除了fly()和quack()的问题之外,Duck类还算一切正常。现在,为了要分开“变化和不会变化的部分”,我们准备建立两组类(完全远离Duck类),一个是“fly”相关的,一个是“quack”相关的,每一组类将实现各自的动作。

设计鸭子的行为

如何设计那组实现飞行和呱呱叫的行为的类呢?

我们希望一切能有弹性,毕竟,正是因为一开始鸭子行为没有弹性,才让我们走上现在这条路。我们应该在鸭子类中包含设定行为的方法,这样可以实现在“运行时”动态地“改变”鸭子的飞行行为。

设计原则:针对接口编程,而不是针对实现编程。

// 针对实现编程

// 声明变量“d”为Dog类型,会造成我们必须针对具体实现编码

Dog d = new Dog();

d.bark();

// 针对接口/超类型编程

// 我们知道该对象是狗,但是我们现在利用animal进行多态的调用

Animal animal = new Dog();

animal.makeSound();

// 更棒的是,子类实例化的动作不再需要在代码中硬编码,

// 例如new Dog(),而是“在运行时才指定具体实现的对象”

// 我们不知道实际的子类型是“什么”,我们只关心它知道如何正确地进行makeSound()的动作就够了

a = getAnimal();

a.makeSound();

我们利用接口代表行为,比方说,FlyBehavior与QuackBehavior,而行为的每个实现都将实现其中一个接口。

所以这次鸭子类不会负责实现Flying与Quacking接口,反而是由我们制造一组其他类专门实现FlyBehavior与QuackBehavior,这就成为“行为”类。

这样的做法迥异于以往,以前的做法是:行为来自Duck超类的具体实现,或是继承某个接口并由子类自行实现。这两种做法都是依赖于“实现”,我们被“实现”绑得死死的,没办法更改行为。

在我们的新设计中,鸭子的子类将使用接口(FlyBehavior与QuackBehavior)所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类中。

实现鸭子的行为

在此,我们有两个接口,FlyBehavior与QuackBehavior,还有对应的子类,负责实现具体的行为:

FlyWithWings继承自FlyBehavior,实现鸭子飞行;

FlyNoWay继承自FlyBehavior,实现不会飞的鸭子的行为;

Quack继承自QuackBehavior,实现鸭子呱呱叫;

Squeak继承自QuackBehavior,实现橡皮鸭子吱吱叫;

MuteQuack继承自QuackBehavior,实现不会叫的鸭子的行为。

这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子类无关了。这么一来,有了继承的“复用”好处,却没有继承所带来的包袱。

而我们可以新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为的鸭子类。

整合鸭子的行为

在Duck类中加入两个实例变量,分别为“flyBehavior”与“quackBehavior”,声明为接口类型,每个鸭子对象都会动态地设置这些变量以在运行时引用正确的行为类型。

我们用两个相似的方法performFly()与performQuack()取代Duck类中的fly()与quack()。

实现performQuack():

// 在这部分代码中,我们不在乎quackBehavior接口的对象到底是什么,

// 我们只关心该对象知道如何进行呱呱叫就够了。

public class Duck {

// 每只鸭子都会引用实现QuackBehavior接口的对象

QuackBehavior quackBehavior;

public void performQuack(){

// 鸭子对象不亲自处理呱呱叫行为,而是委托给quackBehavior引用的对象

quackBehavior.quack();

}

}

设定quackBehavior的实例变量:

public class MallardDuck extends Duck {

public MallardDuck(){

// 绿头鸭使用Quack类处理呱呱叫,所以当performQuack()被调用时,

// 叫的职责被委托给Quack对象,而我们得到了真正的呱呱叫

quackBehavior = new Quack();

}

}

同样的处理方式也可以用在飞行行为上。

组合

每一个鸭子都有一个FlyBehavior和QuackBehavior,好将飞行和呱呱叫委托给它们代为处理。

当你将两个类结合起来使用,如同本例一般,这就是组合(composition)。这种做法和“继承”不同的地方在于,鸭子的行为不是继承来的,而是和适当地行为对象“组合”来的。

设计原则:多用组合,少用继承。

使用组合建立系统具有很大的弹性,不仅可将算法封装成类,更可以“在运行时动态地改变行为”,只要组合的行为对象符合正确的接口标准即可。

完整的代码

// Duck超类

public abstract class Duck {

FlyBehavior flyBehavior;

QuackBehavior quackBehavior;

public Duck(){

}

public abstract void display();

public void performFly(){

flyBehavior.fly();

}

public void performQuack(){

quackBehavior.quack();

}

public void swim(){

System.out.println("All ducks float, even decoys!");

}

}

// MallardDuck类

public class MallardDuck extends Duck {

public MallardDuck(){

quackBehavior = new Quack();

flyBehavior = new FlyWithWings();

}

public void display() {

System.out.println("I'm a real Mallard duck");

}

}

// 飞行行为接口

public interface FlyBehavior {

public void fly();

}

// 飞行行为类

public class FlyWithWings implements FlyBehavior {

public void fly(){

System.out.println("I'm flying!!");

}

}

// 飞行行为类

public class FlyNoWay implements FlyBehavior {

public void fly(){

System.out.println("I can't fly");

}

}

// 叫声行为接口

public interface QuackBehavior {

public void quack();

}

// 叫声行为类

public class Quack implements QuackBehavior{

public void quack(){

System.out.println("quack");

}

}

// 叫声行为类

public class MuteQuack implements QuackBehavior{

public void quack(){

System.out.println("<< silence >>");

}

}

// 叫声行为类

public class Squeak implements QuackBehavior{

public void quack(){

System.out.println("squeak");

}

}

// 测试类

public class MiniDuckSilmulator{

public static void main(String[] args){

Duck mallard = new MallardDuck();

mallard.performQuack();

mallard.performFly();

}

}

本系列:

《Head first设计模式》学习笔记 – 观察者模式

《Head first设计模式》学习笔记 – 策略模式

《Head first设计模式》学习笔记 – 工厂方法模式

《Head first设计模式》学习笔记 – 抽象工厂模式

《Head first设计模式》学习笔记 – 单件模式

《Head first设计模式》学习笔记 – 命令模式

《Head first设计模式》学习笔记 – 适配器模式

《Head first设计模式》学习笔记 – 外观模式

《Head first设计模式》学习笔记 – 模板方法模式

《Head first设计模式》学习笔记 – 迭代器模式

打印鸭子戏水java,《Head first设计模式》学习笔记 #8211; 策略模式,相关推荐

  1. C++设计模式学习笔记:策略模式

    C++设计模式学习笔记:策略模式 策略模式设计商场促销 1.策略模式介绍 2.商场收银系统策略模式实现 3.策略模式与工厂模式结合 3.策略模式与工厂模式对比 策略模式设计商场促销 1.策略模式介绍 ...

  2. 设计模式学习笔记--Strategy 策略模式

    所谓策略模式(Strategy Pattern),就是将策略 (算法) 封装为一个对象,易于相互替换,如同 USB 设备一样可即插即用:如果将策略.具体的算法和行为,编码在某个类或客户程序内部,将导至 ...

  3. 设计模式学习笔记(5) - 策略模式

    我一直觉得策略模式是一个很好玩的模式,让我们用游戏来了解一下. 举一个武侠的例子: 小说中大侠一般都有两样功夫: 第一:武功 第二:轻功 说到武功,让我们看看天龙八部里的三位高手的绝招: 萧峰:降龙十 ...

  4. Head First 设计模式学习笔记 一 策略模式

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 这是一个比较简单的模式,核心的思想就是将应用中的变化之处独立出来,这样就可以 ...

  5. 设计模式学习笔记——访问者(Visitor)模式

    设计模式学习笔记--访问者(Visitor)模式 @(设计模式)[设计模式, 访问者模式, visitor] 设计模式学习笔记访问者Visitor模式 基本介绍 访问者案例 类图 实现代码 Visit ...

  6. 设计模式学习笔记——组合(Composite)模式

    设计模式学习笔记--组合(Composite)模式 @(设计模式)[设计模式, 组合模式, composite] 设计模式学习笔记组合Composite模式 基本介绍 组合案例 类图 实现代码 Ent ...

  7. 设计模式学习笔记——桥接(Bridge)模式

    设计模式学习笔记--桥接(Bridge)模式 @(设计模式)[设计模式, 桥接模式, bridge] 设计模式学习笔记桥接Bridge模式 基本介绍 类的功能层次结构 类的实现层次结构 使用桥接模式的 ...

  8. 设计模式学习笔记——解释器(Interpreter)模式

    设计模式学习笔记--解释器(Interpreter)模式 @(设计模式)[设计模式, 解释器模式, Interpreter] 设计模式学习笔记解释器Interpreter模式 基本介绍 解释器案例 类 ...

  9. 设计模式学习笔记——命令(Command)模式

    设计模式学习笔记--命令(Command)模式 @(设计模式)[设计模式, 命令模式, command] 设计模式学习笔记命令Command模式 基本介绍 命令案例 类图 实现代码 Command接口 ...

最新文章

  1. 如果神经网络规模足够大,会产生智能吗?
  2. 得到Raster的一些基本信息(高程值,列数,列数)
  3. ubuntu12.4上安装minigui3.0.12
  4. 广东职业教育信息化研究会2019年会暨区块链专题研讨会
  5. 前端学习(2178):vue-router得参数传递二
  6. 华为上机试---购物单(算法:背包问题)
  7. python3.6.1安装,linux下安装Python3.6.1
  8. linux 修改hba参数,更改Raid卡和HBA卡在linux下的启动顺序
  9. numpy 二维、高维矩阵重新排列轴与数据(轴转换)
  10. 【前端】前端面试题整理
  11. python websocket server模块_Python 3.5.2实现websocket服务端(四): WebSocketServer类实现...
  12. MySql中时间类型总结
  13. 百余篇SCI文章的一字经验----短
  14. COMSOL:案列应用实操教学---光电
  15. Windows电脑桌面云便签快捷键怎么查看?
  16. 世界杯开打,病毒借明星无孔不入
  17. python 录音vad_静音检测VAD算法
  18. lstm中look_back的大小选择_基于时空关联度加权的LSTM短时交通速度预测
  19. android颜色对应的xml配置值,颜色表
  20. 继小米机器狗、特斯拉机器人后,小鹏汽车发布智能机器马:真的能骑!

热门文章

  1. 基于ssm的列车火车高铁票务信息管理系统 java毕业设计项目介绍
  2. 数码相框 在LCD上显示多行文字(6)
  3. 弱电工作5年,我是如何下定决心转行从事网络技术的
  4. 解决“无法定位序数***于动态链接库iertutil.dll上”的问题
  5. Logical Import Mode
  6. 动态规划算法计算硬币组合
  7. 恭喜开源社谭中意和庄表伟荣获“2022 年度开源优秀人物奖”
  8. Ladder Net 学习笔记
  9. 计算机组成五大部分是什么?
  10. 使用jqPaginator分页