一、定义
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
策略模式的主要角色如下,
1>抽象策略类(Strategy):定义了一个公共接口,各种不同的角色以不同的方式实现这个接口,一般使用接口或抽象类实现。
2>具体策略类(Concrete Strategy):实现了抽象策略类中所定义的方法,提供了具体的算法实现。
3>上下文环境类(Context):拥有一个对策略类的引用,最终给客户端调用。
二、适用场景
一个系统有许多类,而区分它们的只是他们直接的行为时。
三、实现
策略接口
public interface IStrategy {
//定义的抽象算法方法 来约束具体的算法实现方法
public void algorithmMethod();
}
具体的策略实现:
public class ConcreteStrategy implements IStrategy {
@Override
public void algorithmMethod() {
System.out.println("this is ConcreteStrategy method...");
}
}
public class ConcreteStrategy2 implements IStrategy {
@Override
public void algorithmMethod() {
System.out.println("this is ConcreteStrategy2 method...");
}
}
策略上下文:
public class StrategyContext {
//持有一个策略实现的引用
private IStrategy strategy;
//使用构造器注入具体的策略类
public StrategyContext(IStrategy strategy) {
this.strategy = strategy;
}
public void contextMethod(){
//调用策略实现的方法
strategy.algorithmMethod();
}
}
//外部客户端
public class Client {
public static void main(String[] args) {
//1.创建具体测策略实现
IStrategy strategy = new ConcreteStrategy2();
//2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中
StrategyContext ctx = new StrategyContext(strategy);
//3.调用上下文对象的方法来完成对具体策略实现的回调
ctx.contextMethod();
}
}
四、优缺点
优点:1>算法可以自由切换;
2>避免使用多重条件判断;
3>扩展性良好,增加一个策略只需要实现接口即可。
缺点:1>策略类的数量会增加,每个策略都是一个类,复用的可能性很小;
2>所有的策略类都需要对外暴露。
五、实例
现实生活中我们到商场买东西的时候,卖场往往根据不同的客户制定不同的报价策略,比如针对新客户不打折扣,针对老客户打9折,针对VIP客户打8折...
import java.math.BigDecimal;
public class QuoteManager {
public BigDecimal quote(BigDecimal originalPrice,String customType){
if ("新客户".equals(customType)) {
System.out.println("抱歉!新客户没有折扣!");
return originalPrice;
}else if ("老客户".equals(customType)) {
System.out.println("恭喜你!老客户打9折!");
originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}else if("VIP客户".equals(customType)){
System.out.println("恭喜你!VIP客户打8折!");
originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
//其他人员都是原价
return originalPrice;
}
}
经过测试,上面的代码工作的很好,可是上面的代码是有问题的。上面存在的问题:把不同客户的报价的算法都放在了同一个方法里面,使得该方法很是庞大。下面看一下上面的改进,我们把不同客户的报价的算法都单独作为一个方法,
import java.math.BigDecimal;
public class QuoteManagerImprove {
public BigDecimal quote(BigDecimal originalPrice, String customType){
if ("新客户".equals(customType)) {
return this.quoteNewCustomer(originalPrice);
}else if ("老客户".equals(customType)) {
return this.quoteOldCustomer(originalPrice);
}else if("VIP客户".equals(customType)){
return this.quoteVIPCustomer(originalPrice);
}
//其他人员都是原价
return originalPrice;
}
/**
* 对VIP客户的报价算法
* @param originalPrice 原价
* @return 折后价
*/
private BigDecimal quoteVIPCustomer(BigDecimal originalPrice) {
System.out.println("恭喜!VIP客户打8折");
originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
/**
* 对老客户的报价算法
* @param originalPrice 原价
* @return 折后价
*/
private BigDecimal quoteOldCustomer(BigDecimal originalPrice) {
System.out.println("恭喜!老客户打9折");
originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
/**
* 对新客户的报价算法
* @param originalPrice 原价
* @return 折后价
*/
private BigDecimal quoteNewCustomer(BigDecimal originalPrice) {
System.out.println("抱歉!新客户没有折扣!");
return originalPrice;
}
}
算法方法,然后再quote方法中再加一个else if的分支,违反了设计原则之一的开闭原则(open-closed-principle).
开闭原则:
对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。
2>我们经常会面临这样的情况,不同的时期使用不同的报价规则,比如在各个节假日举行的各种促销活动时、商场店庆时往往都有普遍的折扣,但是促销时间一旦过去,报价就要回到正常价格上来。按照上面的代码我们就得修改if else里面的代码很是麻烦。
使用策略模式进行改进:
1>公共报价策略接口
import java.math.BigDecimal;
public interface IQuoteStrategy {
//获取折后价的价格
BigDecimal getPrice(BigDecimal originalPrice);
}
2>新客户报价策略实现
import java.math.BigDecimal;
public class NewCustomerQuoteStrategy implements IQuoteStrategy {
@Override
public BigDecimal getPrice(BigDecimal originalPrice) {
System.out.println("抱歉!新客户没有折扣!");
return originalPrice;
}
}
3>老客户报价策略实现
import java.math.BigDecimal;
public class OldCustomerQuoteStrategy implements IQuoteStrategy {
@Override
public BigDecimal getPrice(BigDecimal originalPrice) {
System.out.println("恭喜!老客户享有9折优惠!");
originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
}
4>VIP客户报价策略实现
import java.math.BigDecimal;
public class VIPCustomerQuoteStrategy implements IQuoteStrategy {
@Override
public BigDecimal getPrice(BigDecimal originalPrice) {
System.out.println("恭喜!VIP客户享有8折优惠!");
originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
return originalPrice;
}
}
5>报价上下文
import java.math.BigDecimal;
public class QuoteContext {
//持有一个具体的报价策略
private IQuoteStrategy quoteStrategy;
//注入报价策略
public QuoteContext(IQuoteStrategy quoteStrategy){
this.quoteStrategy = quoteStrategy;
}
//回调具体报价策略的方法
public BigDecimal getPrice(BigDecimal originalPrice){
return quoteStrategy.getPrice(originalPrice);
}
}
6>实现
import java.math.BigDecimal;
public class Client {
public static void main(String[] args) {
//1.创建老客户的报价策略
IQuoteStrategy oldQuoteStrategy = new OldCustomerQuoteStrategy();
//2.创建报价上下文对象,并设置具体的报价策略
QuoteContext quoteContext = new QuoteContext(oldQuoteStrategy);
//3.调用报价上下文的方法
BigDecimal price = quoteContext.getPrice(new BigDecimal(100));
System.out.println("折扣价为:" +price);
}
}