策略模式(行为型模式)
模式概述
策略模式是一种行为设计模式,它通过定义一系列操作,并将每种操作放到独立的类中,以使操作的对象能够相互替换。
问题
李星云最近打算做一款基于娆疆的导游应用,它可以提供精准的地图,帮助后来者快速地找到十二峒所在。
程序的首个版本只能规划高速公路路线,因此自驾游的用户非常满意。但很显然,并非所有人都打算开车去。所以他在新版本中加入了骑行路线,后来又添加了步行导航路线。
由于应用越来越受欢迎,不久之后,需要为娆疆中的所有景点规划路线了。
尽管从商业角度看它成功了,但现在李星云却十分头疼,因为在每次添加新的路线规划算法后,应用中主要类的体积就会增加一倍。终于,他觉得自己已经没法继续维护这堆代码了。
方案
策略模式通过将以不同算法或操作封装到不同的策略类中,实现功能的独立。
然后,一个名为上下文的原始类包含每种策略的引用,上下文并不执行具体操作,只是将工作委派给策略对象而已。
客户端会将所需策略传递给上下文,上下文与所有策略通过接口交互,该接口只需暴露一个方法来触发所选策略中封装的操作。
这样,上下文独立于具体策略,就可在不修改代码或其他策略的情况下添加新的操作或修改已有操作了。
结构

实现
Java
/**
* 支付策略接口
*
*/
public interface PayStrategy {
boolean pay(int paymentAmount);
void collectPaymentDetails();
}
/**
* 微信支付
*
*/
public class WeixinPay implements PayStrategy {
@Override
public void collectPaymentDetails() {
System.out.print("使用微信支付");
}
@Override
public boolean pay(int paymentAmount) {
System.out.print("使用微信支付");
}
}
/**
* 支付宝支付
*
*/
public class AliPay implements PayStrategy {
@Override
public void collectPaymentDetails() {
System.out.print("使用支付宝支付");
}
@Override
public boolean pay(int paymentAmount) {
System.out.print("使用支付宝支付");
}
}
/**
* 订单类
*
*/
public class Order {
public void processOrder(PayStrategy strategy) {
strategy.collectPaymentDetails();
}
}
/**
* 客户端
*
*/
public class Client {
private static boolean flag = true;
private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
private static Order order = new Order();
private static PayStrategy strategy;
public static void main(String[] args) throws IOException {
while (flag) {
if (strategy == null) {
System.out.println("请选择支付方式:" + "\n" +
"1 - 微信" + "\n" +
"2 - 支付宝");
String paymentMethod = reader.readLine();
if (paymentMethod.equals("1")) {
strategy = new WeixinPay();
} else {
strategy = new AliPay();
}
order.processOrder(strategy);
flag = false;
// 继续处理剩下的业务逻辑
// ......
}
}
}
}
Go
package main
import "fmt"
/**
* 缓存淘汰策略接口
*
*/
type EvictionAlgo interface {
evict(c *Cache)
}
/**
* 具体策略:FIFO
*
*/
type Fifo struct {
}
func (l *Fifo) evict(c *Cache) {
fmt.Println("FIFO策略")
}
/**
* 具体策略:LFU
*
*/
type Lfu struct {
}
func (l *Lfu) evict(c *Cache) {
fmt.Println("LFU策略")
}
/**
* 处理缓存
*
*/
type Cache struct {
storage map[string]string
evictionAlgo EvictionAlgo
capacity int
maxCapacity int
}
func initCache(e EvictionAlgo) *Cache {
storage := make(map[string]string)
return &Cache{
storage: storage,
evictionAlgo: e,
capacity: 0,
maxCapacity: 2,
}
}
func (c *Cache) setEvictionAlgo(e EvictionAlgo) {
c.evictionAlgo = e
}
func (c *Cache) add(key, value string) {
if c.capacity == c.maxCapacity {
c.evict()
}
c.capacity++
c.storage[key] = value
}
func (c *Cache) get(key string) {
delete(c.storage, key)
}
func (c *Cache) evict() {
c.evictionAlgo.evict(c)
c.capacity--
}
/**
* 客户端
*
*/
func main() {
lfu := &Lfu{}
cache := initCache(lfu)
cache.add("a", "1")
cache.add("b", "2")
cache.add("c", "3")
lru := &Lru{}
cache.setEvictionAlgo(lru)
cache.add("d", "4")
fifo := &Fifo{}
cache.setEvictionAlgo(fifo)
cache.add("e", "5")
}
适用场景
如果在系统中大多数类之间的区别仅在于行为,就可以使用策略模式动态地让一个对象在许多行为中选择一种。
当有诸多的判断条件,而每一种条件都对应一种操作时,可以使用策略模式来封装这些不同的操作。
如果希望将客户端与复杂的算法条件解耦,可以使用策略模式来提高算法的保密性与安全性。
如果算法在上下文的逻辑中不重要,可以使用策略模式将业务逻辑与算法实现隔离开来。
优缺点
策略模式的优点。
可以在不修改原有系统的基础上增加新的行为,符合
开闭原则
。可将算法的实现和使用算法的代码隔离。
可以使用组合来代替继承。
策略模式的缺点。
如果算法极少改变,那么没有任何理由引入新的类和接口,该模式只会让程序变得复杂。
客户端必须知晓策略间的不同并选择合适的策略,这是另一种耦合。
相关性
桥接模式
、状态模式
、策略模式
和适配器模式
较为相似,它们都基于组合模式
机制——将工作委派给其他对象来解决问题。命令模式
和策略模式
看上去很像,两者都能通过某些行为来参数化对象,但它们的意图不同:可以使用命令模式
来将任何操作转换为对象,而策略模式
通常用于描述完成操作的不同方式,且能够在同一个上下文中切换操作。修饰器模式
可以更改对象的外表,而策略模式
则能够改变其本质。模板方法模式
基于继承机制:它通过扩展子类中的部分内容来改变部分算法。而策略模式
基于组合机制:它通过对相应行为提供不同的策略来改变对象的部分行为。模板方法模式
在类层次上运作,它是静态的。而策略模式
在对象层次上运作,它是动态的。状态模式
可被视为策略模式
的扩展,两者都基于组合机制:都通过将部分工作委派给其他对象来改变其行为。策略模式
使得这些对象之间完全独立,它们不知道其他对象的存在;但状态模式
模式不限制具体状态之间的依赖,允许它们自行改变在不同上下文中的状态。
感谢支持
更多内容,请移步《超级个体》。