策略模式(Strategy Pattern)
定义
策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式使算法的变化独立于使用算法的客户。——《Head First设计模式》
模式总结过程
- 公司新开发一款打怪等级游戏,三个角色:猎人、剑士、枪手,所有角色都能移动、攻击和外观,但是有两个不同,1攻击的方式不同,猎人使用弓箭,剑士使用利剑,枪手使用手枪,2外观不同
// 抽象角色类,都能移动,攻击抽象,让子类实现
public abstract class AbstractRole {
public void move() {
System.out.println("移动");
}
public abstract void attack();
public abstract void display();
}
// 猎人
public class HunterRole extends AbstractRole {
@Override
public void attack() {
System.out.println("使用弓箭射击怪物");
}
@Override
public void display() {
System.out.println("我是一个猎人");
}
}
// 剑士
public class SwordmanRole extends AbstractRole {
@Override
public void attack() {
System.out.println("使用利剑攻击怪物");
}
@Override
public void display() {
System.out.println("我是一个剑士");
}
}
// 枪手
public class GunnerRole extends AbstractRole {
@Override
public void attack() {
System.out.println("使用手枪射击怪物");
}
@Override
public void display() {
System.out.println("我是一个枪手");
}
}
- 版本升级,公司要求每个角色有默认的武器,但可以切换武器,开发的想法是,给角色一把武器,再给一个切换武器的方法
// 抽象角色
public abstract class AbstractRole {
// 武器
protected Weapon weapon;
public void move() {
System.out.println("移动");
}
// 玩家随意切换武器
public void switchWeapon(Weapon weapon) {
this.weapon = weapon;
}
// 调用武器进行攻击
public void attack() {
weapon.attack();
}
public abstract void display();
}
// 猎人
public class HunterRole extends AbstractRole {
// 注入默认的武器
public HunterRole() {
this.weapon = new BowAndArrowWeapon();
}
@Override
public void display() {
System.out.println("我是一个猎人");
}
}
// 剑士
public class SwordmanRole extends AbstractRole {
// 注入默认的武器
public SwordmanRole() {
this.weapon = new BladeWeapon();
}
@Override
public void display() {
System.out.println("我是一个剑士");
}
}
// 枪手
public class GunnerRole extends AbstractRole {
// 注入默认的武器
public GunnerRole() {
this.weapon = new HandgunWeapon();
}
@Override
public void display() {
System.out.println("我是一个枪手");
}
}
// 武器接口
public interface Weapon {
void attack();
}
// 弓箭
public class BowAndArrowWeapon implements Weapon {
@Override
public void attack() {
System.out.println("弓箭射击");
}
}
// 利剑
public class BladeWeapon implements Weapon {
@Override
public void attack() {
System.out.println("利剑攻击");
}
}
// 手枪
public class HandgunWeapon implements Weapon {
@Override
public void attack() {
System.out.println("手枪射击");
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 猎人-默认武器-弓箭
AbstractRole role = new GunnerRole();
// 攻击
role.attack();
// 切换武器-利剑
role.switchWeapon(new BladeWeapon());
// 攻击
role.attack();
}
}
/*
手枪射击
利剑攻击
*/
- 后续再增加角色,则只需要多一个角色类,并覆盖角色的外观方法即可
- 如果增加武器,也只需要增加一个武器类的实现
设计原则
- 依赖倒转原则(针对接口编)
- 合成/聚合复用原则(多用组合,少用继承)
模式的重心
策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性
本例中,所有武器构成算法族,策略算法是相同行为的不同实现,可自由切换
优点
- 符合开闭原则
- 维护性高、扩展性高
- 代码逻辑清晰明了
缺点
- 客户端需要知道所有的算法(本例中的武器)
- 算法增多时,对象的数目就会很可观
更多例子
备注:引用网上比较好的例子(https://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html)
假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。
根据描述,折扣是根据以下的几个算法中的一个进行的:
算法一:对初级会员没有折扣。
算法二:对中级会员提供10%的促销折扣。
算法三:对高级会员提供20%的促销折扣。
// 抽象折扣类
public interface MemberStrategy {
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double calcPrice(double booksPrice);
}
// 初级会员折扣类
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于初级会员的没有折扣");
return booksPrice;
}
}
// 中级会员折扣类
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于中级会员的折扣为10%");
return booksPrice * 0.9;
}
}
// 高级会员折扣类
public class AdvancedMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于高级会员的折扣为20%");
return booksPrice * 0.8;
}
}
// 价格类
public class Price {
//持有一个具体的策略对象
private MemberStrategy strategy;
/**
* 构造函数,传入一个具体的策略对象
* @param strategy 具体的策略对象
*/
public Price(MemberStrategy strategy){
this.strategy = strategy;
}
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double quote(double booksPrice){
return this.strategy.calcPrice(booksPrice);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
//选择并创建需要使用的策略对象
MemberStrategy strategy = new AdvancedMemberStrategy();
//创建环境
Price price = new Price(strategy);
//计算价格
double quote = price.quote(300);
System.out.println("图书的最终价格为:" + quote);
}
}
从上面的示例可以看出,策略模式仅仅封装算法,提供新的算法插入到已有系统中,以及老算法从系统中“退休”的方法,策略模式并不决定在何时使用何种算法。在什么情况下使用什么算法是由客户端决定的。