1 前言
上一篇文章中对“适配器模式”进行概述与总结,分别描述了适配器模式含义、特点、优缺点、适用场景以及实现过程步骤,并以C++语言实现具体例子。本文描述另一常用的设计模式——策略模式。
相关文章:
设计模式回顾——原型模式
设计模式回顾——观察者模式
设计模式回顾——模板模式
设计模式回顾——策略模式
设计模式回顾——适配器模式
设计模式回顾——建造者模式
设计模式回顾——工厂模式
设计模式回顾——单例模式
设计模式回顾——设计模式概念与基本原则
2 什么是策略模式
策略模式(Strategy Pattern),指的是定义一系列算法,并将这些算法封装到具有公共接口的一系列策略类中,使得它们可以动态自由切换。策略模式的本质是:分离算法,选择实现。
-
策略模式仅仅封装算法并提供接口,并不决定在何时、何地、使用何种算法;客户通过抽象接口访问具体算法
-
策略模式将算法的责任和算法本身进行解耦,将算法的使用权由不同的对象管理,客户不关心算法的具体实现
2.1 策略模式组成
策略模式由环境角色(Context)、抽象策略(Abstract Strategy)、具体策略(Concrete Strategy)、客户(Client)i四个要素组成。
- 环境角色(Context), 持有一个对
Abstract Strategy
的引用,最终由客户端调用 - 抽象策略(Abstract Strategy), 声明一个公共抽象接口,由不同算法类实现该接口;通过该接口,
Context
可以调用不同的算法 - 具体策略(Concrete Strategy), 继承
Abstract Strategy
类,并实现抽象接口,提供具体算法 - 客户(Client),客户通过调用
Context
调用策略算法,严格来说客户不属于策略模式的一部分
2.2 策略模式UML图
根据策略模式的组成要素,以及它们之间的关系,画出策略模式的UML图如下。
2.3 策略模式作用
- 让算法和对象分离,使得算法可以独立于使用它的客户而变化
- 客户端可以根据外部条件来选择不同策略来解决不同问题
- 替换
“if-else”
或者“switch-case”
臃肿逻辑代码
3 策略模式优缺点
优点:
-
灵活性好
提供管理相关算法族的方法,策略类(算法)可自由切换,而不影响客户使用。
-
易扩展
新增策略(算法)时,只需要添加一个具体的策略类即可,不需改动原有代码和代码框架,符合“开闭原则“。
-
代码结构清晰
可以避免使用多重条件转移语句(
“if-else”
、“switch-case”
),逻辑性强,可读性和可维护性好。
条件语句的不足:
多重条件转移语句不易维护,特别是语句越来越多的场景。条件转移语句一般是把算法种类、算法选择与算法行为、算法本身的逻辑混合在一起,增、删、改算法时都需改动大量的代码,维护性差。
不足:
-
暴露所有策略类
客户端必须知道所有的策略类,才能根据具体场景选择使用一个策略类。 -
导致产生众多策略类和对象
策略模式导致成产生众多策略类和对象,可通过亨元模式弥补该不足。
4 什么地方使用策略模式
策略模式的优点决定了其适用的场景,反过来其缺点即是其不适用的场景。策略模式适用场景:
-
一个需要多种算法处理的场景,并需要动态选择其中一个算法
-
一个有多种相似类的场景,类之间的区别仅仅是行为不同,可考虑使用策略模式使得对象动态选择一个行为
-
需要屏蔽算法规则的场景,不希望将算法相关数据结构、算法实现过程、算法原理等暴露给用户
-
重构历史遗留代码,以
if-else、switch-case
语句实现的,将算法行为和算法实现混合在一起的,或者多种行为混合一起的等维护性差的遗留代码,考虑使用策略模式重构
具体实例:
- null
5 策略模式实现
实例情况:
- 抽象策略类
Strategy
声明具体策略类访问接口AlgorithmInterface
- 环境角色
Context
提供算法访问方法ContextInterface
- 具体策略类
ConcreteStrategyA
、ConcreteStrategyB
、ConcreteStrategyB
分别实现三个不同行为(算法)具体方法AlgorithmInterface
- 用户
client
调用Context
方法选择三个策略类行为(算法)
实现过程:
- 第一步,声明抽象策略类
Strategy
/* strategy.h */
#ifndef _STRATEGY_H_
#define _STRATEGY_H_
class Strategy
{
public:
virtual void AlgorithmInterface() = 0; /* 抽象接口 */
};
#endif
- 第二步,声明环境角色类
context
/* context.h */
#ifndef _CONTEXT_H_
#define _CONTEXT_H_
#include "strategy.h"
class Context
{
public:
Context(Strategy *strategy);
void ContextInterface();
private:
Strategy *m_strategy;
};
#endif
- 第三步,环境角色方法实现
/* context.cpp */
#include "context.h"
#include <iostream>
using namespace std;
Context::Context(Strategy *strategy)
{
m_strategy = strategy;
}
void Context::ContextInterface()
{
cout << "Call Context::ContextInterface()" << endl;
m_strategy->AlgorithmInterface();
}
- 第四步,声明具体策略类
/* concrete_strategy.h */
#ifndef _CONCRETE_STRATEGY_H_
#define _CONCRETE_STRATEGY_H_
#include "strategy.h"
class ConcreteStrategyA : public Strategy
{
public:
void AlgorithmInterface();
};
class ConcreteStrategyB : public Strategy
{
public:
void AlgorithmInterface();
};
class ConcreteStrategyC : public Strategy
{
public:
void AlgorithmInterface();
};
#endif
- 第五步,具体策略类方法实现
/* concrete_strategy.cpp */
#include "concrete_strategy.h"
#include <iostream>
using namespace std;
void ConcreteStrategyA::AlgorithmInterface()
{
/* todo */
cout<<"Call ConcreteStrategyA::AlgorithmInterface()"<<endl;
}
void ConcreteStrategyB::AlgorithmInterface()
{
/* todo */
cout<<"Call ConcreteStrategyB::AlgorithmInterface()"<<endl;
}
void ConcreteStrategyC::AlgorithmInterface()
{
/* todo */
cout<<"Call ConcreteStrategyC::AlgorithmInterface()"<<endl;
}
- 第六步,客户调用具体策略算法
/* client.cpp */
#include "strategy.h"
#include "context.h"
#include "concrete_strategy.h"
int main(int argc, char **arv)
{
Strategy *strategyA = new ConcreteStrategyA;
Context *contextA = new Context(strategyA);
contextA->ContextInterface();
delete strategyA;
delete contextA;
Strategy *strategyB = new ConcreteStrategyB;
Context *contextB = new Context(strategyB);
contextB->ContextInterface();
delete strategyB;
delete contextB;
Strategy *strategyC = new ConcreteStrategyC;
Context *contextC = new Context(strategyC);
contextC->ContextInterface();
delete strategyC;
delete contextC;
return 0;
}
执行结果:
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design/strategy$ g++ -o client client.cpp concrete_strategy.cpp context.cpp
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design/strategy$ ./client
Call Context::ContextInterface()
Call ConcreteStrategyA::AlgorithmInterface()
Call Context::ContextInterface()
Call ConcreteStrategyB::AlgorithmInterface()
Call Context::ContextInterface()
Call ConcreteStrategyC::AlgorithmInterface()