设计思路:
Context上下文(环境角色):持有一个策略对象,
Strategy(抽象策略角色):抽象类或接口等着具体策略类来实现 ,
具体策略角色:实现了Strategy抽象类或接口
场景:
最近手游比较火,一个餐厅为了吸引客户,活动期间根据王者荣耀段位来本店打折消费,现在要你写个程序来实现计算实际金额.
铂金(platinum)0.9
砖石(masonry) 0.8
王者(the king) 0.6
UML:
实例:
Strategy:抽象策略角色
package com.sl.demo.strategy;
/**
* 策略接口
* @author pengkun
*
*/
public interface Strategy {
double getPrice(double price);
}
具体策略角色:
/**
* 铂金策略
* @author pengkun
*
*/
public class MasonryStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("铂金大神你好:");
return price*0.9;
}
}
/**
* 砖石策略
* @author pengkun
*
*/
public class PlatinumStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("砖石大神你好:");
return price*0.8;
}
}
/**
* 王者策略
* @author pengkun
*
*/
public class TheKingStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("王者大神你太厉害了,带带我吧!我给你打折");
return price*0.6;
}
}
环境角色:
package com.sl.demo.strategy;
/**
* 上下文(环境角色)
* @author pengkun
*
*/
public class Context {
//持有一个策略
private Strategy strategy;
//创建有参构造方法,参数为一个策略
public Context(Strategy strategy) {
super();
this.strategy = strategy;
}
public double getPrice(double price) {
//调用具体策略,get实际价格
return strategy.getPrice(price);
}
}
测试:
@Test
public void test() {
Strategy strategy=new TheKingStrategy();
Context context=new Context(strategy);
double price=context.getPrice(1000);
System.out.println("你本次实际消费:"+price);
}
结果:
现在老板又要改优惠政策了 加了个满300减100,然而我们其他代码都不需要去更改,只需要加一个优惠策略就行了,是不是很爽
/**
* 优惠卷(满300减100)
* @author pengkun
*
*/
public class CouponStrategy implements Strategy {
@Override
public double getPrice(double price) {
System.out.println("使用了优惠券(满300减100):");
if(price>=300) {
return price-100;
}
return price;
}
}
测试:
@Test
public void test1() {
Strategy strategy=new CouponStrategy();
Context context=new Context(strategy);
double price=context.getPrice(1000);
System.out.println("你本次实际消费:"+price);
}
简单工厂+策略
我们只要修改context就好了
package com.sl.demo.strategy;
/**
* 上下文(环境角色)
* @author pengkun
*
*/
public class Context2 {
//持有一个策略
private Strategy strategy;
//创建有参构造方法,注意参数是个字符串
public Context2(String grade) throws Exception {
if("铂金".equals(grade)) {
strategy=new MasonryStrategy();
}else if("砖石".equals(grade)) {
strategy=new PlatinumStrategy();
}else if("王者".equals(grade)) {
strategy=new TheKingStrategy();
}else {
throw new Exception("未找到对应优惠策略");
}
}
public double getPrice(double price) {
//调用具体策略,get实际价格
return strategy.getPrice(price);
}
}
测试:
@Test
public void test2() {
try {
Context2 context2=new Context2("王者");
double price=context2.getPrice(1000);
System.out.println("你本次实际消费:"+price);
} catch (Exception e) {
System.out.println("异常:"+e.getMessage());
}
}
这样子client 以后添加一个就不需要改了,简单工厂模式把生成策略类与客户端分离
当是这里的工厂(context)中的if-else的使用是不太好的,
总结:
优点:
1、 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。
2、 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
3、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
缺点:
1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
2、 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。