1. 范例

公司做了一套鸭子模拟游戏(SimUDuck),游戏中会有各种鸭子,会游戏划水、会呱呱叫。

2. 初始化版本—继承

定义一个鸭子父类:Duck,并让各种鸭子继承此父类

鸭子父类:Duck类 方法分析:

  • 所有鸭子都会呱呱叫,所以此行为由父类实现
  • 所有鸭子都会游泳,所以此行为由父类实现
  • 因为每一个鸭子的外观都不尽相同,所以此外观方法定义为抽象方法,具体内容由子类实现

鸭子父类:Duck类

package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鸭子父类* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/
public abstract class Duck {// 呱呱叫行为方法实现public void quack(){System.out.println("呱呱叫...");}// 游泳行为方法实现public void swim(){System.out.println("游泳...");}// 外观:每个鸭子的外观都不相同,由子类自己实现public abstract void display();}

鸭子子类:绿头鸭

package com.jbp.designpattern;/*** @ClassName: MallardDuck* @description: 绿头鸭子类* @author: JiangBeiPing* @create: 2021-06-22 16:59* @Version: 1.0**/
public class MallardDuck extends Duck {@Overridepublic void display() {System.out.println("绿头鸭...");}
}

鸭子子类:红头鸭

package com.jbp.designpattern;/*** @ClassName: RedheadDuck* @description: 红头鸭子类* @author: JiangBeiPing* @create: 2021-06-22 17:00* @Version: 1.0**/
public class RedheadDuck extends Duck{@Overridepublic void display() {System.out.println("红头鸭...");}
}

2.1 需求变化—新增会飞的鸭子

后续公司决定在此模拟程序新增会飞的鸭子

功能实现思路:在Duck类中加上飞行fly()方法

Duck类:

package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鸭子父类* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/
public abstract class Duck {// 呱呱叫行为方法实现public void quack(){System.out.println("呱呱叫...");}// 游泳行为方法实现public void swim(){System.out.println("游泳...");}// 外观:每个鸭子的外观都不相同,由子类自己实现public abstract void display();// 新增飞行方法public void fly(){System.out.println("飞行...");}}

鸭子子类:橡皮鸭

package com.jbp.designpattern;/*** @ClassName: RubberDuck* @description: 橡皮鸭子类* @author: JiangBeiPing* @create: 2021-06-22 17:13* @Version: 1.0**/
public class RubberDuck extends Duck{// 橡皮鸭只能吱吱吱叫,所以覆盖此方法@Overridepublic void quack() {System.out.println("吱吱吱");}@Overridepublic void display() {System.out.println("橡皮鸭...");}
}

带来的问题:游戏中,所有的鸭子都会飞(橡皮、玩具鸭也都会飞)

解决方案:

把橡皮鸭子类的飞行方法fly()覆盖成空方法。

新的问题—后面的每创建一个新的鸭子类,都需要按情况进行方法覆盖。

package com.jbp.designpattern;/*** @ClassName: RubberDuck* @description: 橡皮鸭子类* @author: JiangBeiPing* @create: 2021-06-22 17:13* @Version: 1.0**/
public class RubberDuck extends Duck{// 橡皮鸭只能吱吱吱叫,所以覆盖此方法@Overridepublic void quack() {System.out.println("吱吱吱");}@Overridepublic void display() {System.out.println("橡皮鸭...");}@Overridepublic void fly() {}
}

3. 优化版本—接口

把 fly()方法从父类 Duck类中抽取成一个 Flyable接口,只需要会飞的鸭子子类实现该接口即可,不需要飞行的鸭子子类无需理会此接口。同理,把呱呱呱叫的方法 quack() 也抽取成一个 Quackable接口。

新的问题:

代码复用性差。

比如,有 n 个鸭子子类,就需要实现 n 次飞行或者叫声的方法的具体内容。

4. 最终版本—策略模式


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


思路分析:

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

变化的部分:

  • quack():鸭子叫声
  • fly():飞行

不会变化的部分:

