策略模式

695 阅读2分钟

一、概念

定义一系列的算法,把他们一个个封装起来,并且使他们可互相替换。本模式使得算法可独立于使用它的客户而变化。

二、使用场景

一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式避免在类中使用大量的条件语句。

三、UML结构图

策略模式UML.png

通过一个持有算法的Context上下文对象,来封装一系列的算法。 Context对象并不负责具体决定哪个算法,而是把算法选择工作交给了Client端,Client端可以选择好具体的算法,把其设置到上下文对象中,让上下文对象持有客户端所选择的具体的策略。 当客户端通知Context类去执行某项他想要的业务功能时,Context类就会转到具体的算法中。

四、代码示例

1.不使用策略模式的代码及弊端:

PriceCalculator:

public class PriceCalculator {
	
	private static final int BUS = 1;
	private static final int SUBWAY = 2;
	
	public static void main(String[] args) {
		PriceCalculator calculator = new PriceCalculator();
		System.out.println("20km公交票价:"+calculator.calculatePrice(20,BUS));
		System.out.println("20km地铁票价:"+calculator.calculatePrice(20,SUBWAY));
	}

	/**
	 * 公交车计价 10km之内1块钱;
	 * 超过10km,每加一块钱可以乘5km
	 * @param km
	 * @return
	 */
	private int busPrice(int km){
		//超过10km的距离
		int extraTotal = km - 10;
		//超过距离是5km的倍数
		int extraFactor = extraTotal / 5;
		//超过的距离对5km取余
		int fraction = extraTotal % 5;
		//计算价格
		int price = 1 + extraFactor % 5;
		
		return fraction > 0? ++price : price;
	} 
	
	/**
	 * 地铁计价 : 6-12km:4块 ;12-22km:5块; 22-32km:6块;其他距离7块
	 * @param km
	 * @return
	 */
	public int subwayPrice(int km){
		if(km <= 6){
			return 3;
		}else if(km > 6 && km < 12){
			return 4;
		}else if(km >12 && km < 22){
			return 5;
		}else if(km >22 && km < 32){
			return 6;
		}
		return 7;
	}
	
	/**
	 * 根据不同类型方式计价
	 * @param km
	 * @param type
	 * @return
	 */
	int calculatePrice(int km,int type){
		if(type == BUS){
			return busPrice(km);
		}else if(type == SUBWAY){
			return subwayPrice(km);
		}
		return 0;
	}
	
	
}

弊端: PriceCalculator 类很明显的一个问题就是不是单一职责,首先它承担计算公交和地铁乘坐价格的职责;另一个问题是通过if-else的形式来判断使用哪种计算形式,当增加一种出行方式时,如出租车,那么就需要在PriceCalculator中增加一个方法来计算出租车出行的价格,并且在calculatePrice(int km,int type)函数增加一个判断。

此种情况下,如果要增加一个出租车的类型时:

	private static final int TAXI = 3;
	
	private int taxiPrice(int km){
		return km * 2;
	}
	
	int calculatePrice(int km,int type){
		if(type == BUS){
			return busPrice(km);
		}else if(type == SUBWAY){
			return subwayPrice(km);
		}else if(type == TAXI){
			return taxiPrice(km);
		}
		return 0;
	}

2.使用策略模式实现:

AbstractStrategy:

public interface AbstractStrategy {
	
	//按距离计算价格
	int calculatePrice(int km);

}

BusStrategy:

public class BusStrategy implements AbstractStrategy{

	@Override
	public int calculatePrice(int km) {
		//超过10km的距离
		int extraTotal = km - 10;
		//超过距离是5km的倍数
		int extraFactor = extraTotal / 5;
		//超过的距离对5km取余
		int fraction = extraTotal % 5;
		//计算价格
		int price = 1 + extraTotal % 5;
		return fraction > 0 ? ++price : price;
	}

}

SubwayStrategy:

public class SubwayStrategy implements AbstractStrategy{

	@Override
	public int calculatePrice(int km) {
		if(km <= 6){
			return 3;
		}else if(km > 6 && km < 12){
			return 4;
		}else if(km > 12 && km < 22){
			return 5;
		}else if(km > 22 && km < 32){
			return 6;
		}
		return 7;
	}

}

TaxiStrategy:

public class TaxiStrategy implements AbstractStrategy{

	@Override
	public int calculatePrice(int km) {
		return km * 2;
	}

}

Context:

public class Context {
	
	//持有抽象接口的引用
	private AbstractStrategy strategy;
	
	//把抽象引用作为参数传进来
	public void setStrategy(AbstractStrategy strategy){
		this.strategy = strategy;
	}
	
	public int calculatePrice(int km){
		return strategy.calculatePrice(km);
	}
	
	public static void main(String[] strings){
		Context calculator = new Context();
		//只需要在客户端动态的传入类型,而不需要更改管理类
		//calculator.setStrategy(new BusStrategy());
		calculator.setStrategy(new TaxiStrategy());
		System.out.println("公交车20km价格:"+calculator.calculatePrice(20));
	}

}

五、策略模式的优点

  • 上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系。
  • 策略模式满足“开-闭原则”