这两年,中国好声音席卷全国,深受观众的青睐,收视率也创造了新高。无论你是否身份低微或是高富帅,都可以通过好声音这个平台,实现自己的明星梦。紧随其后,中国好舞蹈等节目铺天盖地席卷而来,给选手一个圆梦的机会。如果想实现明星梦,可以参加中国好声音,也可以参加中国好舞蹈这些节目,还可以参加星光大道,或者像筷子兄弟一样,拍摄MV。实现明星梦的方式很多,根据自己的喜爱选择其中的一种方式。在设计模式中,也存在一种模式,实现某个功能有多种方式,每种方式对应一种算法,根据需要选择其中的一种方式来实现这个功能,称之为策略模式。
1、策略模式
在策略模式中,我们可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里,每一个封装算法的类我们都可以称之为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做规则的定义,而每种算法则对应于一个具体策略类。
策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合“针对抽象进行编程原则”。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。策略模式定义如下:
策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。 |
策略模式结构并不复杂,但我们需要理解其中环境类Context。
在策略模式结构图中包含如下几个角色:
Context(环境类):环境类是使用算法的角色,在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略,实际上环境类将调用策略类中实现算法的方法。环境类针对抽象策略进行编程,无需知道具体是哪一种策略,只要该策略类中实现了具体的算法方法就可以。在同一时刻,环境类只能使用一种策略,而不能使用多种。
Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。
策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列具体策略类里面,作为抽象策略类的子类。在策略模式中,对环境类和抽象策略类的理解非常重要,环境类是需要使用算法的类。
2、游戏武器系统的设计与实现
现需要开发一款射击类游戏,玩家可以选择斧头、手枪、AK47冲锋枪三种武器,每种武器都有不同的杀伤力等级,如斧头杀伤力6级,手枪杀伤力8级,AK47杀伤力10级。要求系统具有扩展性,如果有其他的武器,可以直接进行扩展,请使用策略模式进行设计。 |
可以设计一个抽象武器类,具体武器如斧头、手枪、AK47继承于抽象武器类。
武器类的实现代码如下:
#ifndef _WEAPON_H_
#define _WEAPON_H_
#include <iostream>
#include <string>
using namespace std;
//抽象武器类(抽象策略)
class Weapon
{
public:
//虚方法,使用武器打斗
virtual void Fight(string Player) = 0;
};
//手枪(具体策略)
class Gun : public Weapon
{
public:
void Fight(string Player, int nKillAbility)
{
cout << "游戏玩家:" << Player << " 战斗力:8级" << endl;
}
};
//Ak47冲锋枪(具体策略)
class Ak47 : public Weapon
{
public:
void Fight(string Player)
{
cout << "游戏玩家:" << Player << " 战斗力:10级" << endl;
}
};
//斧头(具体策略)
class Axe : public Weapon
{
public:
void Fight(string Player)
{
cout << "游戏玩家:" << Player << " 战斗力:6级" << endl;
}
};
#endif
Weapon是抽象策略类,具体武器Gun、Ak47、Axe是具体的策略类。每个具体策略实现抽象策略中打斗方法。
现在需要设计一个游戏玩家类,也就是环境类,游戏玩家来使用具体的某种武器。游戏玩家实现代码如下:
#ifndef _PLAYER_H_
#define _PLAYER_H_
#include <iostream>
#include <string>
#include "Weapon.h"
using namespace std;
//游戏玩家(环境类)
class Player
{
private:
//玩家名称
string m_strPlayer;
//玩家使用的武器
Weapon * m_pWeapon;
public:
//设置玩家名称
void SetPlayer(string strPlayer)
{
m_strPlayer = strPlayer;
}
//设置玩家使用的武器
void SetWeapon(Weapon * pWeapon)
{
m_pWeapon = pWeapon;
}
//玩家使用武器打斗
void Fight()
{
m_pWeapon->Fight(m_strPlayer);
}
};
#endif
游戏玩家Player维持一个武器类对象的引用。当玩家需要使用某种武器的时候,只需要把武器通过SetWeapon设置给玩家,玩家就可以通过使用这种武器进行打斗操作,实际上调用的仍是武器类的打斗方法。
测试代码实现如下:
#include <iostream>
#include "Player.h"
#include "Weapon.h"
using namespace std;
int main()
{
//创建Ak47武器对象
Weapon * pAk47 = new Ak47();
//创建玩家对象
Player * pPlayer = new Player();
pPlayer->SetPlayer("挑战者");
pPlayer->SetWeapon(pAk47);
//打斗
pPlayer->Fight();
//销毁操作
delete pPlayer;
pPlayer = NULL;
delete pAk47;
pAk47 = NULL;
return 0;
}
编译并运行,结果如下:
因为游戏玩家(环境类)针对抽象武器策略进行编程。如果需要更换武器策略,无需修改代码,只需要把具体武器策略设置给游戏玩家就可以了,可以随时切换武器。环境类不需要知道具体使用哪种策略,符合"针对抽象编程原则"。如果需要添加新的武器,只需要增加一个新的武器类,作为抽象武器类的子类,实现抽象类中的打斗方法。无需修改之前的代码,符合"开放封闭原则"。
3、策略模式总结
策略模式用于算法的自由切换和扩展,它是应用较为广泛的设计模式之一。策略模式对应于解决某一问题的一个算法族,允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换算法或者增加新的算法。只要涉及到算法的封装、复用和切换都可以考虑使用策略模式。如果需要添加新的策略,只需要增加一个新的策略类,作为抽象策略类的子类,实现抽象类中的方法,无需修改之前的代码,符合"开放封闭原则"。环境类针对抽象策略进行编程,如果需要更换,只需要把具体策略设置给环境类就可以了,可以随时切策略。环境类不需要知道具体使用哪种策略,符合"针对抽象编程原则。由于引进了环境类,客户端不需要直接操作具体策略,而是由环境类对策略进行操作处理。环境类起承上启下的作用,屏蔽高层模块对策略算法的直接访问,封装可能存在的变化。环境类和策略类是一种组合关系,通过组合实现对策略类代码的复用。
1.主要优点
策略模式的主要优点如下:
(1) 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
(2) 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。
(3) 使用策略模式可以避免多重条件选择语句。多重条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将它们全部硬编码在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。使用策略模式,if逻辑被封装到各个策略中。
(4) 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。
2.主要缺点
策略模式的主要缺点如下:
(1) 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。违背"迪米特法则,也就是和直接朋友通信原则",可以通过简单工厂模式来解决这个问题。
(2) 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
(3) 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。
3.策略模式具体应用
(1)电影院打折算法,成年人全票、VIP6折、儿童票与学生票0.5折优惠。
(2)数据导出:可以导出到数据库、也可以导出到Xml,或者文本文件中。比如:系统异常信息Log,可以记录到数据库也可以记录到文件中。再如:网页上的信息可以转为Word格式保存,也可以转为PDF格式保存。
(3)网络购物,可以选择货到付款、也可以选择U盾支付、还可以选择支付宝支付等支付方式。
(4)对于加密软件的开发,可以选择不同类型的加密算法,肯定不会只有一种加密算法。同样遍历二叉树,存在先序遍历。中序遍历、后序遍历、层序遍历等四种遍历算法,根据需要选择具体的某种遍历算法。还有数据结构中的各种排序算法,根据具体情况选择某个排序算法。
(5)QQ空间背景风格,可以根据需要选择某种喜欢的风格,用来装扮空间。
(6)UI容器管理器,用于管理各种类型控件布局方式。存在多种不同的布局方式,根据美观程度,选择某种布局方式。
(7)生活中的策略: 条条道路通罗马,36行行行出状元,描述的就是各种策略;人生面临着许多选择,路怎么走,该往哪里走。选择正确,可能一辈子飞黄腾达,选择错误,也许一辈就默默无闻了,这是一种人生选择策略。