  • swim():游泳

新增两个类,一个是 fly()飞行相关的,一个是quack()鸭子叫声相关的,每一个类实现各自的动作。

鸭子行为设计思路分析:

如何设计实现飞行和鸭子叫行为的类?

应该在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态地改变该子类的行为。比如,在产生新的绿头鸭实例时,再指定特定的的“类型”的飞行行为给它。

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

利用接口代表行为,比如,FlyBehavior 和 QuackBehavior,而行为的实现类自行实现自己对应的接口。

所以,鸭子子类不会去实现 Flying 和 Quacking 接口。而是由特定的行为类专门实现 FlyBehavior 和 QuackBehavior。

之前的做法是:行为来自鸭子父类Duck类,或者继承某个行为接口,由鸭子子类自行实现。这两种方法都是行为依赖于实现。

现在的做法是:鸭子的子类使用接口 FlyBehavior 和 QuackBehavior所表示行为,所以具体的实现由行为实现类具体实现而不是由鸭子子类实现。


代码实现:

飞行接口:

package com.jbp.designpattern;/*** @ClassName: FlyBehavior* @description: 飞行接口:所有的飞行鸭子类都需实现此接口,并且实现fly()* @author: JiangBeiPing* @create: 2021-06-22 18:06* @Version: 1.0**/
public interface FlyBehavior {public void fly();
}

飞行实现类:

package com.jbp.designpattern;/*** @ClassName: FlyWithWings* @description: 飞行行为具体实现类* @author: JiangBeiPing* @create: 2021-06-22 18:08* @Version: 1.0**/
public class FlyWithWings implements FlyBehavior {@Overridepublic void fly() {System.out.println("飞行...");}
}

飞行实现类:

package com.jbp.designpattern;/*** @ClassName: FlyNoWay* @description: 不会飞行的行为具体实现类* @author: JiangBeiPing* @create: 2021-06-22 18:10* @Version: 1.0**/
public class FlyNoWay implements FlyBehavior {@Overridepublic void fly() {// 什么都不做,不会飞行}
}

叫声接口:

package com.jbp.designpattern;/*** @ClassName: QuackBehavior* @description: 鸭子叫声实现接口* @author: JiangBeiPing* @create: 2021-06-22 18:14* @Version: 1.0**/
public interface QuackBehavior {public void quack();}

叫声实现类:

package com.jbp.designpattern;/*** @ClassName: Quack* @description: 鸭子呱呱呱叫行为实现* @author: JiangBeiPing* @create: 2021-06-22 18:16* @Version: 1.0**/
public class Quack implements QuackBehavior{@Overridepublic void quack() {System.out.println("呱呱叫...");}
}

叫声实现类:

package com.jbp.designpattern;/*** @ClassName: Squeak* @description: 鸭子吱吱吱叫行为具体实现* @author: JiangBeiPing* @create: 2021-06-22 18:18* @Version: 1.0**/
public class Squeak implements QuackBehavior{@Overridepublic void quack() {System.out.println("吱吱吱...");}
}

叫声实现类:

package com.jbp.designpattern;/*** @ClassName: MuteQuack* @description: 鸭子不能叫行为具体实现* @author: JiangBeiPing* @create: 2021-06-22 18:19* @Version: 1.0**/
public class MuteQuack implements QuackBehavior{@Overridepublic void quack() {// 什么都不做,不会叫}
}

4.1 修改鸭子父类 Duck 类

现在鸭子的飞行和叫声动作会由行为类进行具体实现,而不是在Duck 类或子类中自己实现。

首先,在Duck 类中新增两个实例变量,分别是 FlyBehavior 和 QuackBehavior ,声明为接口类型,而不是具体类实现类型。每个鸭子子类对象都会动地设置这些变量以在运行时引用正确的行为类型。

然后,用两个方法 performFly() 和 performQuack()替换Duck 类中的 fly()和quack()。

Duck 类:

package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鸭子父类* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/
public abstract class Duck {// 每个鸭子子类都会引用实现FlyBehavior接口的对象FlyBehavior flyBehavior;QuackBehavior quackBehavior;// 游泳行为方法实现public void swim(){System.out.println("游泳...");}// 外观:每个鸭子的外观都不相同,由子类自己实现public abstract void display();// 鸭子对象不亲自处理叫声行为,而是由quackBehavior引用的对象实现public void performQuack(){quackBehavior.quack();}public void performFly(){flyBehavior.fly();}}

绿头鸭子类:

package com.jbp.designpattern;/*** @ClassName: MallardDuck* @description: 绿头鸭子类* @author: JiangBeiPing* @create: 2021-06-22 16:59* @Version: 1.0**/
public class MallardDuck extends Duck {@Overridepublic void display() {System.out.println("绿头鸭...");}// 当绿头鸭子类实例化时,构造器会把继承来的quackBehavior实例变量初始化成Quack类的新实例public MallardDuck(){// 绿头鸭使用Quack类处理叫声进行呱呱叫quackBehavior = new Quack();flyBehavior = new FlyWithWings();}
}

测试:

4.2 动态设定行为(避免构造器中设定)

在鸭子子类中通过属性设置方法(setter method)来设置鸭子的行为,从而避免在鸭子的构造器内实例化。

Duck 类新增setter方法:

package com.jbp.designpattern;/*** @ClassName: Duck* @description: 鸭子父类* @author: JiangBeiPing* @create: 2021-06-22 14:57* @Version: 1.0**/
public abstract class Duck {// 每个鸭子子类都会引用实现FlyBehavior接口的对象FlyBehavior flyBehavior;QuackBehavior quackBehavior;// 游泳行为方法实现public void swim(){System.out.println("游泳...");}// 外观:每个鸭子的外观都不相同,由子类自己实现public abstract void display();// 鸭子对象不亲自处理叫声行为,而是由quackBehavior引用的对象实现public void performQuack(){quackBehavior.quack();}public void performFly(){flyBehavior.fly();}public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;}public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;}
}

鸭子子类:模型鸭:

package com.jbp.designpattern;/*** @ClassName: ModelDuck* @description: 模型鸭子类* @author: JiangBeiPing* @create: 2021-06-23 11:02* @Version: 1.0**/
public class ModelDuck extends Duck{@Overridepublic void display() {System.out.println("模型鸭...");}public ModelDuck() {// 模型鸭不会飞flyBehavior = new FlyNoWay();quackBehavior = new Quack();}
}

新增新的飞行行为:火箭飞行:

package com.jbp.designpattern;/*** @ClassName: FlyRocketPowered* @description: 火箭动力飞行* @author: JiangBeiPing* @create: 2021-06-23 11:05* @Version: 1.0**/
public class FlyRocketPowered implements FlyBehavior{@Overridepublic void fly() {System.out.println("火箭动力飞行...");}
}

测试:

package com.jbp.designpattern;/*** @ClassName: DuckTest* @description: 鸭子类测试* @author: JiangBeiPing* @create: 2021-06-22 17:16* @Version: 1.0**/
public class DuckTest {public static void main(String[] args) {Duck modelDuck = new ModelDuck();// 第一次调用performFly(),飞行行为由FlyBehavior接口(具体实现由FlyNoWay实现),在模型鸭构造器设置modelDuck.performFly();// 调用父类的setter方法,把火箭飞行行为设置到模型鸭中modelDuck.setFlyBehavior(new FlyRocketPowered());// 模型鸭动态地改变自身的飞行行为modelDuck.performFly();}
}

5. 总结


重构后,类分为:鸭子子类继承Duck、飞行行为实现FlyBehavior接口、呱呱叫行为实现QuackBehavior接口。

描述事情的方式也从“一组行为”改成“一族算法”,算法表示鸭子能做的事情(不同的叫声和不同的飞行方式)。

每一个鸭子都有一个FlyBehavior和QuackBehavior,飞行和叫声行为都由其行为具体实现类实现。当两个类结合起来一起使用,如同测试类中,即是组合(composition)。这种方式和继承的区别是,鸭子子类的行为不是继承而来的,而是和适当的行为对象组合而来。

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

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

《Head First 设计模式》(一):策略模式相关推荐

