内功心法专题-设计模式-15、策略模式
1. 是什么⭐️🔴
- 1)策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间==可以互相替换==。此模式让算法的变化==独立于使用算法的客户==,并委派给不同的对象对这些算法进行管理。
- 2)这算法体现了几个设计原则
- 第一、把变化的代码从不变的代码中分离出来
- 第二、针对接口编程而不是具体类(定义了策略接口)
- 第三、多用组合/聚合,少用继承(客户通过组合方式使用策略
2. 模式结构
策略模式的主要角色如下:
- 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
3. UML 图示
3.1. 原理图
说明:从上图可以看到,客户 Context
有成员变量 Strategy
或者其他的策略接口。至于需要使用到哪个策略,可以在构造器中指定
- 环境类 (Context)聚合不同的抽象策略类 (Strategy)
- Context构造函数导入抽象策略类 (Strategy)
- 客户端调用时传入 Context 构造函数具体策略类(Concrete Strategy)
4. 案例分析⭐️🔴
❕ ^zx08me
4.1. 商城促销

示例代码:[[SalesMan.java]]
❕
4.2. 主打特性
比如某个型号手机主打拍照策略,另一个型号主打游戏策略等
- 环境类 (Duck)聚合不同的抽象策略类 (XXXBehavior),并通过setter 方法引入
- 不同的环境类的子类 (XXXDuck) 继承 Duck 后,就能直接使用 Duck 聚合的 FlyBehavior 等策略接口
- 不同的环境类 (XXXDuck) 的子类通过调用各自的策略接口设置不同的 Behavior
示例代码:[[pages/002-schdule/001-Arch/001-Subject/013-DemoCode/DesignPattern/src/com/atguigu/strategy/improve/Duck.java]]
1 |
|
1 |
|
1 |
|
1 |
|
5. 优缺点⭐️🔴
5.1. 优点
- 策略类之间可以自由切换
由于策略类都实现同一个接口,所以使它们之间可以自由切换。 - 易于扩展
增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“,避免了使用多重转移语句(if...else if...else
) - 通过 set 方法动态替换
提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy
类中,使得我们可以独立于其Context
改变它,使它易于切换、易于理解、易于扩展
比如下面代码中的 pekingDuck set 了其他 flyBehavior1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static void main(String[] args) {
// TODO Auto-generated method stub
WildDuck wildDuck = new WildDuck();
wildDuck.fly();//
ToyDuck toyDuck = new ToyDuck();
toyDuck.fly();
PekingDuck pekingDuck = new PekingDuck();
pekingDuck.fly();
//动态改变某个对象的行为, 北京鸭 不能飞
pekingDuck.setFlyBehavior(new NoFlyBehavior());
System.out.println("北京鸭的实际飞翔能力");
pekingDuck.fly();
}
5.2. 缺点
- 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
6. 适用场景⭐️🔴
- 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
- 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
❕
7. JDK 源码分析⭐️🔴
7.1. Arrays 的 Comparator
❕Comparator
中的策略模式。在 Arrays 类中有一个 sort()
方法,如下:
1 |
|
Arrays 就是一个环境角色类,这个 sort 方法可以传一个新策略让 Arrays 根据这个策略来进行排序。就比如下面的测试类。
1 |
|
这里我们在调用 Arrays 的 sort 方法时,第二个参数传递的是 Comparator 接口的子实现类对象。所以 Comparator 充当的是抽象策略角色,而具体的子实现类充当的是具体策略角色。也就是我们自定义的匿名内部类 public int compare(Integer o1, Integer o2) {xxx}
。环境角色类(Arrays)应该持有抽象策略的引用来调用。那么,Arrays 类的 sort 方法到底有没有使用 Comparator 子实现类中的 compare()
方法吗?让我们继续查看 TimSort 类的 sort()
方法,代码如下:
1 |
|
上面的代码中最终会跑到 countRunAndMakeAscending()
这个方法中。我们可以看见,只用了 compare 方法,所以在调用 Arrays.sort 方法只传具体 compare 重写方法的类对象就行,这也是 Comparator 接口中必须要子类实现的一个方法。
8. 策略模式与桥接模式的区别⭐️🔴
❕
[[策略模式与桥接模式区别-pudn.com]]
8.1. 相同点
策略模式
桥接模式
核心逻辑都是使用了聚合 + 构造导入
8.2. 不同点
❕
8.2.1. 变化不同
在桥接模式中不仅 Implementor 具有变化(ConcreateImplementior),而且 Abstraction 也可以发生变化(RefinedAbstraction),而且两者的变化是完全独立的,RefinedAbstraction 与 ConcreateImplementior 之间松散耦合,它们仅仅通过 Abstraction 与 Implementor 之间的关系联系起来。而在策略模式中,并不考虑 Context 的变化,只有算法的可替代性。因此桥接模式一般比策略模式更加复杂。
8.2.2. 目的不同
- 在桥接模式中不仅定义 Implementor 的接口而且定义 Abstraction 的接口,Abstraction 的接口不仅仅是为了与 Implementor 通信而存在的,这也反映了结构型模式的特点:通过继承、聚合的方式组合类和对象以形成更大的结构。属于结构型模式,目的可以理解为高内聚。
- 在策略模式中,Startegy 和 Context 的接口都是两者之间的协作接口,并不涉及到其它的功能接口,所以它是行为模式的一种。行为模式的主要特点就是处理的是对象之间的通信方式,往往是通过引入中介者对象将通信双方解耦,在这里实际上就是将 Context 与实际的算法提供者解耦。属于行为型模式,目的可以理解为低耦合。
❕
8.2.3. 范畴不同
- 相对与策略模式,桥接模式要表达的内容要更多,结构也更加复杂。桥接模式表达的主要意义其实是接口隔离的原则,即把本质上并不内聚的两种体系区别开来,使得它们可以松散的组合,而策略在解耦上还仅仅是某一个算法的层次,没有到体系这一层次。
- 从结构图中可以看到,策略的结构是包容在桥接结构中的,桥接中必然存在着策略模式,Abstraction 与 Implementor 之间就可以认为是策略模式,但是桥接模式一般 Implementor 将提供一系列的成体系的操作,而且 Implementor 是具有状态和数据的静态结构。而且桥接模式 Abstraction 也可以独立变化。
8.2.4. 逻辑不同
聚合其他接口的类的子类,通过构造导入的方式,可以调用接口实现类的方法
9. 策略模式与状态模式的区别
- 策略模式有多个抽象策略类,而状态模式一般只有一个抽象状态类
- 策略模式不同策略之间不存在流转等其他关系,而状态模式不同状态之间需要流转
10. 实战经验⭐️🔴
- 1)策略模式的关键是:分析项目中变化部分与不变部分
- 2)策略模式的核心思想是:多用组合/聚合,少用继承;用行为类组合,而不是行为的继承,更有弹性
❕