策略模式(Strategy Pattern)


定义

  • 定义一系列的算法,并将每一个算法封装起来,而且使它们可以相互替换。

  • 策略模式让算法独立于使用它的客户而独立变化。


组成

  • 抽象策略角色:策略类,通常由一个接口或者抽象类实现。(1个)

  • 具体策略角色:包装了相关的算法和行为。(≥1个)

  • 环 境  角 色:持有一个策略类的引用,最终给客户端调用。(1个)


UML图

设计模式:策略模式



标准代码

抽象策略角色

package StrategyPattern.Standard;

public interface IStrategy 
{
	public void algorithm();
}


具体策略角色A

package StrategyPattern.Standard;

//接口实现:具体策略A
public class ConcreteStrategyA implements IStrategy
{

	@Override
	public void algorithm()
	{
		System.out.println("执行A策略");
	}

}


具体策略角色B

package StrategyPattern.Standard;

//接口实现:具体的策略B
public class ConcreteStrategyB implements IStrategy 
{

	@Override
	public void algorithm()
	{		
		System.out.println("执行B策略");
	}

}

具体策略角色C

package StrategyPattern.Standard;

//接口实现:具体策略C
public class ConcreteStrategyC implements IStrategy 
{

	@Override
	public void algorithm() 
	{
		System.out.println("执行C策略");
	}

}


上下文环境角色

package StrategyPattern.Standard;

//上下文环境,维护具体策略引用
public class Context 
{
	private IStrategy strategy;
	
	public Context(IStrategy strategy)
	{
		this.strategy = strategy;
	}
	
	public void contextInterface()
	{
		this.strategy.algorithm();
	}
}


客户端调用演示代码

package StrategyPattern.Standard;

public class Demo 
{
	public static void main(String args[])
	{
		//初始使用A策略
		Context testContext = new Context(new ConcreteStrategyA());
		testContext.contextInterface();
		
		//使用B策略
		testContext = new Context(new ConcreteStrategyB());
		testContext.contextInterface();
		
		//使用C策略
		testContext = new Context(new ConcreteStrategyC());
		testContext.contextInterface();
	}
}



说明分析

  • 策略模式中如果新增加1个具体策略角色,不需要修改任何代码,只需要增加一个具体策略角色类即可,这是符合面向对象程序设计6大原则中的开发封闭原则的。

  • 策略模式要求客户端代码掌握各个具体策略的信息,并加以使用,在客户端增加了和具体策略的耦合;如果客户端要使用新增加的具体策略角色,就必须修改客户端代码,在客户端违反了开放封闭原则。因此策略在代码中的使用位置在设计和使用中显得就比较重要,需要加以权衡,具体情况具体分析;



示例演示

背景

在地图应用中,通过地图应用软件输入起始点字符串和终止点字符串来查找公交线路,地图软件会返回相应的查询结果字符串,一般查询的结果有3种:少换乘,较快捷,少步行。

分析

以上的路径规划查询过程中就是用了策略模式,其中抽象策略角色为路径规划算法,具体策略角色为少换乘路径规划算法,少步行路径规划算法和较快捷路径规划算法;

UML

设计模式:策略模式


代码

package StrategyPattern.Example;

//抽象策略角色:路径规划算法
public interface IRoutePlanning 
{
	//start:起始点地址字符串,end:终止点地址字符串
	public String routePlan(String start, String end);
}


package StrategyPattern.Example;

//具体策略角色:少换乘路径规划算法
public class LessTranRoutePlanning implements IRoutePlanning 
{

	@Override
	public String routePlan(String start, String end) 
	{
		String path;
		
		//经过一系列的地图少换乘路径规划算法处理,获取到path路径		
		path = start + "到" + end + "的少换乘路径";
		
		return path;
	}

}
package StrategyPattern.Example;

//具体策略角色:少步行路径规划算法
public class LessWalkRoutePlanning implements IRoutePlanning 
{

	@Override
	public String routePlan(String start, String end) 
	{
		String path;
		
		//经过一系列的地图少步行路径规划算法处理,获取到path路径		
		path = start + "到" + end + "的少步行路径";
		
		return path;
	}

}
package StrategyPattern.Example;

//具体策略角色:较快捷路径规划算法
public class MoreFasterRoutePlanning implements IRoutePlanning 
{

	@Override
	public String routePlan(String start, String end) 
	{
		String path;
		
		//经过一系列的地图较快捷路径规划算法处理,获取到path路径		
		path = start + "到" + end + "的较快捷路径";
		
		return path;
	}

}
package StrategyPattern.Example;

//上下文环境角色:维护路径规划算法的调用
public class RoutePlanningContext
{
	private IRoutePlanning routePlanning;
	
	public RoutePlanningContext(IRoutePlanning routePlanning)
	{
		this.routePlanning = routePlanning;
	}
	
	String routePlan(String start, String end)
	{
		return routePlanning.routePlan(start, end);
	}
}


package StrategyPattern.Example;

public abstract class Client 
{
	public static void main(String args[])
	{
		RoutePlanningContext context = null;
		String path;
		
		context = new RoutePlanningContext(new LessTranRoutePlanning());
		path = context.routePlan("北京南站", "北京北站");
		System.out.println(path);
		
		context = new RoutePlanningContext(new LessWalkRoutePlanning());
		path = context.routePlan("北京南站", "北京北站");
		System.out.println(path);
		
		context = new RoutePlanningContext(new MoreFasterRoutePlanning());
		path = context.routePlan("北京南站", "北京北站");
		System.out.println(path);
		
	}
}


总结

优点:

  • 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。

  • 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。

  • 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。

缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。

  • 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。