zoukankan      html  css  js  c++  java
  • 策略模式

     
    策略模式(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-OxygenDesign_Pattern

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

  • 相关阅读:
    Text Rendering in the QML Scene Graph
    freetype2文档部分翻译
    一些距离测算方法
    制作交叉工具链
    图像处理链接
    Scene Management scene graph
    Google的九条创新原则
    C#颜色和名称样式对照表【转载】
    sql语句性能优化【转载】
    数据挖掘十大经典算法【转载】
  • 原文地址:https://www.cnblogs.com/lyc-smile/p/10310386.html
Copyright © 2011-2022 走看看