[head first 设计模式] 第一章 策略模式
[head first 设计模式] 第一章 策略模式
让我们先从一个简单的鸭子模拟器开始讲起。
假设有个简单的鸭子模拟器,游戏中会出现各种鸭子,此系统的原始设计如下,设计了一个鸭子超类,并让各种鸭子继承此超类。

若此时我们有了一个新的需求,我们需要鸭子会飞,那么我们该如何修改代码呢?
最初,我们想在基类上加上fly方法,使得所有子类鸭子都拥有相应的fly方法。但这样错误产生了,即使是本不该会飞的橡皮鸭子也拥有了fly方法。
或许我们可以把橡皮鸭中的fly方法覆盖掉,但这样每次新加入的不会飞的新鸭子类型,难道都要额外覆盖一次fly方法吗?太麻烦了。
利用继承来提供duck的行为,会导致运行时的行为不容易改变,且改变容易牵一发动全身。
那么,利用接口如何?
若经常需要更新产品,那么每次覆盖fly简直是噩梦。那么,我们将fly单独写成一个接口,只有会飞的鸭子实现这个接口如何?

但这样其实重复的代码会变得非常多,造成fly代码无法复用,每个会飞的鸭子都要实现fly方法。
那么我们该如何解决这个问题?在使用设计模式之前,不妨先求索于OO原则!
软件开发中,什么是永恒真理?
唯一不变的是变化本身——约翰逊·斯宾塞
现在我们已经知道了继承无法很好的解决问题,因为鸭子的行为在子类中不断改变,并且有的行为子类不应该拥有。使用接口初看挺不错的,但继承接口无法达到代码的复用。这意味着,无论合适你需要修改某个行为,你必须向下追踪并在每一个定义此行为的类中修改它。
但还好,有一个OO设计原则正好适用于此种情况:
找出系统中可能需要变化之处,把他们独立出来,不要和那些不变化的代码堆在一起。
也就是把会变化的部分取出来,好让其他部分不会受此影响。
把会变化的部分取出来并封装,以后可以轻易地改动或扩充此部分,而不影响其他不需要变化的部分。
那么,现在是时候把鸭子的行为从Duck类中取出了。
分开变化和不会变化的部分
目前而言,除了fly()和quack()以外,duck类其他部分看起来不怎么变动,所以我们仅做些小改变。
为此,我们准备建立两组类,一个是和fly相关的,另一个和quack相关的,每一组类都实现各自的动作。

设计鸭子的行为
如何设计
那组实现飞行和叫声的类呢?我们希望一切能有弹性,并且能够将行为指定到鸭子的实例。并且可以让鸭子的行为动态的改变。
有了这些目标要实现,我们看第二个设计原则
针对接口编程,而不是针对实现编程。
从现在开始,鸭子的行为将被放在分开的类中,此类专门提供某行为接口的实现。这样,鸭子类就不再需要知道行为的具体细节。
这次鸭子类不会负责实现flying和quacking接口,而是由我们制造一组其他类专门实现flybehavior和quackbeavior,这就称为行为类,由行为类而不是duck类来实现行为接口。
这种做法和以往不同,以往是行为来自于duck超类的具体实现,或是继承某个接口并由子类自行实现而来。这两种方法都是依赖于实现,没法变更行为。
在我们的新设计中,鸭子的子类将使用接口所表示的行为,所以具体的实现不会被绑定在鸭子的子类中。

整合鸭子的行为
关键在于,鸭子会将飞行和叫声行为委托给其他对象处理。而不是由自己定义。
在Duck类中加入flyBehavior和quackBehavior变量,声明为接口类型。每个鸭子对象动态设置这些变量以在运行时引用正确的行为类型。
代码如下
public interface FlyBehavior {
public void fly();
}
public interface QuackBehavior {
public void quack();
}
public class FlyWithWings implements FlyBehavior{
@Override
public void fly() {
System.out.println("I'm flying");
}
}
public class FlyNoWay implements FlyBehavior{
@Override
public void fly() {
System.out.println("I can't fly");
}
}
public class Quack implements QuackBehavior{
@Override
public void quack() {
System.out.println("quack!");
}
}
public class MuteQuack implements QuackBehavior{
@Override
public void quack() {
System.out.println("<<silence>>");
}
}
public class Squeak implements QuackBehavior{
@Override
public void quack() {
System.out.println("Squeak!");
}
}
public abstract class Duck {
protected FlyBehavior flyBehavior;
protected QuackBehavior quackBehavior;
abstract void display();
public void performFly()
{
flyBehavior.fly();
}
public void performQuack()
{
quackBehavior.quack();
}
}
public class MallardDuck extends Duck{
@Override
public void display() {
System.out.println("I'm a real mallard duck");
}
public MallardDuck(){
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
}
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallardDuck = new MallardDuck();
mallardDuck.performFly();
mallardDuck.performQuack();
}
}
动态设定行为
在鸭子子类中为两个behavior加入set方法,而不是在构造器中进行实例化。有了这个,我们就能在运行时随时改变鸭子的行为。
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
整体设计
现在我们来看看整体结构

