Strategy设计模式浅析

Strategy设计模式

什么式Strategy设计模式呢?字面意思来看,Strategy模式翻译过来就是策略模式。听到策略这个词我们自然会联想到计谋、计策之类的字眼。所以,策略模式就是一个面向计策的一个设计模式。在程序中,所谓计策就是算法。但是,话说回来,解决一个问题没有一个统一的算法,也没有一个算法可以解决全部的问题。那么我们的策略模式,自然也就不是一个解决全部问题的一种设计模式。但是,算法有一个特性,那就是它可以解决问题,这个是所有的算法的共同点,那么我们可以将这个共同点抽取出来,于是就有了我们的策略模式。

一个小demo开始

假如现在有一个问题摆在你眼前,让你去计算一个由字符串呈现的1+2-3*(4-1)表达式的值。
解决这个问题,已经有了一个成熟的算法,那就是中缀表达式的求值算法(笔者在这里不在赘述算法的实现流程,不知道的可以自行百度学习)。而且我们也知道,中缀表达式是一个稳定的算法。其大致流程为:
在这里插入图片描述
虽然算法稳定,但是,算法的流程中有一个不太稳定的因素,那就是运算符。我们知道,我们不可能把所有的运算符都枚举在算法中,而且一旦所支持的运算符数目增多,那么我们的算法中将出现很多的if-else分支或者switch-case分支。这对于程序设计来说是非常不友好的,而且是非常不雅观的,而且,这个违背了软件设计的一个原则,那就是开闭原则——我们的软件对扩展是开放的,对修改是关闭的。
不妨我们假设有一个场景,突然有一天我们要增加支持一种新的运算符,我们要怎么做的。显然最直接的办法,就是多一层if-else判断,或者多一层switch-case语句。但是这显然与我们的开闭原则相违背。我们这样做是修改了源代码,而不是扩展了源代码。可能这句话听起来有点别扭,我添加了代码,为什么不是扩展。其实,这是因为,这里所谓的添加不能够做到编译单元的独立,所以这种行为称之为修改源代码。
那么,用strategy设置模式该怎么去书写呢?其实很简单,只要记住我们的面向对象设计原则一即可:高层模块不应该依赖于低层模块,二者皆应该依赖于抽象。

代码实现

首先我们定义一个运算符的抽象类

class OperatorStrategy{

public:
    virtual ~OperatorStrategy(){};

    virtual void run(Stack<float> *num)=0;
};

这个抽象类,他有一个抽象方法run。这个抽象方法就是我们运算符对应的操作的实现。
然后,我们就可以定义我们的运算符操作类了。比如我们想支持加法运算操作

class AddStrategy
    :public OperatorStrategy{

public:
    void run(Stack<float> *num){
        float n2 = num->pop();
        float n1 = num->pop();
        num->push(n1 + n2);
    }

};

抑或是我们想支持减法操作

class SubtractStrategy
    :public OperatorStrategy{

public:
    void run(Stack<float> *num){

        float n2 = num->pop();
        float n1 = num->pop();
        num->push(n1 - n2);
    }

};

乘除操作自然也是可以

class MultiStrategy
    :public OperatorStrategy{

public:
    void run(Stack<float> *num){
        float n2 = num->pop();
        float n1 = num->pop();
        num->push(n1 * n2);
    }
};

class DivideStrategy
    :public OperatorStrategy{

public:
    void run(Stack<float> *num){
        float n2 = num->pop();
        float n1 = num->pop();
        num->push(n1 / n2);
    }

};

然后我们定义一个类工厂

class OperatorStrategyFactory{

private:
    vector<OperatorStrategy*> operatorArray;
public:

    OperatorStrategyFactory(){
        operatorArray.push_back(new AddStrategy());
        operatorArray.push_back(new SubtractStrategy());
        operatorArray.push_back(new MultiStrategy());
        operatorArray.push_back(new DivideStrategy());
    }

    OperatorStrategy* getStrategy(char symbol){
        int index = getOperatorIndex(symbol);
        return operatorArray[index];
    }

};

这个类工厂在被实例出来之后,将会把其所支持的运算符操作加载到容器中。同时类工厂提供了一个getStrategy的方法,用于我们获取相应的运算符操作类。注意,在这里我们的OperatorStrategy*的指针是一个多态指针,他可指向任何继承自它的子类。

然后就是我们的中缀表达式的实现部分了

while( (symbol = expression[i]) ){

        if( symbol >= '0' && symbol <= '9' ){
            i += readNumber(expression, i,NUMBER);
            continue;
        }else if ( isOperator(symbol) ){
            char stackTopSymbol = OPERATOR->getTopValue();
            // get those symbol's index that at priority table
            int t = getOperatorIndex(stackTopSymbol);
            int n = getOperatorIndex(symbol);
			/*
			* action:
			*  0    ->    pop and calculate
			*  1    ->    push and next
			*  2    ->    pop and next
			*  -1   ->    invalid
			*/
            int action = getAction(t, n);
            if( action == 0 ){
                char st = OPERATOR->pop();
                OperatorStrategy* op = factory->getStrategy(st);
                if( op ){
                    op->run(NUMBER);
                    continue;
                }else{
                    printf("invalid expression\n");
                    break;
                }
            }else if( action == 1 ){
                OPERATOR->push(symbol);
                i++;
                continue;
            }else if( action == 2 ){
                OPERATOR->pop();
                i++;
                continue;
            }else if( action == -1 ){
                printf("invalid expression\n");
                break;
            }

        }else{
            i++;
        }
    }

不难看到,在代码块

 if( action == 0 ){
 	char st = OPERATOR->pop();
    OperatorStrategy* op = factory->getStrategy(st);
     if( op ){
         op->run(NUMBER);
         continue;
     }else{
         printf("invalid expression\n");
         break;
     }
}

我们发现通过借助OperatorStrategy*这根多态指针,我们就可以将if-else分支简化为一行代码。而且,我们也实现了编译单元的独立,我们只需将所有OperatorStrategy的实现类包括它自身放到一个独立的文件中。这样当我们需要增添新的运算符时,只需要将运算符实现类所在文件编译即可,无需关注中缀表达式实现所在的文件。

扫描二维码关注公众号,回复: 11322034 查看本文章

猜你喜欢

转载自blog.csdn.net/hbn13343302533/article/details/103002110