策略模式

 
策略模式(Strategy): 定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
当不同的的行为堆砌在一个类中,就很难避免使用条件语句来选择合适的行为,将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。
--摘自《大话设计模式》

 

该模式涉及到三个角色:
  • 环境(Context)角色:持有一个Strategy的引用
  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的绝体策略类所需的接口
  • 具体策略(Concrete Stategy)角色:包装了相关的算法或行为
public class Context {
    //持有一个具体策略的对象
    private Strategy strategy;
    /**
     * 构造函数,传入一个具体策略对象
     * @param strategy    具体策略对象
     */
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    /**
     * 策略方法
     */
    public void contextInterface(){
        
        strategy.strategyInterface();
    }
    
}
public interface Strategy {
    /**
     * 策略方法
     */
    public void strategyInterface();
}
public class ConcreteStrategyA implements Strategy {

    @Override
    public void strategyInterface() {
        //相关的业务
    }

}

public class ConcreteStrategyB implements Strategy {

    @Override
    public void strategyInterface() {
        //相关的业务
    }

}
使用场景:
    假设有三种类别客户,分别为普通客户,会员,超级会员。针对不同类别的会员,有不同的打折方式。
    普通客户原价,会员9折,超级会员8折。
 
不使用策略模式:
 
package com.example.demo.designpattern;
/**
* 假设有3种会员,分别为会员,超级会员以及金牌会员和普通顾客,针对不同类别的会员,有不同的打折方式,
* 并且一个顾客每消费10000就增加一个级别
* 以上四种会员分别采用原价(普通顾客),九折(会员),八折(超级会员)和七折(金牌会员)的折扣方式。
*/
public class Settlement {
    /**
     * 总价
     */
    private double totalPrice = 0;
    /**
     * 单次消费的金额
     */
    private double amount = 0;
    /**
     * 购买的方法
     * @param amount    商品价格
     * @return double   支付的价格
     */
    public double buy(double amount) throws Exception {
        this.amount = amount;
        this.totalPrice += this.amount;
        if(this.totalPrice < 10000) {
            return this.amount;
        } else if(this.totalPrice >= 10000 && this.totalPrice < 20000) {
            return this.amount * 0.9;
        } else if(this.totalPrice >= 20000 && this.totalPrice < 30000) {
            return this.amount * 0.8;
        } else {
            return this.amount * 0.7;
        }
    }
}
---------------------
作者:zhongxuebin_xq
来源:CSDN
原文:https://blog.csdn.net/zhongxuebin_xq/article/details/81275958
版权声明:本文为博主原创文章,转载请附上博文链接!

这种面向过程的实现方式,再以后的维护和新增会员类型的时候,得需要重新添加条件判断,来满足我们的业务需求变化,是一种不灵活的设计方式,下面进行改造。

package strategy;
/**
*
* Title: IDiscountStrategy
* Description: 折扣 策略
* @author yacong_liu Email:2682505646@qq.com
* @date 2019年1月22日
*/
public interface IDiscountStrategy {
    
    double discout(double price);
}

普通顾客策略

package strategy;
public class OrdinaryCustomerStrategy implements IDiscountStrategy {
    @Override
    public double discout(double price) {
        System.out.println("普通顾客不打折.......");
        return price;
    }
}

会员策略

package strategy;
/**
*
* Title: Member
* Description: 会员 9折
* @author yacong_liu Email:2682505646@qq.com
* @date 2019年1月22日
*/
public class MemberStrategy implements IDiscountStrategy {
    @Override
    public double discout(double price) {
        System.out.println("会员打9折......");
        return price * 0.9;
    }
}

超级会员策略

package strategy;
/**
*
* Title: SuperMember Description: 超级会员 8 折
*
* @author yacong_liu Email:2682505646@qq.com
* @date 2019年1月22日
*/
public class SuperMemberStrategy implements IDiscountStrategy {
    @Override
    public double discout(double price) {
        System.out.println("超级会员打8折......");
        return price * 0.8;
    }
}

