深入解析策略模式

原文地址:

https://www.cnblogs.com/lewis0077/p/5133812.html

 在讲策略模式之前,我们先看一个日常生活中的小例子:

  现实生活中我们到商场买东西的时候,卖场往往根据不同的客户制定不同的报价策略,比如针对新客户不打折扣,针对老客户打9折,针对VIP客户打8折...

  现在我们要做一个报价管理的模块,简要点就是要针对不同的客户,提供不同的折扣报价。

如果是有你来做,你会怎么做?

我们很有可能写出下面的代码:

package strategy.examp02;

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;
    }

}

经过测试,上面的代码工作的很好,可是上面的代码是有问题的。上面存在的问题:把不同客户的报价的算法都放在了同一个方法里面,使得该方法很是庞大(现在是只是一个演示,所以看起来还不是很臃肿)。

下面看一下上面的改进,我们把不同客户的报价的算法都单独作为一个方法

package strategy.examp02;

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;
    }

}

上面的代码比刚开始的时候要好一点,它把每个具体的算法都单独抽出来作为一个方法,当某一个具体的算法有了变动的时候,只需要修改响应的报价算法就可以了。

但是改进后的代码还是有问题的,那有什么问题呢?

1.当我们新增一个客户类型的时候,首先要添加一个该种客户类型的报价算法方法,然后再quote方法中再加一个else if的分支,是不是感觉很是麻烦呢?而且这也违反了设计原则之一的开闭原则(open-closed-principle).

开闭原则:

  对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。

  对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。

2.我们经常会面临这样的情况,不同的时期使用不同的报价规则,比如在各个节假日举行的各种促销活动时、商场店庆时往往都有普遍的折扣,但是促销时间一旦过去,报价就要回到正常价格上来。按照上面的代码我们就得修改if else里面的代  码很是麻烦

那有没有什么办法使得我们的报价管理即可扩展、可维护,又可以方便的响应变化呢?当然有解决方案啦,就是我们下面要讲的策略模式。

 

定义:

  策略模式定义了一系列的算法,并将每一个算法封装起来,使每个算法可以相互替代,使算法本身和使用算法的客户端分割开来,相互独立。

结构:

  1.策略接口角色IStrategy:用来约束一系列具体的策略算法,策略上下文角色ConcreteStrategy使用此策略接口来调用具体的策略所实现的算法。

  2.具体策略实现角色ConcreteStrategy:具体的策略实现,即具体的算法实现。

  3.策略上下文角色StrategyContext:策略上下文,负责和具体的策略实现交互,通常策略上下文对象会持有一个真正的策略实现对象,策略上下文还可以让具体的策略实现从其中获取相关数据,回调策略上下文对象的方法。

UML类图:

                                                    

UML序列图:

                                                

策略模式代码的一般通用实现:

策略接口

package strategy.examp01;

//策略接口
public interface IStrategy {
    //定义的抽象算法方法 来约束具体的算法实现方法
    public void algorithmMethod();
}

具体的策略实现:

package strategy.examp01;

// 具体的策略实现
public class ConcreteStrategy implements IStrategy {
    //具体的算法实现
    @Override
    public void algorithmMethod() {
        System.out.println("this is ConcreteStrategy method...");
    }
}

 

package strategy.examp01;

 // 具体的策略实现2
public class ConcreteStrategy2 implements IStrategy {
     //具体的算法实现
    @Override
    public void algorithmMethod() {
        System.out.println("this is ConcreteStrategy2 method...");
    }
}

策略上下文:

package strategy.examp01;

/**
 * 策略上下文
 */
public class StrategyContext {
    //持有一个策略实现的引用
    private IStrategy strategy;
    //使用构造器注入具体的策略类
    public StrategyContext(IStrategy strategy) {
        this.strategy = strategy;
    }

    public void contextMethod(){
        //调用策略实现的方法
        strategy.algorithmMethod();
    }
}

 

外部客户端:

package strategy.examp01;

//外部客户端
public class Client {
    public static void main(String[] args) {
        //1.创建具体测策略实现
        IStrategy strategy = new ConcreteStrategy2();
        //2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中
        StrategyContext ctx = new StrategyContext(strategy);
        //3.调用上下文对象的方法来完成对具体策略实现的回调
        ctx.contextMethod();
    }
}

针对我们一开始讲的报价管理的例子:我们可以应用策略模式对其进行改造,不同类型的客户有不同的折扣,我们可以将不同类型的客户的报价规则都封装为一个独立的算法,然后抽象出这些报价算法的公共接口

公共报价策略接口:

针对我们一开始讲的报价管理的例子:我们可以应用策略模式对其进行改造,不同类型的客户有不同的折扣,我们可以将不同类型的客户的报价规则都封装为一个独立的算法,然后抽象出这些报价算法的公共接口

公共报价策略接口:

复制代码

1 package strategy.examp02;
2 
3 import java.math.BigDecimal;
4 //报价策略接口
5 public interface IQuoteStrategy {
6     //获取折后价的价格
7     BigDecimal getPrice(BigDecimal originalPrice);
8 }

复制代码

新客户报价策略实现:

