前言
温故而知新
还是先复习学习到的行为型模式:
- 模板方法模式:定义一个算法骨架,特定的步骤延迟到子类实现(请客流程:点单-》吃-》买单,吃什么由子类负责)
- 命令模式:将命令封装成对象,聚合执行者,命令请求者仅需调用命令对象即可完成命令(遥控器按钮就是命令对象,按下按钮即可完成开关灯)
- 访问者模式:将施加于对象结构的元素上的操作隔离,封装成具体访问者,修改访问者并不会改动对象结构(购物车是一个对象结构,内部存放需要商品元素,顾客可以查看商品质量,收银员可以查看商品价格)
- 迭代器模式:将聚合对象的存储功能和遍历功能分隔,遍历功能封装成迭代器,聚合对象工厂方法创建对应的迭代器(迭代器遍历Linux目录)
- 观察者模式:定义对象间的一对多的依赖关系,当对象改变时,自动通知所有依赖的对象并自动更新对象数据(气象局天气数据改变,对应的APP的天气数据也会改变)
- 中介者模式:定义一个中介者封装对象间的交互关系,使对象间无显示的交互,可以通过中介对象访问任一其他对象(QQ聊天不需要知道对方的IP,通过服务器可以相互通信)
- 备忘录模式:定义一个备忘录角色,用于存储对象的内部状态,提供给对象恢复状态的功能(游戏的存档读档)
- 解释器模式:给分析对象定义一个语言,并定义该语言的文法表示,设计一个解释器解释语言中的句子(加减法解释器可以解释表达式字符串)
- 状态模式:允许对象在其内部状态改变时改变它的行为(进程的五个状态,每个状态都有自己的方法,能够切换到其他状态)
行为型模式有11种,接下来学习第10种设计模式:策略模式
现实中的问题
其实在现实生活中,可以感受到,当我们为了实现一个目标,会有多种策略可以选择
如,出行时可以选择走路、骑自行车、乘公交车。。。
存储对象,可以选择不同的数据结构:数组、List、Map、Set等等
这一种种选择,可以称为策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务
策略模式
什么是策略模式?
策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式
为什么要策略模式?
在软件系统中,有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法
当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…等条件判断语句来进行选择。
这两种实现方法我们都可以称之为硬编码(Hard Coding),如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难
为了解决硬编码的缺点,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,这就是策略模式
策略模式结构
策略模式角色:
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用
可以看出,策略模式其实很简单,仅仅通过聚合、抽象继承的方法解耦合
具体实现
简单的实现出行策略的选择
package com.company.Behavioral.Strategy;
//抽象策略类
abstract class Strategy {
protected String type;
public abstract void travel();
}
//具体策略类:步行
class Walk extends Strategy{
public Walk() {
type = "Walk";
}
@Override
public void travel() {
System.out.println(" 选择出行方式 :"+type);
}
}
//具体策略类:自行车
class Bicycle extends Strategy{
public Bicycle() {
type = "Bicycle";
}
@Override
public void travel() {
System.out.println(" 选择出行方式 :"+type);
}
}
//具体策略类:公交车
class Bus extends Strategy{
public Bus() {
type = "Bus";
}
@Override
public void travel() {
System.out.println(" 选择出行方式 :"+type);
}
}
//环境类
class TravelType{
private Strategy type;
public Strategy getType() {
return type;
}
public void setType(Strategy type) {
this.type = type;
}
public void travel(){
type.travel();
}
}
//客户类
class Client{
public static void main(String[] args) {
//创建环境类
TravelType travelType = new TravelType();
//选择一个出行策略:自行车
Strategy bicycle = new Bicycle();
travelType.setType(bicycle);
//出行
travelType.travel();
}
}
策略模式是一个很简单的模式
策略模式优缺点
优点
- 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为
- 策略模式提供了管理相关的算法族的办法
- 策略模式提供了可以替换继承关系的办法
- 使用策略模式可以避免使用多重条件转移语句
缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量
对于策略模式的思考
其实,看策略模式的结构,会发现挺简单的,就是定义好抽象策略类,实现多个具体策略对象,然后聚合到环境类中
- 策略模式需要客户明确需要的策略
- 策略模式仅仅是对算法的封装,根据客户的选择,提供策略的更新
策略模式与状态模式
他们两有点类似,一个是聚合状态对象,一个是聚合策略对象
区别在与:
- 客户需要明确选择哪个策略,而状态模式中客户无须关心具体的状态,只需知道在哪个状态调用哪个方法(策略模式客户类的压力更大)
- 策略模式环境类无须知道是哪个具体的策略;状态模式先需要确定一个初始状态,然后通过状态的方法来切换状态(状态模式状态的切换是在具体状态类中,策略模式是靠客户类切换策略)
- 状态模式强调的是不同状态下的不同行为;策略模式强调的是一个类的行为有多种实现策略,交由客户类选择一种
适用场景
从上面的策略模式的解析,可以总结策略模式的适用场景
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,动态的让一个对象在许多行为中选择一个
- 一个系统需要动态地在几种算法中选择一种
- 使用多重的条件选择语句来实现的一个类的行为的选择
- 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性
总结
- 策略模式:定义一系列算法并封装起来,让客户选择需要的算法
- 策略模式有是三个角色:抽象策略类、具体策略类、环境类
- 具体策略类是不同的算法封装;环境类聚合策略对象,交给客户调用
- 策略类的优点:对开闭原则的支持,可以在不修改系统就基础上修改算法,提高了代码复用性;缺点:增加了客户类的负担,需要明确具体策略类,会增加系统中的类
- 适用情况:系统中许多类的区别在与它们的行为;希望动态的让对象在许多行为中选择一个行为;避免使用难以维护的多重条件选择语句;希望封装算法和相关的数据结构
- 策略模式较为简单,强调的是一个类的行为有多种实现策略,交由客户类选择一种