《Head First 设计模式》(点击查看详情

1、写在前面的话

之前在列书单的时候,看网友对于设计模式的推荐里说,设计模式的书类别都大同小异,于是自己就选择了Head First系列,基于之前读过《Head First Java》,被文中生动的图片和讲解所吸引,相信这本书也能带给我更好的体验,关键在于,设计模式作为Java中更巧妙的部分,需要的不光是知道它是什么,更应该去理解为什么,才能更好地去掌握和应用,而Head First系列图书正是为了如此。

对于这本书的读书笔记,就以每个设计模式来分章进行记录吧,好了,不多叨叨,正式进入读书学习章节吧。

2、策略模式

我们知道,单纯利用继承来提供父类其下子类的各种行为,是难以维护的,我们无法确保所有子类的行为。比如我们有一只鸭子Duck作为父类,写下了fly()方法,可是涉及到例如玩具鸭ToyDuck作为子类的时候,它并不会飞啊?

那么这样,我们干脆把一些方法独立出来做成接口,比如一个Flyable的接口,让会飞的鸭子去实现它,玩具鸭不实现它不就行了,那么这个设计怎么样?

然而这个方法也并不是那么好,接口的方式虽然可以解决部分问题,可是接口不具有实现代码,这样一来,我们对于fly()就无法达到代码复用的目的,这意味着,如果某时我们需要修改一个方法(比如飞行是利用翅膀,随着科技化现在鸭子是用火箭飞行了),你必须从接口往下追踪并修改每个定义该行为的类,不仅工作量重复,更可能不小心造成新的错误。

2.1 剥离变化之处

为了解决这种问题,我们在设计之初要利用好一个设计原则:
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起 
1
 
1
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起 
(这个概念很简单,却几乎是每个设计模式的精神所在,所有模式都提供了一套方法让“系统中某部分改变不会影响其他部分”)

我们回到刚才的鸭子案例,随着鸭子的不同fly()可能不同,那么我们把它提取出来,建立新的类(接口)来代表这个行为


2.2 接口编程,独立实现

为了动态地决定鸭子的飞行行为,我们要利用到第二个设计原则:
针对接口编程,而不是针对实现编程
1
 
1
针对接口编程,而不是针对实现编程

我们利用接口来代表行为,比如FlyBehavior接口,这一次,我们不让鸭子类来实现这个接口,而是专门制造一组其他类来实现接口,这个类我们称之为“行为类”,即由行为类实现接口而不是Duck类来实现。这样一来,“实现”不必死死地捆绑在子类中。

这样的好处在于:
  • 动作可以被其他对象复用,因为独立为动作,而跟鸭子类无关了,比如麻雀也可以使用fly()了
  • 我们可以新增行为,且不影响既有行为类,也不影响使用飞行行为的鸭子类

2.3 整合

做完这些,你可能要问了,行为既然委托给别人了,不在鸭子身上了,那么我们如何整合鸭子的行为呢?

  • 将剥离部分作为实例变量,加入到类中(即把FlyBehavior接口作为变量设置到Duck类中)
  • 设置类似原来fly()的方法performFly(),调用已经作为实例变量的接口中定义的方法fly()
public class Duck {
protected FlyBehavior flyBehavior;
private String name;
private int size; public String performFly() {
return flyBehavior.fly();
} public String swim() {
return "Swimming ~ ~ ~";
}
}
13
 
1
public class Duck {
2
    protected FlyBehavior flyBehavior;
3
    private String name;
4
    private int size;
5

6
    public String performFly() {
7
        return flyBehavior.fly();
8
    }
9

10
    public String swim() {
11
        return "Swimming ~ ~ ~";
12
    }
13
}

  • 其他类实现行为接口
//通过翅膀飞行
public class FlyingByWings implements FlyBehavior { public String fly() {
return "Flying By Wings~";
}
}
7
 
1
//通过翅膀飞行
2
public class FlyingByWings implements FlyBehavior {
3

4
    public String fly() {
5
        return "Flying By Wings~";
6
    }
7
}
//通过火箭飞行
public class FlyingByRocket implements FlyBehavior { public String fly() {
return "Flying By super-cool Rocket!!!";
}
}
7
 
1
//通过火箭飞行
2
public class FlyingByRocket implements FlyBehavior {
3

4
    public String fly() {
5
        return "Flying By super-cool Rocket!!!";
6
    }
7
}

  • 子类初始化时设定想要的实例变量

public class RocketDuck extends Duck {

    public RocketDuck() {
flyBehavior = new FlyingByRocket();
} }
7
 
1
public class RocketDuck extends Duck {
2

3
    public RocketDuck() {
4
        flyBehavior = new FlyingByRocket();
5
    }
6

7
}

  • 编辑测试类
public class Test {
public static void main(String[] args) {
RocketDuck rocketDuck = new RocketDuck();
System.out.println(rocketDuck.performFly());
}
}
6
 
1
public class Test {
2
    public static void main(String[] args) {
3
        RocketDuck rocketDuck = new RocketDuck();
4
        System.out.println(rocketDuck.performFly());
5
    } 
6
}
 
另外,我们还可以在父类Duck中对行为接口设置setter,这样一来,我们随时都可以动态地设定方法了!

public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
3
 
1
public void setFlyBehavior(FlyBehavior flyBehavior) {
2
    this.flyBehavior = flyBehavior;
3
}
 

3、模式总结

回忆一下我们从最开始的想法到现在的方式:
  • 最开始,我们直接让子类继承父类来复用其方法,但是子类不同对于方法也不同,类的行为划分不清,同时方法覆盖的方式并不灵活;
  • 然后,改变成通过实现接口来实现行为,行为划分清晰,可无法使用代码复用,后期维护困难;
  • 最终,我们利用接口和多态原理,剥离行为,让一些专门的类来实现行为接口,让不同的子类去初始化不同的行为(实现类调用接口引用),完美解决了问题。

第三种的方式优秀在于,父类是 “有一个行为类型”,这种和继承不同的是,行为不是继承而来的,而是和适当的对象 “组合” 而来的,这里,也利用到了我么要说到的第三个设计模式:

多用组合,少用继承
1
 
1
多用组合,少用继承

最后,我们来看看策略模式的正儿八经的定义吧:

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

策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想。
x
 
1
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
2

3
策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想。
 
其适用性常在于:
  • 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种;
  • 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 / 时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式;
  • 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构;
  • 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

策略模式的场景示例:游戏中,每个角色只能使用一种武器,在游戏过程中可以更换武器:

 


4、本文涉及的设计原则汇总

  • 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
  • 针对接口编程,而不是针对实现编程
  • 多用组合,少用继承

5、更多相关好文推荐


6、文章示例源码下载


《Head First 设计模式》读书笔记(1) - 策略模式的更多相关文章

  1. head first 设计模式读书笔记 之 策略模式

    作为一个php开发者,深知曾经很多程序员都鄙视php,为什么呢?因为他们认为php的语法是dirty的,并且由于开发者水平参差不齐导致php的代码更加乱上加乱,维护起来简直一坨shit一样.随着php ...

  2. Head First 设计模式读书笔记(1)-策略模式

    一.策略模式的定义 策略模式定义了算法族,分别封装起来,让它们之间可以互换替换,此模式让算法的变化独立使用算法的客户. 二.使用策略模式的一个例子 2.1引出问题 某公司做了一套模拟鸭子的游戏:该游戏 ...

  3. HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern)

    策略模式(Strategy Pattern): 定义了了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户端. 第一个设计原则:找出应用中可能需要变化之处,把他们独立 ...

  4. HeadFirst设计模式读书笔记之策略模式

    1. 例子 1. 做一个鸭子模拟器,里面有很多不同的鸭子,有的可以游泳,有的可以睡觉,有的可以呱呱叫,一般套路是定义一个鸭子的超类,在 超类里定义睡觉,游泳,呱呱叫的方法,再让不同的鸭子子类继承这个超 ...

  5. 《JavaScript设计模式与开发实践》读书笔记之策略模式

    1.策略模式 定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换 1.1 传统实现 根据工资基数和年底绩效来发送年终奖 var calculateBonus= function (perfo ...

  6. HeadFirst设计模式读书笔记(4)-工厂模式

    工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到子类. 所有工厂模式都用来封装对象的创建.工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象 ...

  7. HeadFirst设计模式读书笔记之工厂模式

    1. 简单工厂 1. 你开了一家披萨店,点披萨的方法可能是这样: public Pizza orderPizza(String type) { Pizza pizza; if (type.equals ...

  8. Java 设计模式学习笔记1——策略模式(Duck例子)

    0.假设现有工程(Duck)中遇到为类添加功能的问题,如何设计类添加新的功能? 1.利用继承提供的Duck(鸭子)的行为会导致哪些缺点? (1)代码在多个子类中重复 (2)很多男知道所有鸭子的全部行为 ...

  9. HeadFirst设计模式读书笔记--目录

    HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern) HeadFirst设计模式读书笔记(2)-观察者模式(Observer Pattern) HeadFirst设计 ...

  10. JavaScript设计模式与开发实践 - 策略模式

    引言 本文摘自<JavaScript设计模式与开发实践> 在现实中,很多时候也有多种途径到达同一个目的地.比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路. 如果没有时间但 ...

随机推荐

  1. JavaScript基础语句

    一.前言: 有过学习其他语言的经历,会比较好上手.但是,没有学过也无关大雅.只要认真学,都会学好.毕竟,都只是一些基础的语句.关键在于要会运用. 掌握好语句的嵌套,以及要的逻辑思维能力,就已经成功一大 ...

  2. 【windows】常用快捷键

    浏览器 ctrl shift del 清除网页缓存 文件系统 win+e 打开文件窗口 win+r 运行命令窗口 win+l 锁定桌面 win+m 最小化窗口 ctrl+shift +n 创建文件夹

  3. 7.28.1 Spring构造注入还是设置注入

    1. 构造方法注入代码如下:public UserManagerImpl(UserDao userDao) {                                              ...

  4. mysql事件机制——定时任务

    定时任务是老生常谈了,因为我们总是需要定时修改特定的数据. 实现它的方法肯定不止一种,但我在相当长一段时间里都是用程序编码去做的,今天突然想到“为什么一定要采用调用的方式?”,用数据库自身的能力去实现 ...

  5. [2013-06-05]bat脚本设置DNS

    有时候需要切换本机dns,将网络环境转至测试环境 @echo off netsh interface ip set dns name="本地连接" source=static ad ...

  6. MongoDB副本集模式安装

    设备: 三个1G.20G.1核的虚拟机,系统是SentOS7 min 设置目录: Server1: mkdir -p /home/mongoshard/data/shard11 /home/mongo ...

  7. MySQL(五)DDL(数据定义语言)

    SHOW CREATE TABLE 表名\G前言 前面在数据库的讲解中,其实很多东西都非常的细节,在以前的学习过程中我都是没有注意到的.可能在以后的工作中会碰到所以都是做了记录的. 接下来,我将分享的 ...

  8. PHP发送邮件功能--ThinkPHP3.2.3

    首先第一步   :在网上down了一个PHPMailer插件,插件地址>https://github.com/PHPMailer/PHPMailer下载解压后,这里我们只需要用到其中两个文件,如 ...

  9. java初步—参数的值传递

    校招季,本人匆匆忙忙地参加各种宣讲会,几次笔试下来都遇到同一个题目,而且全都错在同一想法上,方知自己的基础实在不太牢固,因此特别写在博客上提醒自己要脚踏实地地学习!不多说了,题目如下: public ...

  10. 弹性布局flex

    前几天写过怪异盒子布局,以前在项目中用到弹性布局flex这个属性,当时没深入研究,这里各种查阅各种测试,把这个属性记录下 以免忘记, 弹性布局:是提供一种更加有效的方式来对一个容器中的条目进行排列.对 ...