复制代码

 1 package strategy.examp02;
 2 
 3 import java.math.BigDecimal;
 4 //新客户的报价策略实现类
 5 public class NewCustomerQuoteStrategy implements IQuoteStrategy {
 6     @Override
 7     public BigDecimal getPrice(BigDecimal originalPrice) {
 8         System.out.println("抱歉!新客户没有折扣!");
 9         return originalPrice;
10     }
11 }

复制代码

老客户报价策略实现:

复制代码

 1 package strategy.examp02;
 2 
 3 import java.math.BigDecimal;
 4 //老客户的报价策略实现
 5 public class OldCustomerQuoteStrategy implements IQuoteStrategy {
 6     @Override
 7     public BigDecimal getPrice(BigDecimal originalPrice) {
 8         System.out.println("恭喜!老客户享有9折优惠!");
 9         originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
10         return originalPrice;
11     }
12 }

复制代码

VIP客户报价策略实现:

复制代码

package strategy.examp02;

import java.math.BigDecimal;
//VIP客户的报价策略实现
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;
    }
}

复制代码

报价上下文:

复制代码

 1 package strategy.examp02;
 2 
 3 import java.math.BigDecimal;
 4 //报价上下文角色
 5 public class QuoteContext {
 6     //持有一个具体的报价策略
 7     private IQuoteStrategy quoteStrategy;
 8 
 9     //注入报价策略
10     public QuoteContext(IQuoteStrategy quoteStrategy){
11         this.quoteStrategy = quoteStrategy;
12     }
13 
14     //回调具体报价策略的方法
15     public BigDecimal getPrice(BigDecimal originalPrice){
16         return quoteStrategy.getPrice(originalPrice);
17     }
18 }

复制代码

外部客户端:

复制代码

 1 package strategy.examp02;
 2 
 3 import java.math.BigDecimal;
 4 //外部客户端
 5 public class Client {
 6     public static void main(String[] args) {
 7         //1.创建老客户的报价策略
 8         IQuoteStrategy oldQuoteStrategy = new OldCustomerQuoteStrategy();
 9 
10         //2.创建报价上下文对象,并设置具体的报价策略
11         QuoteContext quoteContext = new QuoteContext(oldQuoteStrategy);
12 
13         //3.调用报价上下文的方法
14         BigDecimal price = quoteContext.getPrice(new BigDecimal(100));
15 
16         System.out.println("折扣价为:" +price);
17     }
18 }

复制代码

控制台输出:

恭喜!老客户享有9折优惠!
折扣价为:90.00

 

这个时候,商场营销部新推出了一个客户类型--MVP用户(Most Valuable Person),可以享受折扣7折优惠,那该怎么办呢?

这个很容易,只要新增一个报价策略的实现,然后外部客户端调用的时候,创建这个新增的报价策略实现,并设置到策略上下文就可以了,对原来已经实现的代码没有任何的改动。

MVP用户的报价策略实现:

复制代码

 1 package strategy.examp02;
 2 
 3 import java.math.BigDecimal;
 4 //MVP客户的报价策略实现
 5 public class MVPCustomerQuoteStrategy implements IQuoteStrategy {
 6     @Override
 7     public BigDecimal getPrice(BigDecimal originalPrice) {
 8         System.out.println("哇偶!MVP客户享受7折优惠!!!");
 9         originalPrice = originalPrice.multiply(new BigDecimal(0.7)).setScale(2,BigDecimal.ROUND_HALF_UP);
10         return originalPrice;
11     }
12 }

复制代码

外部客户端:

复制代码

 1 package strategy.examp02;
 2 
 3 import java.math.BigDecimal;
 4 //外部客户端
 5 public class Client {
 6     public static void main(String[] args) {
 7         //创建MVP客户的报价策略
 8         IQuoteStrategy mvpQuoteStrategy = new MVPCustomerQuoteStrategy();
 9 
10         //创建报价上下文对象,并设置具体的报价策略
11         QuoteContext quoteContext = new QuoteContext(mvpQuoteStrategy);
12 
13         //调用报价上下文的方法
14         BigDecimal price = quoteContext.getPrice(new BigDecimal(100));
15 
16         System.out.println("折扣价为:" +price);
17     }
18 }

复制代码

控制台输出:

哇偶!MVP客户享受7折优惠!!!
折扣价为:70.00

 

深入理解策略模式:

策略模式的作用:就是把具体的算法实现从业务逻辑中剥离出来,成为一系列独立算法类,使得它们可以相互替换。

策略模式的着重点:不是如何来实现算法,而是如何组织和调用这些算法,从而让我们的程序结构更加的灵活、可扩展。

  我们前面的第一个报价管理的示例,发现每个策略算法实现对应的都是在QuoteManager 中quote方法中的if else语句里面,我们知道if else if语句里面的代码在执行的可能性方面可以说是平等的,你要么执行if,要么执行else,要么执行else if。

  策略模式就是把各个平等的具体实现进行抽象、封装成为独立的算法类,然后通过上下文和具体的算法类来进行交互。各个策略算法都是平等的,地位是一样的,正是由于各个算法的平等性,所以它们才是可以相互替换的。虽然我们可以动态的切换各个策略,但是同一时刻只能使用一个策略。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值