定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化;

 下面给出策略模式的类图,引自百度百科。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

策略模式在LZ第一次接触到的时候,LZ是这么理解的,就是如果我们想往一个方法当中插入随便一段代码的话,就是策略模式。即如下形式。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

public class MyClass {

    public void myMethod(){
        System.out.println("方法里的代码");
        //LZ想在这插入一段代码,而且这个代码是可以改变的,想怎么变就怎么变
        System.out.println("方法里的代码");
    }
}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

                在JAVA中,接口可以满足LZ的这一过分要求,我们可以设计一个接口,并当做参数传进去,就能达到这个效果了。我们来看,先定义一个接口。

public interface MyInterface {
    //我想插入的代码
    void insertCode();
    
}

                将原来的类改成这样,传递一个接口进去。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

public class MyClass {

    public void myMethod(MyInterface myInterface){
        System.out.println("方法里的代码");
        //你看我是不是插进来一段代码?而且这段代码是可以随便改变的
        myInterface.insertCode();
        System.out.println("方法里的代码");
    }
}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

               我们只要实现了MyInterface这个接口,在insertCode方法中写入我们想要插进去的代码,再将这个类传递给myMethod方法,就可以将我们随手写的代码插到这个方法当中。比如这样。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

class InsertCode1 implements MyInterface{

    public void insertCode() {
        System.out.println("我想插进去的代码,第一种");
    }

}

class InsertCode2 implements MyInterface{

    public void insertCode() {
        System.out.println("我想插进去的代码,第二种");
    }

}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

              这样我们在调用myMethod方法时就可以随意往里面插入代码了,比如。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

//客户端调用
public class Client {

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.myMethod(new InsertCode1());
        System.out.println("--------------------");
        myClass.myMethod(new InsertCode2());
    }
    
}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

              那么运行出来的结果就是我们成功的将两端代码插入到了myMethod方法中,以上所讲的算是JAVA中一种技术层面的实现,就是传入一个接口,封装代码。那么既然谈到设计模式,就要有设计模式的应用场景,有关策略模式,所产生的形式就和上述是一模一样的,只是我们适当的给予模式的应用场景,就会让它变的更有价值。

 

上面的例子代码清晰但却理解起来很生硬,下面LZ举一个具有实际意义的例子。

            就比如我们要做一个商店的收银系统,这个商店有普通顾客,会员,超级会员以及金牌会员的区别,针对各个顾客,有不同的打折方式,并且一个顾客每在商店消费1000就增加一个级别,那么我们就可以使用策略模式,因为策略模式描述的就是算法的不同,而且这个算法往往非常繁多,并且可能需要经常性的互相替换。

            这里我们举例就采用最简单的,以上四种顾客分别采用原价,八折,七折和半价的收钱方式。

            那么我们首先要有一个计算价格的策略接口,如下。

public interface CalPrice {
    //根据原价返回一个最终的价格
    Double calPrice(Double originalPrice);
    
}

             下面我们给出四个计算方式。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

class Common implements CalPrice{

    public Double calPrice(Double originalPrice) {
        return originalPrice;
    }

}
class Vip implements CalPrice{

    public Double calPrice(Double originalPrice) {
        return originalPrice * 0.8;
    }

}
class SuperVip implements CalPrice{

    public Double calPrice(Double originalPrice) {
        return originalPrice * 0.7;
    }

}
class GoldVip implements CalPrice{

    public Double calPrice(Double originalPrice) {
        return originalPrice * 0.5;
    }

}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

                  以上四种计算方式非常清晰,分别是原价,八折,七折和半价。下面我们看客户类,我们需要客户类帮我们完成客户升级的功能。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

//客户类
public class Customer {

    private Double totalAmount = 0D;//客户在本商店消费的总额
    private Double amount = 0D;//客户单次消费金额
    private CalPrice calPrice = new Common();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价
    
    //客户购买商品,就会增加它的总额
    public void buy(Double amount){
        this.amount = amount;
        totalAmount += amount;
        if (totalAmount > 3000) {//3000则改为金牌会员计算方式
            calPrice = new GoldVip();
        }else if (totalAmount > 2000) {//类似
            calPrice = new SuperVip();
        }else if (totalAmount > 1000) {//类似
            calPrice = new Vip();
        }
    }
    //计算客户最终要付的钱
    public Double calLastAmount(){
        return calPrice.calPrice(amount);
    }
}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

                 下面我们看客户端调用,系统会帮我们自动调整收费策略。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

//客户端调用
public class Client {

    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.buy(500D);
        System.out.println("客户需要付钱:" + customer.calLastAmount());
        customer.buy(1200D);
        System.out.println("客户需要付钱:" + customer.calLastAmount());
        customer.buy(1200D);
        System.out.println("客户需要付钱:" + customer.calLastAmount());
        customer.buy(1200D);
        System.out.println("客户需要付钱:" + customer.calLastAmount());
    }
    
}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

           运行以后会发现,第一次是原价,第二次是八折,第三次是七折,最后一次则是半价。我们这样设计的好处是,客户不再依赖于具体的收费策略,依赖于抽象永远是正确的。不过上述的客户类实在有点难看,尤其是buy方法,我们可以使用简单工厂来稍微改进一下它。我们建立如下策略工厂。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

//我们使用一个标准的简单工厂来改进一下策略模式
public class CalPriceFactory {

    private CalPriceFactory(){}
    //根据客户的总金额产生相应的策略
    public static CalPrice createCalPrice(Customer customer){
        if (customer.getTotalAmount() > 3000) {//3000则改为金牌会员计算方式
            return new GoldVip();
        }else if (customer.getTotalAmount() > 2000) {//类似
            return new SuperVip();
        }else if (customer.getTotalAmount() > 1000) {//类似
            return new Vip();
        }else {
            return new Common();
        }
    }
}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

                 这样我们就将制定策略的功能从客户类分离了出来,我们的客户类可以变成这样。

七:策略模式(不同等级会员打折算法)-冯金伟博客园

//客户类
public class Customer {

    private Double totalAmount = 0D;//客户在本商店消费的总额
    private Double amount = 0D;//客户单次消费金额
    private CalPrice calPrice = new Common();//每个客户都有一个计算价格的策略,初始都是普通计算,即原价
    
    //客户购买商品,就会增加它的总额
    public void buy(Double amount){
        this.amount = amount;
        totalAmount += amount;
        /* 变化点,我们将策略的制定转移给了策略工厂,将这部分责任分离出去 */
        calPrice = CalPriceFactory.createCalPrice(this);
    }
    //计算客户最终要付的钱
    public Double calLastAmount(){
        return calPrice.calPrice(amount);
    }
    
    public Double getTotalAmount() {
        return totalAmount;
    }
    
    public Double getAmount() {
        return amount;
    }
    
}

七:策略模式(不同等级会员打折算法)-冯金伟博客园

                   现在比之前来讲,我们的策略模式更加灵活一点,但是相信看过LZ博文的都知道,LZ最不喜欢elseif,所以策略模式也是有缺点的,就是当策略改变时,我们需要使用elseif去判断到底使用哪一个策略,哪怕使用简单工厂,也避免不了这一点。比如我们又添加一类会员,那么你需要去添加elseif。再比如我们的会员现在打九折了,那么你需要添加一个九折的策略,这没问题,我们对扩展开放,但是你需要修改elseif的分支,将会员的策略从八折替换为九折,这是简单工厂的诟病,在之前已经提到过,对修改开放。