《Head First设计模式》读书笔记_第一章

 

策略模式

例:设计一个模拟鸭子游戏,游戏中有各种鸭子,一边戏水一边嘎嘎叫。

所以学习设计模式前,我们最先想到的就是设置一个超类,并让其他子类去继承这个类,UML图如下:

*
*

但是,程序需求是会经常变动的,若给游戏中加入飞行方法以及玩具小黄鸭呢?并不是所有鸭子都可以飞,也并不是所有鸭子都是嘎嘎叫。

此时你可能会想,在父类添加相应方法,在子类重写就可以让不同鸭子的同一方法具有不同表现了!

然而当子类数量剧增时你都要到对应子类去逐个修改,这将是个复杂又无聊的工作!单独使用接口也同样会使代码量变多,出现许多重复性代码。

那么,还有其他更好的方式来添加飞行方法吗?

有,就是委托。我们可以把委托者和被委托者想成has-a(有一个)的关系。比如上面这个例子,鸭子(被委托者)有一个飞行行为(委托者)

把飞行想成Duck的一个功能,Duck具有该功能,即Duck本身含有飞行行为的实例,可以调用飞行行为的飞行方法。

找出易于变化的部分,把他们独立起来

把会变化的部分取出封装起来,好让其他部分不会受到影响。代码变化引起的不经意后果变少,系统将变得更有弹性。

针对接口编程,而不是针对实现编程

将会变化的部分放在类中,此类专门提供某行为接口的实现,这样主类就不需要知道行为的实现细节。

多用组合,少用继承

相应的UML图

实际代码

设计两个接口类让所有飞行类及所有叫声类都实现对应的接口
接口 FlyBehavior

1

2

3

public interface flyBehavior{

    public void fly();

}

接口QuackBehavior

1

2

3

public interface QuackBehavior{

    public void quack();

}

这样设计接口让飞行与叫声的动作可以被其他对象复用又可以新增一些行为类而不会影响既有的行为类,也不会影响到“使用”行为的类。

编写对应的类实现接口,让这些类负责实现具体的行为。

1

2

3

4

5

6

7

8

9

10

11

12

13

public class FlyNoWay implements FlyBehavior {

    @Override

    public void fly() {

        System.out.println("我不会飞");

    }

}

* * * * * * * * * * * * * * * * * * * * * * *分割线 * * * * * * * * * * * * * * * * * * * *

public class Quack implements QuackBehavior {

    @Override

    public void quack() {

        System.out.println("Quack");

    }

}

在此省略其他行为类的展示...

在Duck类中怎加两个实例变量,声明为我们刚刚建立的接口类型,这样Duck都能在运行时动态地设置这些变量并保证引用正确的行为类型。

Duck类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

//抽象化

public abstract class Duck {

//接口类型的实例变量

    QuackBehavior quackBehavior;

    FlyBehavior flyBehavior;

    public Duck() {

    }

    //设定display()方法为抽象的,让每个继承的类都必须实现它

    public abstract void display();

 

    public void performFly() {

    //将行为的实现委托给flyBehavior引用的对象

        flyBehavior.fly();

    }

    public void performQuack() {

    //将行为的实现委托给quackBehavior引用的对象

        quackBehavior.quack();

    }

    public void swim() {

        System.out.println("所有鸭子都能会游泳");

    }

    //建立一个seter,方便动态地设定行为

    public void setFlyBehavior(FlyBehavior fb) {

        flyBehavior=fb;

    }

    public void setQuackBehavior(QuackBehavior qb) {

        quackBehavior=qb;

    }

}

MallardDuck类:

1

2

3

4

5

6

7

8

9

10

11

public class MallardDuck extends Duck{

    public MallardDuck() {

        quackBehavior=new Quack();

        flyBehavior=new FlyWithWings();

    }

 

    public void display() {

        System.out.println("我是只野鸭");

    }

 

}

MiniDuckSimulator类:

1

2

3

4

5

6

7

8

9

10

11

12

public class MiniDuckSimulator {

    public static void main(String[] args) {

        Duck mini=new MallardDuck();

 

    /*这会调用MallardDuck类继承来的performFly方法,

     *进而委托给FlyBehavior对象处理(即调用继承来的

     *对象flyBehavior引用的FlyWithWings()。

     */

        mini.performFly();

        mini.performQuack();

    }

}

动态设定行为