Context 持有抽象策略 调用具体方法

package strategy;
/**
*
* Title: Context Description: 策略类的引用
*
* @author yacong_liu Email:2682505646@qq.com
* @date 2019年1月22日
*/
public class Context {
    private IDiscountStrategy strategy;
    public Context(IDiscountStrategy strategy) {
        super();
        this.strategy = strategy;
    }
    public double buy(double price) {
        return this.strategy.discout(price);
    }
}

客户端

@Test
    public void testBuy() {
        Context ctx = new Context(new MemberStrategy());
        System.out.println("会员最终价格:" + ctx.buy(300));
        Context ctx2 = new Context(new OrdinaryCustomerStrategy());
        System.out.println("普通顾客最终价格:" + ctx2.buy(300));
        Context ctx3 = new Context(new SuperMemberStrategy());
        System.out.println("超级会员最终价格:" + ctx3.buy(300));
        /*
         * 此时 需要客户端自己确定选择那种打折策略进行实例化 需要进行解耦。因此需要借助简单工厂,封装一个产生打折策略对象过程的类
         */
    }

运行结果

会员打9折......
会员最终价格:270.0
普通顾客不打折.......
普通顾客最终价格:300.0
超级会员打8折......
超级会员最终价格:240.0
上述的客户端测试类中可以看到,使用的过程中 客户端需要自己知道调用的具体策略,造成了耦合。因此下面进行使用工厂模式进行解耦。
 
构造策略工厂类,根据购买价格自行判定 构造具体的策略类
package strategy.factory;
import strategy.IDiscountStrategy;
import strategy.MemberStrategy;
import strategy.OrdinaryCustomerStrategy;
import strategy.SuperMemberStrategy;
/**
*
* Title: PriceFactory Description: 策略工厂类
*
* @author yacong_liu Email:2682505646@qq.com
* @date 2019年1月22日
*/
public class PriceFactory {
    public static PriceFactory getInstance() {
        return new PriceFactory();
    }
    public IDiscountStrategy getStrategy(double price) {
        if (price < 1000) {
            return new OrdinaryCustomerStrategy();
        } else if (price >= 1000 && price < 2000) {
            return new MemberStrategy();
        } else {
            return new SuperMemberStrategy();
        }
    }
}

此时的Context 改造如下

package strategy.factory;
import strategy.IDiscountStrategy;
public class Context {
    @SuppressWarnings("unused")
    private IDiscountStrategy strategy = null;
    public double buy(double price) {
        IDiscountStrategy strategy =  PriceFactory.getInstance().getStrategy(price);
        return strategy.discout(price);
    }
}

客户端

@Test
    public void testBuyByFactory() {
        strategy.factory.Context ctx = new strategy.factory.Context();
        System.out.println("顾客最终价格:" + ctx.buy(1000));
        
        System.out.println("顾客最终价格:" + ctx.buy(599));
        
        System.out.println("顾客最终价格:" + ctx.buy(2499));
        
       System.out.println(System.getProperty("user.dir"));
    }

此时可以看到,客户端不再复杂具体的策略调用,完全根据价格交给工厂类判定。

测试结果

会员打9折......
顾客最终价格:900.0
普通顾客不打折.......
顾客最终价格:599.0
超级会员打8折......
顾客最终价格:1999.2
G:\WS-Eclipse-Oxygen\Design_Pattern

此时还可以分析出 构造的 策略工厂类中也需要 知道具体有那些策略类,当增加打折策略时,需要修改这个工厂类,也会违反开闭原则,此时可以使用反射进行处理。但是所有的需求变更都是有成本的! 此处不再介绍如何搭配反射进行改造了。

 

posted @ 2019-01-23 17:37  蚂蚁分享圈  阅读(232)  评论(0编辑  收藏  举报