  1. Java设计模式之策略模式与状态模式

    一.策略模式定义 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们之间可以相互替换,策略模式可以在不影响客户端的情况下发生变化. 好了,定义看看就完了,我知道你很烦看定义. 二.策 ...

  2. 换个姿势学设计模式:策略模式

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源:公众号「闻人的技术博客」 前言 前段时间,接到一个 ...

  3. 研磨设计模式之 策略模式--转

    http://www.uml.org.cn/sjms/201009092.asp 研磨设计模式之 策略模式   2010-09-09 作者:云飞龙行 来源:云飞龙行的blog   先感谢众多朋友的支持 ...

  4. 设计模式:策略模式(Strategy)

    定   义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...

  5. C++设计模式之策略模式(Strategy)

    Strategy策略模式 作用:定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. UML图: 代码实现 #include <iostream& ...

  6. python策略模式包含角色_详解Python设计模式之策略模式

    虽然设计模式与语言无关,但这并不意味着每一个模式都能在每一门语言中使用.<设计模式:可复用面向对象软件的基础>一书中有 23 个模式,其中有 16 个在动态语言中"不见了,或者简 ...

  7. 一篇博客读懂设计模式之-----策略模式

    设计模式之策略模式 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的对象 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换. 主要解决:在有多种算法相似的情况下 ...