模型鸭既不会飞也不会叫,但我们在Duck类中添加了seter方法以便动态设定行为,给模型鸭添加一个火箭助推器,让它飞起来。
ModelDuck类

1

2

3

4

5

6

7

8

9

10

11

public class ModelDuck extends Duck{

    @Override

    public void display() {

        System.out.println("我是模型鸭");

    }

    public ModelDuck() {

        flyBehavior= new FlyNoWay();

        quackBehavior= new Quack();

    }

 

}

对MiniDuckSimulator类稍作改动,

1

2

3

4

5

6

7

8

9

public class MiniDuckSimulator {

    public static void main(String[] args) {

        Duck model=new  ModelDuck();

        model.performFly();

        //FlyRocketPower()是继承飞行接口的一个飞行行为类

        model.setFlyBehavior(new FlyRocketPower());

        model.performFly();

    }

}

输出将是:

1

2

我不会飞;

我通过火箭飞起来了;

总结

策略模式的三项原则:

1、找出应用中可能需要变化之处,把它们独立出来,不要和那些无需变化的代码混在一起。

2、针对接口编程,而不是针对实现编程

3、多用组合,少用继承

分离变与不变。回忆之前的需求,可以发现,不变的是swim,变的是fly与quack。虽然swim和fly都是鸭子的行为,但是swim不怎么变化,它可以利用继承来实现代码复用,而飞行方式却有很多种,无法继承,因此需要将飞行行为抽出来,各自实现,让Duck实现类拥有飞行行为实例,达到操作飞行行为的目的。这种思想就像零件的组装,主体是不变的,零件有各种相同功能不同性能的各种款式,这样可以做出不同的产品。
另外策略模式还有一个好处是可以动态变化行为,比如木头鸭子原来是不会飞的,它用setFlyBehavior设置不会飞行,后来设计师给他装上了引擎翅膀,它可以再次通过setFlyBehavior在运行时动态变更飞行行为。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445475.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++基础学习(02)--(数据类型,变量类型,变量作用域,常量,修饰符类型)

文章目录目录一. 数据类型C 中的数据类型typedefenumeration枚举类型c中变量类型二.变量作用域三.常量四.修饰符类型目录 一. 数据类型 C 中的数据类型 使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着&a…

c++基础学习(03)--(存储类,运算符,循环,判断)

文章目录目录一.存储类二.运算符三.循环whilefor四.判断目录 一.存储类 可见static存储类修饰之后&#xff0c;i的值没有从头开始&#xff0c;而是从上一次的结果中保留下来 #include <iostream>using namespace std; class Data { public:Data(){}~Data(){}void show()…

皇后问题

八皇后问题是一个以国际象棋为背景的问题&#xff1a;如何能够在 88 的国际象棋棋盘上放置八个皇后&#xff0c;使得任何一个皇后都无法直接吃掉其他的皇后&#xff1f;为了达到此目的&#xff0c;任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的…

c++基础学习(04)--(函数、数字、数组、字符串)

文章目录目录1.函数2.数字3.字符串4.数组目录 1.函数 #include <iostream> #include <limits>using namespace std;void swap(int *x , int *y);int main(){int a 100 , b200;cout<<"交换前:"<<"a is :"<<a<<"…

【精品计划0】蓝桥杯 摔手机

原题描述&#xff1a; x星球的居民脾气不太好&#xff0c;但好在他们生气的时候唯一的异常举动是&#xff1a;摔手机。 各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试&#xff0c;并且评定出一个耐摔指数来&#xff0c;之后才允许上市流通。 …

数据结构课上笔记15

图的存储 多重链表&#xff1a;完全模拟图的样子&#xff0c;每个节点内的指针都指向该指向的节点。 节点结构内指针数为度 缺点&#xff1a;浪费空间、不容易操作 数组表示法&#xff08;邻接矩阵表示法&#xff09; 可用两个数组存储。其中一个 一维数组存储数据元素&#…

c++基础学习(05)--(指针,引用)

文章目录目录1.指针2.引用目录 1.指针 #include <iostream>using namespace std;int main () {int var1;char var2[10];cout << "var1 变量的地址&#xff1a; ";cout << &var1 << endl;cout << "var2 变量的地址&#xff…

由旅行商问题认识何为状态压缩

动态规划 动态规划(dynamic programming)是运筹学的一个分支&#xff0c;是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时&#xff0c;提出了著名的最优化原理(pri…

