[整理] 设计模式之策略模式

策略模式(Strategy Pattern)

  • 定义了算法族,分别封装起来,让它们之间可相互替换,此模式让算法的变化独立于使用算法的客户。

优点:

1、算法可以自由切换。
2、避免使用多重条件判断。
3、扩展性良好。

缺点:

1、策略类会增多。
2、所有策略类都需要对外暴露。

案例分析

假设公司需要做一款武侠游戏,我们就是负责游戏的角色模块,需求是这样的:每个角色对应一个名字,每类角色对应一种样子,每个角色拥有一个逃跑、攻击、防御的技能。

// 游戏的角色超类
public abstract class Role
{
	protected String name;
	protected abstract void display();
	protected abstract void run();
	protected abstract void attack();
	protected abstract void defend();
}
public class RoleA extends Role
{
	public RoleA(String name)
	{
		this.name = name;
	}

	@Override
	protected void display()
	{
		System.out.println("样子1");
	}

	@Override
	protected void run()
	{
		System.out.println("金蝉脱壳");
	}

	@Override
	protected void attack()
	{
		System.out.println("降龙十八掌");
	}

	@Override
	protected void defend()
	{
		System.out.println("铁头功");
	}
}

如果再加两个角色
RoleB(样子2 ,降龙十八掌,铁布衫,金蝉脱壳)。
RoleC(样子1,拥有九阳神功,铁布衫,烟雾弹)。
你又得类似的去写一遍,里边就出现了大量重复的代码。找出应用中可能需要变化的部分,把它们独立出来,不要和那些不需要变化的代码混在一起。

我们发现,对于每个角色的display,attack,defend,run都是有可能变化的,于是我们必须把这写独立出来。
再根据另一个设计原则:针对接口(超类型)编程,而不是针对实现编程,于是我们把代码改造成这样:

public interface IAttackBehavior
{
	void attack();
}
public interface IDefendBehavior
{
	void defend();
}
public interface IDisplayBehavior
{
	void display();
}
public class AttackJY implements IAttackBehavior
{
	@Override
	public void attack()
	{
		System.out.println("九阳神功!");
	}
}
public class DefendTBS implements IDefendBehavior
{
	@Override
	public void defend()
	{
		System.out.println("铁布衫");
	}
}
package com.zhy.bean;

public class RunJCTQ implements IRunBehavior
{
	@Override
	public void run()
	{
		System.out.println("金蝉脱壳");
	}
}

这时候需要对Role的代码做出改变:

// 游戏的角色超类
public abstract class Role
{
	protected String name;

	protected IDefendBehavior defendBehavior;
	protected IDisplayBehavior displayBehavior;
	protected IRunBehavior runBehavior;
	protected IAttackBehavior attackBehavior;

	public Role setDefendBehavior(IDefendBehavior defendBehavior)
	{
		this.defendBehavior = defendBehavior;
		return this;
	}

	public Role setDisplayBehavior(IDisplayBehavior displayBehavior)
	{
		this.displayBehavior = displayBehavior;
		return this;
	}

	public Role setRunBehavior(IRunBehavior runBehavior)
	{
		this.runBehavior = runBehavior;
		return this;
	}

	public Role setAttackBehavior(IAttackBehavior attackBehavior)
	{
		this.attackBehavior = attackBehavior;
		return this;
	}

	protected void display()
	{
		displayBehavior.display();
	}

	protected void run()
	{
		runBehavior.run();
	}

	protected void attack()
	{
		attackBehavior.attack();
	}

	protected void defend()
	{
		defendBehavior.defend();
	}
}

每个角色现在只需要一个name了:

public class RoleA extends Role
{
	public RoleA(String name)
	{
		this.name = name;
	}
}

现在我们需要一个金蝉脱壳,降龙十八掌!,铁布衫,样子1的角色A只需要这样:

public class Test
{
	public static void main(String[] args)
	{
		Role roleA = new RoleA("A");

		roleA.setAttackBehavior(new AttackXL())//
				.setDefendBehavior(new DefendTBS())//
				.setDisplayBehavior(new DisplayA())//
				.setRunBehavior(new RunJCTQ());
		System.out.println(roleA.name + ":");
		roleA.run();
		roleA.attack();
		roleA.defend();
		roleA.display();
	}
}

经过修改,现在所有的技能的实现做到了100%的复用,并且随便需要什么样的角色,只需要动态设置一下技能和展示方式,是不是很完美?
这就是策略模式。定义了一个算法族(各种技能),且根据需求可以进行相互替换,算法(各种技能)的实现独立于客户(角色)。现在是不是很好理解策略模式的定义了。

相关的OO的原则:

1、封装变化(把可能变化的代码封装起来)
2、多用组合,少用继承(我们使用组合的方式,为客户设置了算法)
3、针对接口编程,不针对实现(对于Role类的设计完全的针对角色,和技能的实现没有关系)

posted @ 2020-11-24 13:11  哆啦梦乐园  阅读(87)  评论(0编辑  收藏  举报