设计模式-策略模式简介

引言

前段时间再看 《阿里巴巴Java编程规范 》时,看到这么一条推荐规范:

超过3层的if-else的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现

好奇这策略模式(Strategy Pattern)是一种什么样的解决方案,我决定去看看。

定义:

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,策略模式让算法独立于使用它的客户而独立变化。

结构

策略(Strategy)

定义所有支持的算法的公共接口。 Context 使用这个接口来调用某 ConcreteStrategy 定义的算法。

策略实现(ConcreteStrategy)

以 Strategy 接口实现某具体算法
上下文(Context)
用一个 ConcreteStrategy 对象来装配
维护一个 Strategy 对象的引用
可定义一个接口让 Strategy 访问它的数据

案例

假如,公司要你设计一款游戏中的角色,于是你这样设计:
创建了一个 角色接口,

/**
* @author hobart
* 角色接口
*/
public interface IRole {
    // 所有角色 都可以 战斗
    public void fight(); 
}

建立了一个 武当的角色:

/**
 * @author hobart
 * 武当派L角色
 */
public class WudangRole implements IRole {
	public void fight() {
		System.out.println("我是武当派角色,我用剑战斗!");
	}
}

再建立了一个 少林的角色:

/**
 * @author hobart
 * 少林派角色
 */
public class ShaolinRole implements IRole {
	public void fight() {
		System.out.println("我是少林派角色,我用棍战斗!");
	}
}

还有野怪角色:

/**
 * @author hobart
 * 野怪角色
 */
public class WildMonsterRole implements IRole{
	public void fight() {
		System.out.println("我是野怪角色,我用爪子战斗!");
	}
}

完事,问题解决。
但是后来你们领导说,这些角色只会战斗,让游戏缺乏趣味性,想让门派的角色可以学习和使用武功秘籍**(技能),这时候你发现你的野怪角色不能学习和使用武功秘籍(技能),要实现学习和使用武功秘籍**这个功能,你就要去WudangRole类和ShaolinRole类中各自添加 `` learningEsoterica()`, 则变成这样了

WudangRole类

/**
 * @author hobart
 * 武当派角色
 */
public class WudangRole implements IRole {
	public void fight() {
		System.out.println("我是武当派角色,我用剑战斗!");
	}
	
	public void learningEsoterica() {
		System.out.println("武当派角色学习了太极剑法!");
	}
}

ShaolinRole类

/**
 * @author hobart
 * 少林派角色
 */
public class ShaolinRole implements IRole {
	public void fight() {
		System.out.println("我是少林派角色,我用棍战斗!");
	}
	
	public void learningEsoterica() {
		System.out.println("少林派角色学习了易筋经!");
	}
}

可是,现在你的 角色 已经 不止这个两个了。还有许多其他门派的角色,也实现了IRole,他们也需要学习技能, 你的工作量就大大增加了。
解决办法
这时候,你就需要使用策略模式,将他们的行为抽取出来,则Fight为:

/**
 * @author hobart
 * 战斗接口
 */
public interface IFight {
	void fight();
}
/**
 * @author hobart
 * 少林战斗
 */
public class ShaolinFight implements IFight {
	public void fight() {
		System.out.println("我是少林派角色,我用棍战斗!");
	}
}
/**
 * @author hobart
 * 武当战斗
 */
public class WudangFight implements IFight {
	public void fight() {
		System.out.println("我是武当派角色,我用剑战斗!");
	}
}
/**
 * @author hobart
 * 野怪战斗
 */
public class WildMonsterFight implements IFight {
	public void fight() {
		System.out.println("我是野怪角色,我用爪子战斗!");
	}
}

learningEsoterica

public class ShaolinFight implements IFight {
	public void fight() {
		System.out.println("我是少林派角色,我用棍战斗!");
	}
}
/**
 * @author hobart
 * 武当战斗
 */
public class WudangFight implements IFight {
	public void fight() {
		System.out.println("我是武当派角色,我用剑战斗!");
	}
}
/**
 * @author hobart
 * 野怪战斗
 */
public class WildMonsterFight implements IFight {
	public void fight() {
		System.out.println("我是野怪角色,我用爪子战斗!");
	}
}

学习技能

/**
 * @author hobart
 * 少林学习技能
 */
public class ShaolinEsoterica implements ILearningEsoterica{
	public void learningEsoterica() {
		System.out.println("少林派角色学习了易筋经!");
	}
}
/**
 * @author hobart
 * 武当学习技能
 */
public class WudangEsoterica implements ILearningEsoterica{
	public void learningEsoterica() {
		System.out.println("武当派角色学习了太极剑法!");
	}
}

应用上下文(Context)

public class ContextRole {
	private IFight fight = null;
	private ILearningEsoterica esoterica = null;
    public ContextRole(IFight fight){
        this.fight = fight;
    }
    public ContextRole(IFight fight, ILearningEsoterica esoterica){
    	this.fight = fight;
    	this.esoterica = esoterica;
    }
    public void fight(){
    	if(fight != null) {
    		this.fight.fight();
        }
    }
    public void learningEsoterica(){
    	if(esoterica != null) {
    		this.esoterica.learningEsoterica();
    	}
    }
}

使用

public class StrategyUse {
	public static void main(String[] args) {
		ContextRole wudangRole = new ContextRole(new WudangFight(), new WudangEsoterica());
		wudangRole.fight();
		wudangRole.learningEsoterica();
		ContextRole wildMonster = new ContextRole(new WildMonsterFight());
		wildMonster.fight();
	}
}

总结

策略模式用于算法的自由切换和扩展,它是应用较为广泛的设计模式之一。策略模式对应于解决某一问题的一个算法族,允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换算法或者增加新的算法。只要涉及到算法的封装、复用和切换都可以考虑使用策略模式。
1. 主要优点
策略模式的主要优点如下:
(1) 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
(2) 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。
(3) 策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式,那么使用算法的环境类就可能会有一些子类,每一个子类提供一种不同的算法。但是,这样一来算法的使用就和算法本身混在一起,不符合“单一职责原则”,决定使用哪一种算法的逻辑和该算法本身混合在一起,从而不可能再独立演化;而且使用继承无法实现算法或行为在程序运行时的动态切换。
(4) 使用策略模式可以避免多重条件选择语句。多重条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将它们全部硬编码(Hard Coding)在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。
(5) 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。
2. 主要缺点
策略模式的主要缺点如下:
(1) 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
(2) 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
(3) 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。
3. 适用场景
在以下情况下可以考虑使用策略模式:
(1) 一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,根据“里氏代换原则”和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。
(2) 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
(3) 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漏墨小子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值