《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 设计模式》[01] 策略模式的更多相关文章

  1. python设计模式之策略模式

    每次看到项目中存在大量的if else代码时,都会心生一丝不安全感. 特别是产品给的需求需要添加或者更改一种if条件时,生怕会因为自己的疏忽而使代码天崩地裂,哈哈,本文的目的就是来解决这种不安全感的, ...

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

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

  3. PHP设计模式之策略模式

    前提: 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查 找.排序等,一种常用的方法是硬编码(Hard Cod ...

  4. JavaScript设计模式之策略模式(学习笔记)

    在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...

  5. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  6. JavaScript设计模式之策略模式

    所谓"条条道路通罗马",在现实中,为达到某种目的往往不是只有一种方法.比如挣钱养家:可以做点小生意,可以打分工,甚至还可以是偷.抢.赌等等各种手段.在程序语言设计中,也会遇到这种类 ...

  7. 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查

    原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...

  8. [design-patterns]设计模式之一策略模式

    设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不 ...

  9. 设计模式入门,策略模式,c++代码实现

    // test01.cpp : Defines the entry point for the console application.////第一章,设计模式入门,策略模式#include &quo ...

  10. 设计模式之策略模式和状态模式(strategy pattern & state pattern)

    本文来讲解一下两个结构比较相似的行为设计模式:策略模式和状态模式.两者单独的理解和学习都是比较直观简单的,但是实际使用的时候却并不好实践,算是易学难用的设计模式吧.这也是把两者放在一起介绍的原因,经过 ...

随机推荐

  1. Object.assign简单总结

    定义 Object.assign方法用来将源对象source的所有可枚举属性复制到目标对象target.至少需要两个对象作为参数,第一个参数为源对象,后面的均为目标对象.(以下用source代指源对象 ...

  2. 【代码笔记】iOS-MBProgressHUD+MJ

    一,效果图. 二,工程图. 三,代码. ViewController.m #import "ViewController.h" #import "MBProgressHU ...

  3. 墨卡托投影、地理坐标系、地面分辨率、地图比例尺、Bing Maps Tile System

    GIS理论(墨卡托投影.地理坐标系.地面分辨率.地图比例尺.Bing Maps Tile System) 墨卡托投影(Mercator Projection),又名"等角正轴圆柱投影&quo ...

  4. JavaScript数组转字符串,字符串转数组

    //--------------------数组转字符串--------------------------- //需要将数组元素用某个字符连接成字符串 var a1, b1,c1; a1 = [&q ...

  5. [Objective-C] 从NSInteger说开去

    在iOS开发过程中,我一直习惯于使用C语法里的基本类型,而很少用(除非必须使用)Foundation的数据类型.最近看了一些资料,发现自己这样写可能有风险,虽然目前没遇到过相关的问题,但这是非常需要注 ...

  6. [Objective-C] Block实现回调和简单的学习思考

    初识Block的时候,总觉得其很可怕,因为看不懂其运行原理,所以用起来总是觉得不安全.关于Block的语法,等我把手里的资料全部看完,整理好再发出来.这次先看看用Block怎么实现回调. 新博客:wo ...

  7. 70部MAYA灯光材质渲染教程合集

    MAYA灯光材质渲染教程合集 教程格式:MP4和flv 两种格式 使用版本:教程不是一年出的教程,各个版本都有 (教程软件为英文版) 清晰度:可以看清软件上的文字 语言:部分中文字幕,其他英文(通过看 ...

  8. 万恶的浏览器缓存 Vuex state里面的成员改名后浏览器不会马上更新

    今天在用Vuex的时候,在state里面加了个名叫rootUrl的属性 但是怎么都取不到值,重新启动程序,ctrl+f5浏览器刷新都不行,纠结了大半上午,于是用console.log(store.ge ...

  9. 系统监控工具-glances

    作为运维不仅要时时监控系统,还需要优雅的装13,当别人还在命令的小黑框里敲打那个top命令去查看系统性能的时候,我们带你使用web端监控系统性能 glances 是利用Python语言开发,的监控工具 ...

  10. Active Directory、Exchange、单点登录,企业账号统一管理解决方案

    现在的公司一般都会有很多内部管理系统,比如OA.ERP.CRM.邮件系统等.员工入职之后如果每个系统都创建一个账号和密码,首先员工记系统账号就是一件非常头疼的事情,如果公司有一百个系统那就得创建一百个 ...