  8. 面向对象设计模式之策略模式

    面向对象设计模式之策略模式 1.策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户 2.抽象鸭子类,鸭子飞行行为在此处类似于算法族 1 package ...

  9. java策略模式详解_Java经典设计模式之策略模式原理与用法详解

    本文实例讲述了Java经典设计模式之策略模式.分享给大家供大家参考,具体如下: 策略模式指:策略模式指将程序中可变部分抽象分离成一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式 ...

  10. 策略设计模式_设计模式之策略模式总结

    再上一篇文章<设计模式之策略模式>中,我们通过模拟鸭子项目,了解了什么是策略模式,怎么使用策略模式.本文将通过鸭子项目的学习,对策略模式进行总结. 策略模式: 分别封装行为接口,实现算法族 ...

最新文章

  1. 大脑活动与认知: 热力学与信息论的联系
  2. crash工具解析_IDA反汇编静态调试Android平台C++的so文件Crash入门
  3. npm命令,开发依赖,版本号【正解】
  4. 图片加尺寸php代码,php获取图片尺寸(宽度,高度)_php
  5. 详细剖析linux的内存管理方式(分段式、分页式、段页式),以及进程状态的具体关系
  6. python程序设计基础第三版_Python程序设计(第三版)PPT及源码
  7. flex4 日期类型字符串转日期类型(string转Date)
  8. 关于部分应用无法向POJ提交代码的解决方案
  9. windows下运行python打印有颜色的字_Windows和Linux下Python输出彩色文字的方法教程...
  10. Flutter TextField 限制只允许输入数字,字母,小数,设置限制小数位数
  11. 12-ubuntu:010 Editor
  12. fastdb缩小初始生成文件
  13. python核心教程:min函数和max函数用法
  14. unity功能开发——实名认证
  15. python猜大小程序_「每日一练」巧用python实现猜大小的游戏
  16. 外媒分析:为何说苹果一定没造车!
  17. matlab的模型文件扩展名,[转载]matlab simulink基础知识
  18. Think in java(四)枚举类enum的基本特性、构造方法与方法覆盖、Switch语句中的enum运用
  19. Tomcat项目启动后,页面无法显示验证码
  20. ImageViewEx控件介绍

热门文章

  1. 产品必备-产品FDD模板(PRD)
  2. 计算机组成原理——基础知识
  3. 《Unix传奇》:众神的创世记
  4. PHP 生成 ppt,PhpPresentation生成ppt
  5. 泰凌微ble mesh蓝牙模组天猫精灵学习之旅 ④ 初认识阿里天猫精灵官方Genie BT mesh Stack框架, windows平台搭建打印Hello World !
  6. Win10系统的背景颜色
  7. 通信信号调制方式识别——综述/硕博
  8. linux 进程killed_linux下运行Gaussian09进程被killed
  9. linux版gaussian运行,【求助】gaussian 在linux不能运行?急急急 - 量子化学 - 小木虫 - 学术 科研 互动社区...
  10. java网站后台管理系统_java网站后台管理系统