c++基础学习(06)--(时间,输入输出,数据结构)

文章目录目录1.时间2.输入输出数据结构目录 1.时间 当前日期和时间 下面的实例获取当前系统的日期和时间&#xff0c;包括本地时间和协调世界时&#xff08;UTC&#xff09;。 #include <iostream> #include <ctime>using namespace std;int main( ) {// 基于当前…

Abstract Self-Balancing Binary Search Tree

二叉搜索树 二叉查找树&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又&#xff1a;二叉搜索树&#xff0c;二叉排序树&#xff09;它或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结…

AVL Tree

前言 希望读者 了解二叉搜索树 了解左旋右旋基本操作 https://blog.csdn.net/hebtu666/article/details/84992363 直观感受直接到文章底部&#xff0c;有正确的调整策略动画&#xff0c;自行操作。 二叉搜索树 二叉查找树&#xff08;Binary Search Tree&#xff09;&a…

c++基础学习(07)--(类)

文章目录目录类与对象1.类成员函数2.类访问修饰符3.构造函数与析构函数4.拷贝构造函数5. 友元函数6.内联函数7.this指针8.指向类的指针9.类的静态成员目录 类与对象 #include <iostream>using namespace std;class Box {public:double length; // 长度double breadth;…

c++基础学习(08)--(继承、重载、多态、虚函数)

文章目录目录1.继承2.重载3.多态 && 虚函数目录 1.继承 #include <iostream>using namespace std;// 基类 class Shape {public:void setWidth(int w){width w;}void setHeight(int h){height h;}protected:int width;int height; };// 派生类 class Rectang…

图的应用

1. 图的应用总览 在数据结构中图的应用很广泛&#xff0c;本文主要从以下四个方面介绍&#xff1a; ①最小生成树&#xff1a;给定一个无向网络&#xff0c;在该网的所有生成树中&#xff0c;使得各边权数之和最小的那棵生成树称为该网的最小生成树&#xff0c;也叫最小代价…

c++基础学习(09)--(数据抽象、数据封装、接口)

文章目录目录1.数据抽象2.数据封装3.抽象接口类目录 1.数据抽象 数据抽象&#xff1a;就是把它当做黑箱子使用&#xff0c;内部实现与外部接口分开 C类实现数据抽象&#xff0c;如sort()函数&#xff0c;ostream的cout对象 #include <iostream> using namespace std…

c++基础学习(10)--(文件、流、异常处理、动态内存、命名空间)

文章目录目录1.文件和流2.异常处理3.动态内存4.命名空间目录 1.文件和流 注意 文件打开方式中的in和out都是相对于内存&#xff08;计算机&#xff09;而言的&#xff0c;计算机读取文件&#xff0c;是将数据从磁盘中的文件读入到内存中&#xff0c;所以用的是in #include &…

数据结构和算法(02)---字符串(c++)

文章目录目录一.c风格的字符串与操作函数1.c风格字符串2.c风格字符串处理函数二.c中的字符串与操作函数1.c中的string类2.string类的基本操作3.string类的操作汇总目录 数据结构&#xff1a; 逻辑结构&#xff1a;数组&#xff0c;栈&#xff0c;队列&#xff0c;字符串&#x…

如何学习数据结构和算法——大佬文章汇总

第一篇 第二篇、 作者&#xff1a;左程云 我分别说一下国内和国外的行情。 国内的话&#xff0c;一般来讲&#xff0c;工资高的公司在面试时算法和数据结构题目的比重较大&#xff0c;工资一般的公司比重较小。当然同样公司的不同岗位&#xff0c;要求也会不同&#xff0c;…

c++基础学习(13)--(STL、标准库)

文章目录目录1. STL教程2.标准库3.有用的资源目录 1. STL教程 #include <iostream> #include <vector> using namespace std;int main() {// 创建一个向量存储 intvector<int> vec; int i;// 显示 vec 的原始大小cout << "vector size " &…

哈夫曼实现文件压缩解压缩(c语言)

写一个对文件进行压缩和解压缩的程序&#xff0c;功能如下&#xff1a; ① 可以对纯英文文档实现压缩和解压&#xff1b; ② 较好的界面程序运行的说明。 介绍哈夫曼&#xff1a; 效率最高的判别树即为哈夫曼树 在计算机数据处理中&#xff0c;霍夫曼编码使用变长编码表对源…