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

    2018-09-21 15:54:34

    策略模式

      工厂系列模式只是解决了对象创建的问题。策略(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。算法本身是一种策略,而且这种策略是随时都可能相互替换的,这就是变化点,而封装变化点是面向对象的一种很重要的思维方式。策略模式是一种定义一系列算法的方式,从概念上卡,所有这些算范完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

    UML类图

      Context:上下文,用一个ConcreteStrategy来配置,持有一个Strategy的引用,最终交给客户端代码使用

      Strategy:策略类,定义所有支持的算法的公共接口

      ConcreteStrategy:具体策略类,封装了具体的算法或行为,继承于Strategy。

    策略模式的优缺点

    优点:

      1.策略模式的Strategy类层次为Context定义了一些列的可供重用的算法或行为。继承有助于析取出这些算法的公共功能。

      2.简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。

      3.当不同的行为堆砌在一个类中时,很难避免使用条件语句来选择合适的行为。比如你需要在客户端代码中来进行选择使用哪一种算法。将这些行为封装在一个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。策略模式就是用来封装算法的,但在实践中,我们发现可以用它来分装几乎任何类型的规则,只要在分析过程中听到需要在不同实践应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

    缺点:

      1.在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象,这本身并没有解除客户端需要选择判断的压力(也就是说客户端需要知道所有的策略类型)。但是可以通过工厂模式进行一定程度的改进。

      2.但策略类增加时,将可能产生大量的子类,如果一个算法被多次使用,但仅是外部条件不同的时候,可以考虑使用享元模式来进行优化,减少实例的个数,但这在涉及线程安全的时候需要格外注意。

    适用场景:

      1.需要在不同的时间应用不同的业务规则时可以考虑使用策略模式,以后规则还有可能增加,且每种规则都可以抽象成独立的类

      2.对客户端隐藏实现算法的细节,是客户端和算法之间完全独立

    代码示例

      超市打折策略:正常收费、消费满指定金额优惠一定金额、消费满指定金额给予一定的折扣。

    1.所有策略的基类(UML类图中的Strategy)

    #ifndef STRATEGY_H_
    #define STRATEGY_H_
    
    class Strategy
    {
    public:
        virtual double acceptCash(const double iOriginal) const = 0;
        Strategy() = default;
        virtual ~Strategy() = default;
    };
    #endif
    Strategy

    2.超市的三种打折策略(UML类图中的ConcreteStrategy)

    #ifndef CASHNORMAL_H_
    #define CASHNORMAL_H_
    
    #include "Strategy.h"
    
    class CashNormal : public Strategy
    {
    public:
        double acceptCash(const double dOriginalCash) const override
        {
        return dOriginalCash;
        }
        CashNormal() = default;
        ~CashNormal() = default;
    };
    #endif
    
    
    #ifndef CASHREBATE_H_
    #define CASHREBATE_H_
    
    #include "Strategy.h"
    
    class CashRebate : public Strategy
    {
    public:
        double acceptCash(const double iOriginalCash) const
        {
        if(iOriginalCash > m_dCondition)
            return iOriginalCash * m_dRebate; 
        return iOriginalCash;
        }
        CashRebate(double dCondition,double dRebate):m_dCondition(dCondition),m_dRebate(dRebate){};
        ~CashRebate() = default;
    private:
        double m_dCondition{0};        //if cash > m_dCondition,you can have a discount
        double m_dRebate{1};        //discount as m_dRebate
    };
    #endif
    
    #ifndef CASHRETURN_H_
    #define CASHRETURN_H_
    #include "Strategy.h"
    
    class CashReturn : public Strategy
    {
    public:
        double acceptCash(double dOriginalCash) const override
        {
        if(dOriginalCash > m_dCondition)
            return dOriginalCash - dOriginalCash/m_dCondition*m_dReturn;
        return dOriginalCash;
        }
        CashReturn(const double dCondition,const double dReturn): m_dCondition(dCondition), m_dReturn(dReturn){}
        ~CashReturn() = default;
    private:
        double m_dCondition{1};
        double m_dReturn{0};
    };
    #endif
    ConcreteStrategy

    3.上下文类(负责和客户代码打交道,UML类图中的Context)

    #ifndef CONTEXT_H_
    #define CONTEXT_H_
    
    #include "Strategy.h"
    
    class Context
    {
    public:
        double contextInterface(double dOriginalCash);
        Context(Strategy * pConcreteStrategy)
        {
        m_pStrategy = pConcreteStrategy;
        }
        ~Context()
        {
        }
    private:
        Strategy* m_pStrategy{nullptr};
    };
    #endif 
    
    #include "Context.h"
    
    double Context::contextInterface(double dOriginalCash)
    {
        if(nullptr == m_pStrategy)
        return 0;
        return m_pStrategy->acceptCash(dOriginalCash);
    }
    Context

    4.Client

    #include "Context.h"
    #include "CashNormal.h"
    #include "CashRebate.h"
    #include "CashReturn.h"
    #include <iostream>
    #include <string>
    using namespace std;
    const std::string RETURN = "return";
    const std::string REBATE = "rebate";
    const std::string NORMAL = "normal"; 
    int main(int argc,char *argv[])
    {
        if(argc != 2)
        {
        cout << "Param count is error!" <<endl;
        }
        string strCommand = argv[1];
        if(strCommand == RETURN)
        {
        CashReturn objCashReturn(300,100);
            Context objCashContext(&objCashReturn);
            cout << "Amount receivable :" << objCashContext.contextInterface(400);
        }
        else if(strCommand == REBATE)
        {
        CashRebate objCashRebate(300,0.8);
            Context objCashContext(&objCashRebate);
            cout << "Amount receivable :"<< objCashContext.contextInterface(350);
        }
        else if(strCommand == NORMAL)
        {
        CashNormal objCashNormal;
            Context objCashContext(&objCashNormal);
            cout << "Amount receivable :" << objCashContext.contextInterface(500);
        }
        else
            cout << "Command is Error " << endl;
        return(1);
    }
    Client

      总结:其实从这个小例子能看出来策略模式的最大优点是通过Context类隔绝了策略的具体实现和客户端代码的关系,并有利于横向扩展,但这里的问题是,客户代码必须要知道每一个策略类叫什么名字,才能正确的使用这个策略。优化的时候可以考虑使用工厂模式,但使用工厂模式的话,必须要考虑,怎样把策略需要满足的条件传递过去,使得整个模式能够正常的工作。

      

  • 相关阅读:
    PSE Access Service
    The JXTA Migration
    JXSE 2.5 : What's Cool #6 PeerGroup Executor and ScheduledExcutor
    JXTA Kitchen
    LookupListener中的resultChanged方法是在EDT中执行么?
    同一台机器启动两个结点时的端口冲突问题
    (转)OpenSSL中对称加密算法的统一接口
    关于“未能加载文件或程序集“System.Core, Version=3.5.0.0
    暗香浮动的夜晚
    java xml序列化与反序列化
  • 原文地址:https://www.cnblogs.com/ToBeExpert/p/9687944.html
Copyright © 2011-2022 走看看