场景引入
假如你在A城市,要去B城市旅游,交通方式有以下几种选择:
- 驾车
- 火车
- 飞机
不难写出这样的代码:
void transport(string method)
{
if (method == "drive")
{
// 处理驾车相关业务逻辑
}
else if (method == "train")
{
// 处理乘坐火车相关业务逻辑
}
else
{
// 处理乘坐飞机相关业务逻辑
}
}
想一想,有什么问题?
屏幕前的你应该已经想到了,如果增加一种交通方式的话,是不是得增加if-else分支?这样就需要大量的修改,不符合开闭原则。
那怎么样能做到在不对源代码直接修改的前提下增加交通方式呢?答案是:把不同交通方式的业务逻辑抽离出来单独做成接口。
首先我们要定义一个基类Transportation
,作为这几个交通方式的父类。这个Transportation
有个纯虚函数run
,从Transportation
派生出Drive
、Train
、Plane
等子类(叫做策略类),重写run
函数。这时的类体系如下:
代码如下:
class Transportation
{
public:
virtual void run() = 0;
}
class Drive : public Transportation
{
public:
void run() override
{
// 处理驾车相关业务逻辑
}
}
class Train : public Transportation
{
public:
void run() override
{
// 处理乘坐火车相关业务逻辑
}
}
class Plane : public Transportation
{
public:
void run() override
{
// 处理乘坐飞机相关业务逻辑
}
?
此时多态就体现出来了,使用不同的类,调用run
函数,就会做出不同的行为。
那怎么样来使用这些策略类呢?我们再新增一个类Context
,可以根据传入的策略类做相应的处理:
class Context
{
Transportation &strategy;
public:
explicit Context(Transportation &strategy) : strategy(strategy) { }
void run()
{
strategy.run();
}
}
定义
策略模式(Strategy Pattern):一个类的行为或其算法可以在运行时更改的设计模式。
主要使用场景
有多种算法,希望避免因为多个if-else分支而带来的难以维护和扩展的问题。
优缺点
优点
- 可维护性、可扩展性高。
- 对客户隐藏了具体的实现细节
缺点
- 客户端必须要知道所有的策略类,并自行选择要用哪一个策略类。