我们不再把鸭子的行为说成是行为,而是一族算法。算法代表鸭子能做的事情。在本例中,我们鸭子的行为是组合来的,而不是继承来的。
我们得到第三个OO设计原则
多用组合,少用继承
学习完以上部分,我们正式定义策略模式

[head first 设计模式] 第一章 策略模式的更多相关文章
- 设计模式之第8章-策略模式(Java实现)
设计模式之第8章-策略模式(Java实现) “年前大酬宾了啊,现在理发冲500送300,冲1000送500了.鱼哥赶紧充钱啊,理发这事基本一个月一回,挺实惠的啊.不过话说那个理发店的老板好傻啊,冲10 ...
- 第 1 章 策略模式【Strategy Pattern】
第 1 章 策略模式[Strategy Pattern] 以下内容出自: 24种设计模式介绍与6大设计原则.pdf 刘备要到江东娶老婆了,走之前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开解决棘手 ...
- Python设计模式: 最佳的"策略"模式实践代码
Python设计模式: 最佳的"策略"模式实践代码 今天抽空看了下流畅的python,发现里面介绍了不少python自带的库的使用实例,用起来非常的优雅. 平时用Python来写爬 ...
- 第21章 策略模式(Strategy Pattern)
原文 第21章 策略模式(Strategy Pattern) 策略模式 导读:策略模式看完之后,大多数人都会感觉有点混了,包括我,感觉策略模式是一种OO思想的体现(纯属个人拙见). 概述: ...
- 《Head First 设计模式》[01] 策略模式
<Head First 设计模式>(点击查看详情) 1.写在前面的话 之前在列书单的时候,看网友对于设计模式的推荐里说,设计模式的书类别都大同小异,于是自己就选择了Head First系列 ...
- 【转】设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成 ...
- 设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕 ...
- javascript设计模式--策略模式
javascript策略模式总结 1.什么是策略模式? 策略模式的定义是:定义一系列的算法,把他们独立封装起来,并且可以相互替换. 例如我们需要写一段代码来计算员工的奖金.当绩效为a时,奖金为工资的5 ...
- Head First 设计模式 第1章 策略模式
本章从浅入深的讲解了策略模式的使用,以及策略模式中所涉及到的几个设计原则,在本章的最后给出了策略模式的定义. 1.定义及优点 什么是策略模式呢? 答:定义算法族(对象),分别封装起来,让他们之间可以相 ...
随机推荐
- linux(centos8):firewalld的运行时日志配置
一,firewalld配置日志的用途: 在生产环境中,firewalld的默认配置是不记录日志 我们通过日志记录下防火墙过滤时拒绝的非法ip, 可以主动把这些有攻击性的ip加入到黑名单, 防患于未然 ...
- C++ Primer第5版 第二章课后练习
练习2.1 C++ 语言规定short 和 int 至少 16 位, long 至少32位, long long 至少64位.带符号类型可以表示整数.负数或0, 无符号类型则仅能表示大于等于0的值Th ...
- 【多次实践】win10+ubuntu18.04lts双系统安装葵花宝典(安装篇)
这个教程诞生的缘由很简单,吃的太饱,硬是要折腾,结果,这一折腾便是20余小时,故写此文,帮助后来者少走弯路! 在本文开始,请先允许我对网上很多类似的教程嗤之以鼻,很成功地让我走了很多的弯路,一些有效简 ...
- ServletResponse使用介绍
ServletResponse为将响应发送到客户端的对象:Servlet 容器创建 ServletResponse 对象,并将它作为参数传递给Servlet的service 方法,如下图 : Http ...
- JS 计算日期相减得天数
言简意赅不呼哨直接懂,可以封装的可以根据自己的需求封装一下 var date1="2020-10-23";var date2="2020-10-26";var ...
- java中true是关键字吗
java中的关键字有哪些? 答:1)48个关键字:abstract.assert.boolean.break.byte.case.catch.char.class.continue.default.d ...
- 通过Relect反射方法创建对象,获得对象的方法,输出对象信息
package reflects; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java. ...
- 如何在 vue 项目里正确地引用 jquery
转载 2016年11月13日 使用vue-cli构建的vue项目,webpack的配置文件是分散在很多地方的,而我们需要修改的是build/webpack.base.conf.js,修改两处的代码 / ...
- 隐马尔科夫模型 HMM(Hidden Markov Model)
本科阶段学了三四遍的HMM,机器学习课,自然语言处理课,中文信息处理课:如今学研究生的自然语言处理,又碰见了这个老熟人: 虽多次碰到,但总觉得一知半解,对其了解不够全面,借着这次的机会,我想要直接搞定 ...
- 需要加强NB-IoT网络优化和终端监管
NB-IoT解决方案在行业规模商用应用中得到了持续的验证,承受住考验,现在芯片和网络问题和障碍已经得到解决. 从统计结果来看,目前的绝大多数问题都集中在终端侧,因为行业的定制化需求以及合伙伙伴的能力差 ...