• 案例分析:设计模式与代码的结构特性


        软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

      本文主要介绍设计模式中的策略模式,将对其目的、结构进行分析,包括各个模块的内聚度和模块之间的耦合度,并提供C++范例,说明封装方法。

      一、策略模式目的

        定义一系列算法,把它们一个个封装起来,并且时它们可以相互替换,使得算法可以独立于使用它们的客户端而变化。

      二、为什么要使用策略模式?

         试想一个程序需要支持不同算法来分析一个文本流;如果程序直接包含分析文本算法的代码,那么当需要支持多种分析算法的时候程序将变得庞大且难以维护,算法和使用算法的代码高度耦合在一起,增加新的算法或改变现有算法将十分困难。

        那么如果定义一个算法接口类,不同算法作为不同的子类继承这个接口呢?这也会导致一些问题,如动态改变算法会比较困难,最后你得到了一堆相关的类,它们之间唯一的差别是所使用的算法或者行为。

      三、策略模式结构

        这个时候策略模式便应运而生了。我们定义一个抽象的Strategy类包含一个Algorithm接口,使用子类ConcreteStrategyA/B/C来支持具体策略;Context类由一个Strategy对象配置,并维护对它的引用。整个结构如下:

        

        通常将Strategy实现为无状态的对象,其余的状态都由Context来维护,Context在每一次对Strategy对象的请求中都将这个状态传递过去,Strategy对象不应该在各次调用之间维护状态。

        具体实现上需要考虑如何让Context传递给Strategy所需的状态。

        方法一,可以让Context将数据放在参数中传递给Strategy操作,这使得Strategy和Context解耦,但另一方面Context可能发送一些Strategy不需要的数据。

        方法二,让Context自身作为一个参数传递给Strategy,该Strategy再显示地调用Context方法请求数据,或者Strategy可以存储它对Context的一个引用,这样根本不需要在调用时传递任何东西。但现在Context必须对它的数据定义一个更为精细的接口,这将Strategy和Context紧密耦合在一起。

        方法三,将Strategy作为Context的模板参数。但这种方式仅仅当满足如下条件时才可以用:①可以在编译时选择Strategy;②不需要在运行时改变。    

             

    template<class Astrategy>
    class Context{
    public:
        void operation(){ 
            theStrategy.DoAlgorithm();
        }
    private:
        Astrategy theStrategy;
    };
    
    class MyStrategy{
    public:
        void DoAlgorithm();
    };
    
    Context<MyStrategy> aContext;

        使用模板不需要给Strategy定义接口的抽象类。把Strategy作为一个模板参数也使得可以将一个Strategy和它的Context静态绑定在一起,从而提高效率。

       四、示例分析

        假设我们有一个APP,用户登录时支持多种方式验证用户身份,包含Basic、Digest、OpenID、OAuth,我们首先想到可以用如下方式验证代码

    class BasicAuth {}
    class DigestAuth {}
    class OpenIDAuth {}
    class OAuth {}
    
    class AuthProgram {
        runProgram(authStrategy:any, ...) {
            this.authenticate(authStrategy)
            // ...
        }
        authenticate(authStrategy:any) {
            switch(authStrategy) {
                if(authStrategy == "basic")
                    useBasic()
                if(authStrategy == "digest")
                    useDigest()
                if(authStrategy == "openid")
                    useOpenID()
                if(authStrategy == "oauth")
                    useOAuth()
            }
        }
    }

      这种方式需要让AuthProgram以来一长串switch-case,并且当我们只需要使用一种验证方式时,也得进行一长串判断。而如果我们使用Strategy Pattern,可以给所有验证方式定义公共接口:

    interface AuthStrategy {
        auth(): void;
    }
    class Auth0 implements AuthStrategy {
        auth() {
            log('Authenticating using Auth0 Strategy')
        }
    }
    class Basic implements AuthStrategy {
        auth() {
            log('Authenticating using Basic Strategy')
        }
    }
    class OpenID implements AuthStrategy {
        auth() {
            log('Authenticating using OpenID Strategy')
        }
    }

      而AuthenProgram可以简化许多:

    class AuthProgram {
        private _strategy: AuthStrategy
        use(strategy: AuthStrategy) {
            this._strategy = strategy
            return this
        }
        authenticate() {
            if(this._strategy == null) {
                log("No Authentication Strategy set.")
            }
            this._strategy.auth()
        }
        route(path: string, strategy: AuthStrategy) {
            this._strategy = strategy
            this.authenticate()
            return this
        }
    }

      调用Authen:

     // Authenticating using OpenID Strategy
    new AuthProgram().use(new OpenID()).authenticate()

      可以看到现在的AuthenProgram没有了一长串条件判断语句,我们只需要为其设置好Strategy,让AuthenProgram直接调用即可,实现AuthenProgram与具体的验证算法实现方式解耦。需要什么方式进行验证,只需要传入验证Strategy给验证程序即可。

      

        

       

        

  • 相关阅读:
    线性回归(Linear Regression)的理解及原理
    3个模型搞清楚用户留存分析
    机器学习简单介绍
    数据分析经典方法:5W2H分析法
    使用guava RateLimiter限流
    Maven之assembly自定义打包
    IDE自动编译
    神奇的$scope
    二分法查找
    深入理解CSS选择器优先级
  • 原文地址:https://www.cnblogs.com/demonatic/p/12006951.html
Copyright © 2020-2023  润新知