一、模式介绍
策略模式属于对象的行为模式,定义了算法簇,不同类型的算法分别封装起来,让他们之间可以相互替换,此模式让算法的变化,独立于算法使用者。策略模式属于行为行模式 。
二、场景解说
以一个简单的三国游戏为例,首先应该有人物,而人物具有不同的外貌以及移动行为。
游戏基本人物出来了,现在是时候要开始战斗了,因此我们加上战斗的方法,不同人物擅长不同的兵器,因此需要重载不同的fight()方法。
随着档次的提高,现在弄来了一匹马,作为大佬的刘备,就开始骑上马了。
如果这时候我们的卧龙骑马出山了,我们又该如何?
随着人物的不断增加,我们会发现角色的子类中重复的行为代码也越来越多,生活条件提高了,自然骑马的人将越来越多。此外我们也很难了解人物的全部行为,人物需要以什么方式移动,需要用什么方式战斗。而且一旦修改父类,子类可能也会受到很大的影响,比如如果修改了父类的移动行为骑马,这时候子类像张飞、关羽还是步行的人员就需要改变。
此时我们就应该思考如何解决这类问题。
首先,我们应该将变化的部分剥离出来,让他们不影响不变的部分。因此,将角色的move行为和fight行为提取出来。其次,我们需要面向接口编程,使模块依赖于抽象而不是实体。
最终版UML图
角色只提供一个关羽为例,其余角色实现类似直接继承Role即可。角色使用了抽象类,使用组合的方式持有Move和Fight行为的抽象接口,屏蔽具体实现方式,实际用户可以在构造方法上指定用户的行为或者调用Role类的对应的set方法实现动态改变行为。该设计模式即为策略模式。
三、场景演练JAVA实现
package com.sn.strategy.threekingdom;
public abstract class Role {
protected FightBehivor fightBehivor;
protected MoveBehavior moveBehavior;
/**
* 外形,也可以根据实际需要添加参数初始化不同造型
*/
public abstract void display();
/**
* 调用移动行为
*/
public void doMove(){
moveBehavior.move();
}
/**
* 调用战斗行为
*/
public void doFight(){
fightBehivor.fight();
}
/**
* 利用多态,运行时可以动态改变战斗行为
* @param fightBehivor
*/
public void setFightBehivor(FightBehivor fightBehivor) {
this.fightBehivor = fightBehivor;
}
/**
* 利用多态,运行时可以动态改变移动行为
* @param moveBehavior
*/
public void setMoveBehavior(MoveBehavior moveBehavior) {
this.moveBehavior = moveBehavior;
}
}
package com.sn.strategy.threekingdom;
/**
* 战斗行为
*/
public interface FightBehivor {
/**
* 战斗
*/
void fight();
}
package com.sn.strategy.threekingdom;
/**
* 移动行为
*
*/
public interface MoveBehavior {
/**
* 移动
*/
void move();
}
package com.sn.strategy.threekingdom.impl;
import com.sn.strategy.threekingdom.FightBehivor;
public class FightWithLance implements FightBehivor {
@Override
public void fight() {
System.out.println("战斗:矛");
}
}
package com.sn.strategy.threekingdom.impl;
import com.sn.strategy.threekingdom.FightBehivor;
public class FightWithWorse implements FightBehivor {
@Override
public void fight() {
System.out.println("战斗:剑");
}
}
package com.sn.strategy.threekingdom.impl;
import com.sn.strategy.threekingdom.FightBehivor;
import com.sn.strategy.threekingdom.MoveBehavior;
import com.sn.strategy.threekingdom.Role;
/**
* 角色:关羽
*/
public class GuanYu extends Role {
public GuanYu(FightBehivor fightBehivor, MoveBehavior moveBehavior) {
//父类定义,创建角色时就可以传入对应行为
this.fightBehivor=fightBehivor;
this.moveBehavior=moveBehavior;
}
@Override
public void display() {
System.out.println("正义荣耀装");
}
}
package com.sn.strategy.threekingdom.impl;
import com.sn.strategy.threekingdom.FightBehivor;
import com.sn.strategy.threekingdom.MoveBehavior;
import com.sn.strategy.threekingdom.Role;
/**
* 角色:刘备
*/
public class LiuBei extends Role {
public LiuBei(FightBehivor fightBehivor, MoveBehavior moveBehavior) {
//父类定义,创建角色时就可以传入对应行为
this.fightBehivor=fightBehivor;
this.moveBehavior=moveBehavior;
}
@Override
public void display() {
System.out.println("王者装");
}
}
package com.sn.strategy.threekingdom.impl;
import com.sn.strategy.threekingdom.MoveBehavior;
public class RideHorse implements MoveBehavior{
@Override
public void move() {
System.out.println("移动:骑马");
}
}
package com.sn.strategy.threekingdom.impl;
import com.sn.strategy.threekingdom.MoveBehavior;
public class Walk implements MoveBehavior {
@Override
public void move() {
System.out.println("移动:走");
}
}
package com.sn.strategy.threekingdom.impl;
import com.sn.strategy.threekingdom.FightBehivor;
import com.sn.strategy.threekingdom.MoveBehavior;
import com.sn.strategy.threekingdom.Role;
/**
* 角色:关羽
*/
public class ZhanFei extends Role {
public ZhanFei(FightBehivor fightBehivor, MoveBehavior moveBehavior) {
//父类定义,创建角色时就可以传入对应行为
this.fightBehivor=fightBehivor;
this.moveBehavior=moveBehavior;
}
@Override
public void display() {
System.out.println("霸气侧漏装");
}
}
测试类
package com.sn.strategy.threekingdom.test;
import com.sn.strategy.threekingdom.Role;
import com.sn.strategy.threekingdom.impl.*;
public class StrategyTest {
public static void main(String[] args) {
System.out.println("生成一个角色:刘备");
Role role = new LiuBei(new FightWithWorse(),new RideHorse());
role.display();
role.doMove();
role.doFight();
System.out.println("刘备:我骑马骑累了,要走路");
role.setMoveBehavior(new Walk());
role.doMove();
System.out.println("刘备:用剑没意思,给我来支矛");
role.setFightBehivor(new FightWithLance());
role.doFight();
}
}
输出结果
生成一个角色:刘备
王者装
移动:骑马
战斗:剑
刘备:我骑马骑累了,要走路
移动:走
刘备:用剑没意思,给我来支矛
战斗:矛
四、总结
1.结构
- 定义抽象的用户(环境对象),以组合的方式持有策略,持有抽象策略的对象(Role)
- 定义抽象的策略,这样降低了用户对实际策略行为的依赖,仅依赖于策略接口
- 定义实际用户,实现抽象用户类
- 定义实际策略,实现抽象策略类
2.设计原则
- 面向接口编程,而不是面向实现编程,接口包括抽象类
- 封装变化:找出应用中变化的部分,将其独立出来,与不变的部分分离
- 多用组合,少用继承(降低耦合度)
3.优点
- 提高扩展性:策略的扩展更加容易,只需增加新的策略实现类
- 降低耦合度:是有组合的方式持有抽象对象,使用者不需要了解实际策略的实现,只是需要了解有哪些策略即可,不需要依赖于具体策略
- 动态改变策略
4.缺点
- 类数目增加了,每种策略都有自己的实现。
参考资料:《Head First 设计模式》《常用设计模式在软件开发中的应用》