设计模式第3式:策略模式

news/2024/11/7 19:19:40/

前言

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

正文

《head first设计模式》给的例子很好,它通过改造了一个Duck功能,将“行为”抽象成接口,每一个接口及其实现类组成一个算法组,每个算法组中的算法可以无缝替换。

通过这个例子,还告诉了我们一个道理:“有一个”关系可能比“是一个”关系更好。

让我们来看这个例子,有一个Duck父类,里面有一些行为方法,具体的鸭子类实现接口。

class Duck {quack();swim();display();
}

具体的鸭子:

class MallardDuck {display() {// 具体实现}
}

但是现在我们想给Duck类加功能,比如让鸭子能飞,于是我们自然想的是在父类中加一个fly()方法:

class Duck {quack();swim();display();fly(); // 在父类新增方法
}

但是,并不是所有鸭子都能飞。当代码设计到维护时,为了复用目的而使用继承,结局并不完美。

于是开发者又想到一招,将fly()和quack()方法抽成接口,让实现类去实现这2个接口。但是这样每当有新的鸭子子类出现时,都要检查是否要实现这个接口,而且重复代码会增多。

此时引出第一个设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

我们知道Duck类中的fly()和quack()会随着鸭子的不同而不同,为了把这两个行为从Duck类中分开,我们将它们从Duck类中取出来,建立一组新类来代表每个行为。

我们希望一切能够有弹性,换句话说,我们应该在鸭子类中加一个设定行为的方法,这样就可以在运行时动态地改变鸭子的行为。

此时引出第二个设计原则:针对接口编程,而不是针对实现编程。

我们利用接口代表每个行为,比如FlyBehavior与QuackBehavior,而行为的每个实现类都将实现其中一个接口。这次鸭子类不再负责实现Flying与Quacking接口,反而是由我们制造一组其他类专门实现FlyBehavior与QuackBehavior,称为行为类。这和以前的做法迥然不同,以前的做法是:行为来自Duck超类的具体实现类,或者继承某个接口并由子类自行实现而来,这两种方式都是依赖于“实现”,我们被实现绑得死死的,没有办法更改行为。

在新设计中,鸭子的子类将持有接口所表示的行为,所以实际的“实现”不会被绑死在鸭子类中。我们来看新设计是什么样的:

首先定义行为,下面是2组行为,也称算法组:

interface FlyBehavior {fly();
}class FlyWithWings implements FlyBehavior {fly() {// 实现鸭子飞行}
}class FlyNoWay implements FlyBehavior {fly() {// 什么都不做,不会飞}
}

另一组:

interface QuackBehavior {quack();
}class Quack implements QuackBehavior {quack() {// 实现鸭子呱呱叫}
}class Squeak implements QuackBehavior {quack() {// 橡皮鸭吱吱叫}
}class MuteQuack implements QuackBehavior {quack() {// 什么都不做,不会叫}
}

我们再来整合鸭子的行为,关键在于:鸭子现在会将飞行和呱呱叫的动作“委托”给别人处理,而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法。

首先在Duck类中加入两个成员变量

public class Duck {FlyBehavior flyBehavior;QuackBehavior quackBehavior;public void performQuack() {quackBehavior.quack();}public void performFly() {flyBehavior.fly();}// 动态设定行为public void setFlyBehavior(FlyBehavior fb) {this.flyBehavior = fb;}public void setQuackBehavior(QuackBehavior qb) {this.quackBehavior = qb;}
}

添加测试类:

public static void main(String[] args) {Duck model = new ModelDuck();model.performFly();model.setFlyBehavior(new FlyRocketPowered());model.performFly();
}

http://www.ppmy.cn/news/9082.html

相关文章

【韩顺平Linux】学习笔记3

【韩顺平Linux】学习笔记3一、文件目录指令pwd指令 ls指令cd指令mkdir指令rmdir指令touch指令cp指令rm指令mv指令cat指令more指令less 指令echo指令 head指令tail指令> 指令 >>指令ln指令history指令二、时间日期指令三、查找指令四、压缩和解压一、文件目录指令 根目…

电子游戏销售之回归模型与数据可视化

电子游戏销售之回归模型与数据可视化 文章目录电子游戏销售之回归模型与数据可视化0、写在前面1、回归模型1.1 模型建立准备1.2 建立模型1.3 模型分析2、数据可视化3、参考资料0、写在前面 该篇文章的任务包括以下3个方面 检测与处理缺失值建立回归模型数据可视化 实验环境 Pyt…

【数据结构】LeetCode移除链表元素、反转链表、链表的中间结点

目录 一、移除链表元素 1、题目说明 2、题目解析 二、反转链表 1、题目说明 2、题目解析 三、链表的中间结点 1、题目说明 2、题目解析 一、移除链表元素 1、题目说明 题目链接:移除链表的元素 给你一个链表的头节点 head ,和一个整数 val,…

DALLE2-文本图像生成

文章目录摘要算法解码器prior图像处理变体插值文本差异限制论文: 《Hierarchical Text-Conditional Image Generation with CLIP Latents》github: https://github.com/lucidrains/DALLE2-pytorchhttps://github.com/LAION-AI/dalle2-laion摘要 CLIP已经…

【JDK工具】jinfo、jps、jstack、jstat、jmap

目录一、前言二、关键工具2.1 jps 显示所有JAVA进程信息1. 参数信息2. 常用命令2.2 jinfo 查看虚拟机配置参数信息1. 查看虚拟机参数 jinfo -flags pid2. 查看虚拟机指定参数 jinfo -flag 具体参数 pid3. 查看环境变量 jinfo -sysprops pid4. 参数列表2.3 jstack1. 能排查哪些问…

Springboot扩展点之BeanDefinitionRegistryPostProcessor

前言通过这篇文章来大家分享一下,另外一个Springboot的扩展点BeanDefinitionRegistryPostProcessor,一般称这类扩展点为容器级后置处理器,另外一类是Bean级的后置处理器;容器级的后置处理器会在Spring容器初始化后、刷新前这个时间…

前端面试常考 | js原型与原型链

文章目录一. 什么是原型?二. 什么是原型链?一. 什么是原型? 在js中所有的引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象。 而在js中的引用类型包括:Object,Array,Date,Function 而所有函数都有…

Lc.152 乘积最大子数组

题目链接1 前言翻译成大白话:就是找一个数组,其连续子数组的乘积最大值。2 算法思路:一般求最值的问题首选动态规划。这道题与[LC.53 最大子序和]很类似。我们假设状态转移方程为:它表示以第 i 个元素结尾乘积最大子数组的乘积可是在这里&…