《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. shell 多进程

    shell 多进程来模拟多线程 (1){ } 建立代码块 (2)使用 & 将进程放入后台 [zheng@localhost ~]$ cat threads.sh #!/bin/bash ;i& ...

  2. 【Mybatis】 JdbcType 与 JavaType对应关系

    一.MyBatis 通过包含的jdbcType类型 BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED TINYINT REAL VARCHAR BINARY BLOB ...

  3. 编写第一个spring MVC程序

    一.下载和安装spring框架 进入http://repo.springsource.org/libs-release-local/org/springframework/spring/4.2.0.R ...

  4. asp.net mvc 接入阿里大于 短信验证码发送

    项目前端页面实例 第1步:登录阿里大于控制台 https://www.alidayu.com/center/user/account?spm=0.0.0.0.P1K1jG 第2步:创建应用 第3步:配 ...

  5. hdu3720 Arranging Your Team

    Arranging Your Team Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  6. JAVA奇技淫巧简化代码之lombok

    背景 最近在做一个小功能,又不想在原有体态臃肿的项目中追加,为了调试方便并且可以快速开发就采用了springboot.由于使用了JPA,建了几个类,然后通过IDE去生成其属性的构造器.getter.s ...

  7. 部分iphone手机上的div的点击事件无效

    最近在做一个手机移动端的项目,Andrio手机和部分iphone手机上点击事件都是好的,只有在老的型号上的iphone手机上点击事件无效果. 后来在网上查了很多资料,发现有加样式cursor:poin ...

  8. windows下命令行模式中cd命令无效的原因

    当我们执行cmd 想切换当前工作目录时,会发现windows下命令行模式中cd命令没有生效,到底是什么原因呢? 例如: 当我们想切换到 D:\MySql\mysql-5.7.19-winx64\bin ...

  9. Json作为配置文件注意事项

    错误描述 在一次开发中,使用了JSON数据作为配置文件,但反序列化时总是出错,开始还以为是转义字符的问题,因为存了一个绝对路径(D:\xx\xx.json),后来测试发现竟然发现是类嵌套的问题. 解决 ...

  10. LAMP一键安装

    author:JevonWei 版权声明:原创作品 #!/bin/bash 定义变量 export MDB=$(rpm -qa *mariadb*) export HTT=$(rpm -qa *htt ...