• 每天学习一个设计模式(二十一):行为型之策略模式


    一、基本概念

    策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern)。其定义如下:Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)

    二、通俗解释

    STRATEGY 策略模式:跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。 策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

    三、策略模式详解

    1.通用模版

    策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。下面就以一个示意性的实现讲解策略模式实例的结构。

    这个模式涉及到三个角色:

     ● 环境(Context)角色:持有一个Strategy的引用。

     ● 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

     ● 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

    环境角色类

    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() {
            //相关的业务
        }
    }
    
    public class ConcreteStrategyC implements Strategy {
    
        @Override
        public void strategyInterface() {
            //相关的业务
        }
    }

    商品打折的案例

    假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。

      根据描述,折扣是根据以下的几个算法中的一个进行的:

      算法一:对初级会员没有折扣。

      算法二:对中级会员提供10%的促销折扣。

      算法三:对高级会员提供20%的促销折扣。

      使用策略模式来实现的结构图如下:

    抽象折扣类

    public interface MemberStrategy {
        /**
         * 计算图书的价格
         * @param booksPrice    图书的原价
         * @return    计算出打折后的价格
         */
        public double calcPrice(double booksPrice);
    }

    初级会员折扣类

    public class PrimaryMemberStrategy implements MemberStrategy {
    
        @Override
        public double calcPrice(double booksPrice) {
            
            System.out.println("对于初级会员的没有折扣");
            return booksPrice;
        }
    
    }

    中级会员折扣类

    public class IntermediateMemberStrategy implements MemberStrategy {
    
        @Override
        public double calcPrice(double booksPrice) {
    
            System.out.println("对于中级会员的折扣为10%");
            return booksPrice * 0.9;
        }
    
    }

    高级会员折扣类

    public class AdvancedMemberStrategy implements MemberStrategy {
    
        @Override
        public double calcPrice(double booksPrice) {
            
            System.out.println("对于高级会员的折扣为20%");
            return booksPrice * 0.8;
        }
    }

    价格类

    public class Price {
        //持有一个具体的策略对象
        private MemberStrategy strategy;
        /**
         * 构造函数,传入一个具体的策略对象
         * @param strategy    具体的策略对象
         */
        public Price(MemberStrategy strategy){
            this.strategy = strategy;
        }
        
        /**
         * 计算图书的价格
         * @param booksPrice    图书的原价
         * @return    计算出打折后的价格
         */
        public double quote(double booksPrice){
            return this.strategy.calcPrice(booksPrice);
        }
    }

    客户端类

    public class Client {
    
        public static void main(String[] args) {
            //选择并创建需要使用的策略对象
            MemberStrategy strategy = new AdvancedMemberStrategy();
            //创建环境
            Price price = new Price(strategy);
            //计算价格
            double quote = price.quote(300);
            System.out.println("图书的最终价格为:" + quote);
        }
    }

    从上面的示例可以看出,策略模式仅仅封装算法,提供新的算法插入到已有系统中,以及老算法从系统中“退休”的方法,策略模式并不决定在何时使用何种算法。在什么情况下使用什么算法是由客户端决定的。

    2.策略模式的优缺点

    策略模式的优点

    ●算法可以自由切换

    这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装,保证对外提供“可自由切换”的策略。

    ●避免使用多重条件判断

    如果没有策略模式,我们想想看会是什么样子?一个策略家族有5个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。

    ●扩展性良好

    这甚至都不用说是它的优点,因为它太明显了。在现有的系统中增加一个策略太容易了,只要实现接口就可以了,其他都不用修改,类似于一个可反复拆卸的插件,这大大地符合了OCP原则。

    策略模式的缺点

    ●策略类数量增多

    每一个策略都是一个类,复用的可能性很小,类数量增多。

    ●所有的策略类都需要对外暴露

    上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。

    3.策略模式的使用场景

    ●多个类只有在算法或行为上稍有不同的场景。

    ●算法需要自由切换的场景。

    例如,算法的选择是由使用者决定的,或者算法始终在进化,特别是一些站在技术前沿的行业,连业务专家都无法给你保证这样的系统规则能够存在多长时间,在这种情况下策略模式是你最好的助手。

    ●需要屏蔽算法规则的场景。

    现在的科技发展得很快,人脑的记忆是有限的(就目前来说是有限的),太多的算法你只要知道一个名字就可以了,传递相关的数字进来,反馈一个运算结果,万事大吉。

    注意 如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就会成为一个烫手山芋,谁都不想接。


    参考秦小波的《设计模式之禅(第2版)》

  • 相关阅读:
    通信中几种复用方式的介绍
    通信的一些基本概念整理
    网易有道2017内推选择题
    腾讯2017暑期实习生编程题
    MATLAB的一些应用--最近用的比较多
    (十六)命令模式-代码实现
    (十四)观察者模式-代码实现
    (十三)备忘录模式-代码实现
    (十二)模板模式-代码实现
    (十一)享元模式-代码实现
  • 原文地址:https://www.cnblogs.com/aohongzhu/p/15174389.html
Copyright © 2020-2023  润新知