我将4年前的设计模式笔记再看一遍(2),Strategy

81 阅读4分钟

一、模式总结

组件协作模式分类,通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。

这个模式要处理掉的问题是,对(可能)需要扩展的if else进行改写。当我们看到许多if else时,就需要敲响警钟,可能需要对其进行重构,这个重构的结果,就是Strategy(策略)模式。

改进之后,可以配合工厂模式使用。

1、原始代码

按照下面代码实现,当需要添加一个国家的Tax计算,则需要在枚举中再加一项,并且需要再加一个else if才能实现。如果需要加的内容很多,那么else if将会一直添加下去,代码不好看。

enum TaxBase {
    CN_Tax = 1,
    US_Tax,
    DE_Tax,
};

class SalesOrder {
    TaxBase tax;
public:
    double calculateTax(){
        // ...
        
        if (tax == CN_Tax){
            // CN *****************
        }
        else if (tax == US_Tax){
            // US *****************
        }
        else if (tax == DE_Tax){
            // DE *****************
        }
        
        // ...
    }    
};

2、改进代码

当需要添加一种新的税务计算类型的时候,再新建一个继承自TaxStrategy的类,SalesOrder类是不用进行更改的。SalesOrder的调用,是C++多态的应用。

class TaxStrategy{
public:
    virtual double calculate(const Context& context) 0;
    virtual ~TaxStrategy();  // 虚析构函数,提过多次,基类必须要,原因还待弄清楚
};

class CNTax : public TaxStrategy{
public:
    virtual double calcuate(cont Context& context){
        // ****************
    }
}

class USTax : public TaxStrategy{
public:
    virtual double calcuate(cont Context& context){
        // ****************
    }
}

class DETax : public TaxStrategy{
public:
    virtual double calcuate(cont Context& context){
        // ****************
    }
}

class SalesOrder{
private:
    TaxStrategy* strategy;
    
public:
    SalesOrder(StrategyFactory* strategyFactory){
        this->strategy = strategyFactory->newStrategy();
    }
    ~SalesOrder(){
        delete this->strategy;
    }
    
    double calculateTax(){
        // ...
        Context context();
        
        double val = strategy->calculate(context);
        
        // ...
    }
};

二、我的使用场景

我在项目中,有过一些优化代码的写法。在使用Python、Lua进行逻辑开发的时候,往往会有一大片的if else(switch case)出现。我会制作一个函数表,通过key去拿到对应的函数,然后再调用该函数。当新的if选项进来的时候,不需要在一大坨if else后面添加内容,只需要写上一个独立函数,再在函数表中加上注册。

由此,看起来结构很清晰,每一个函数只做自己需要做的事情便好。

以上,应该算得上是策略模式的应用,只是需要示例中的class,是我代码中的函数。(四年后补充:Python中甚至于可以不用使用额外table存储函数,直接getattr拿到函数也ok。)

另外,Task.Event中,有一个管理各式EventClass的表,是策略模式的完美应用。(时隔四年,看到这句话,我依然能够想起Task.Event的实现,和上面改进代码一致,都是流程备好,具体做事交给各个独立的Event。)

# 四年后于此处添加一个Python中的小小示例好啦
def oldStyle(inputNum):
    if inputNum == 1:
        print('do sth 1.')
        print('do sth 2.')
        print('do sth 3.')
    elif inputNum == 2:
        print('do sth 3.')
        print('do sth 2.')
        print('do sth 4.')
    elif inputNum == 3:
        print('do sth 5.')
        print('do sth 6.')
        print('do sth 4.')
    # elif ...  # add newline here

# --------------------

def func1():
    print('do sth 1.')
    print('do sth 2.')
    print('do sth 3.')


def func2():
    print('do sth 3.')
    print('do sth 2.')
    print('do sth 4.')


def func3():
    print('do sth 5.')
    print('do sth 6.')
    print('do sth 4.')


funcTbl = {
    1: func1,
    2: func2,
    3: func3,
}


def newStyle(inputNum):
    # if we need num 4, so just add one func in funcTbl
    func = funcTbl.get(inputNum)
    if func:
        # func当然可以加参数
        func()

三、各种tip

1、老师在视频中多次提到《重构》,需要看完,只剩一半啦。2020-7-6。

2、绝对稳定不变的场景,是可以固定if else的,比如一周有7天。

3、代码具有良好的本地性,代码在代码段,进入CPU高级缓存。大概意思是,不要把不被使用的代码放入代码段。而减少if else的使用,是有帮助的。听讲的时候有点明白,但要我用自己的语言记录下来,便差点意思了。说明还差点意思。

(哈哈,四年后好像不太差意思啦。老师的意思是,代码会存储在代码段,代码段的内容最终是需要在CPU上面执行的,而代码进入CPU,需要走过内存、CPU缓存,而缓存的容量有限,如果我们代码的长度短小,那么发生缓存不命中的概率则会降低,由此,会有一些性能提升。这算是策略模式于此处的附加好处。)

4、目前看了两个模式,发现代码中多多少少都有接触到过。和大学时候的云里雾里感觉,很有些不一样。(对的,理论结合实践才是完美的。大学上设计模式课时,老师在第一节课中说:“当我第一次看见设计模式时,感觉豁然开朗、相见恨晚。”在我带着许多编码经验后来看建忠老师视频时,也有相似感受。)