设计模式 c++版(13)——策略模式
定义:
定义一组算法,将每个算法都封装起来,并且使他们之间可以互换
示例一:策略模式(通用版)
1. 类图18-3
2. 类图说明
策略模式使用的就是面向对象的继承和多态机制
- Context 封装角色。也叫上下文角色,起承上启下的封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
- Strategy 抽象策略角色。策略、算法家族的抽象,通常为借口,定义每个策略或算法必须具有的方法和属性。
- ConcreteStrategy 具体策略角色。实现抽象策略中的操作,该类含有具体的算法。
3. 代码清单18-3
//////////////////////// ********** 1. 策略模式(通用版),代码清单18-3:***************//
//抽象的策略角色
class Strategy
{
public:
virtual void operate() = 0;
};
//具体策略角色
class ConcreteStrategy1: public Strategy
{
public:
virtual void operate(){qDebug() << "ConcreteStrategy1";}
};
//吴国太开绿灯
class ConcreteStrategy2: public Strategy
{
public:
virtual void operate(){qDebug() << "ConcreteStrategy2";}
};
//封装角色
class Context
{
public:
Context(Strategy* strategy)
{
this->m_strategy = strategy;
}
void operate()
{
this->m_strategy->operate();
}
private:
Strategy *m_strategy;
};
//使用计谋
int main()
{
Strategy *gy1 = new ConcreteStrategy1();
Strategy *gy2 = new ConcreteStrategy2();
Context *context = new Context(gy1);
context->operate();
context = new Context(gy2);
context->operate();
return 0;
}
4. 代码分析
策略模式就是采用了面向对象的继承和多态机制。在真实的业务环境中,需要看清楚哪个接口是抽象策略接口,哪些是和策略模式没有任何关系的。
示例二:三国策略
1. 类图18-1
2. 类图结构
- IStrategy :策略类总称
- BackDoor:乔国老开后门
- GivenGreenLight:吴国太开绿灯
- BlockEnemy:孙夫人断后
3.增加锦囊和执行人,类图18-2
4.代码清单
////////////////////// ********** 2. 三国策略,代码清单18-2:***************//
//妙计接口
class IStrategy
{
public:
virtual void operate() = 0;
};
//乔国老开后门
class BackDoor: public IStrategy
{
public:
virtual void operate(){qDebug() << "BackDoor";}
};
//吴国太开绿灯
class GivenGreenLight: public IStrategy
{
public:
virtual void operate(){qDebug() << "GivenGreenLight";}
};
//孙夫人断后
class BlockEnemy: public IStrategy
{
public:
virtual void operate(){qDebug() << "BlockEnemy";}
};
//锦囊
class Context
{
public:
Context(IStrategy* strategy)
{
this->m_strategy = strategy;
}
void operate()
{
this->m_strategy->operate();
}
private:
IStrategy *m_strategy;
};
//使用计谋
int main()
{
IStrategy *backDoor = new BackDoor();
IStrategy *greenLight = new GivenGreenLight();
IStrategy *block = new BlockEnemy();
Context *context = new Context(backDoor);
context->operate();
context = new Context(greenLight);
context->operate();
context = new Context(block);
context->operate();
return 0;
}
三、建造者模式的应用
1. 优点
- 算法可以*切换。这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装,保证对外提供“可*切换”的策略。
- 避免使用多重条件判断。多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。
- 扩展性良好。在现有的系统中增加一个策略很容易,只要实现接口就可以了,其他不用修改,类似于一个可反复拆卸的插件。
2. 缺点
- 策略类数量增多。每一个策略都是一个类,复用的可能性很小,类数量增多。
- 所有的策略类都需要对外暴露。上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则相违背,这一缺点可以使用其他模式来修补,如工厂方法模式、代理模式或享元模式。
3. 使用场景:
- 多个类只有在算法或行为上稍有不同的场景。
- 算法需要*切换的场景。如算法的选择由使用者决定。
- 需要屏蔽算法规则的场景。如太多的算法只要知道一个名字,传递相关的数字进来,反馈一个运算结果就可以。
4. 注意事项:
如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就会很麻烦。
四、策略模式的扩展
1. 需求说明
输入3个参数,进行加减运算,参数中两个是int,一个是QString,只有“+”、“-”两个符号可以选择,不考虑校验
示例三:算数加减法(方案一)
2. 代码清单18-3
//////////////////////// ********** 3. 算数加减法(方案一),代码清单18-3:***************//
class Calculator
{
public:
int exec(int a, int b, QString symbol)
{
int result = 0;
if (symbol == this->m_add)
{
result = this->add(a, b);
}
else if (symbol == this->m_sub)
{
result = this->sub(a, b);
}
return result;
}
private:
int add(int a, int b){return a + b;}
int sub(int a, int b){return a - b;}
private:
static QString m_add;
static QString m_sub;
};
QString Calculator::m_add = "+";
QString Calculator::m_sub = "-";
int main()
{
int a = 10;
int b = 20;
Calculator cal;
qDebug() << cal.exec(a, b, "+");
return 0;
}
示例四:算数加减法(方案二)
2. 代码清单18-3,( 简化Calculator )
//////////////////////// ********** 4. 算数加减法(方案二),代码清单18-4:***************//
class Calculator
{
public:
int exec(int a, int b, QString symbol)
{
return (symbol == m_add)? a+b:a-b;
}
private:
int add(int a, int b){return a + b;}
int sub(int a, int b){return a - b;}
private:
static QString m_add;
static QString m_sub;
};
QString Calculator::m_add = "+";
QString Calculator::m_sub = "-";
int main()
{
int a = 10;
int b = 20;
Calculator cal;
qDebug() << cal.exec(a, b, "+");
return 0;
}
示例五:算数加减法(方案三)
2. 代码清单18-3,( 引入策略模式 )
////////////// ********** 5. 算数加减法(方案三:策略模式),代码清单18-5:***************//
class Calculator
{
public:
virtual int exec(int a, int b) = 0;
};
class Add:public Calculator
{
public:
virtual int exec(int a, int b)
{
return a + b;
}
};
class Sub:public Calculator
{
public:
virtual int exec(int a, int b)
{
return a - b;
}
};
class Context
{
public:
Context(Calculator *cal)
{
this->m_cal = cal;
}
int exec(int a, int b)
{
return this->m_cal->exec(a, b);
}
private:
Calculator *m_cal;
};
int main()
{
int a = 10;
int b = 20;
Calculator *cal = new Add();
Context context(cal);
qDebug() << context.exec(a, b);
return 0;
}
六、最佳实践
策略模式是一个非常简单的模式。他在项目中使用的非常多,但单独使用的地方比较少,因为有致命缺陷:所有的策略都需要暴露出去,这样才方便客户端决定使用哪一个策略。
参考文献《秦小波. 设计模式之禅》(第2版) (华章原创精品) 机械工业出版社