设计模式之策略模式

如果不同的税法的计算,会有if ..else 或者swtich case default的办法
不要静态的去看一个软件,要动态的去看,如果未来会有新的税法的计算方法


自我感觉:工厂模式是生成新的对象去调用不同的方法,策略模式可能是同一对象的不同分支。

1. 策略模式呢,是为了维持逻辑稳定
2. 把需要变动的部分抽象成接口,纯虚函数,让实现类去继承他
//原来的问题
enum TaxBase{
    CN_tax = 1;
    US_tax;
    uk_tax;
    
}
class SalesOrder{
    TaxBase tax;
public:
    double Calculate(){
        if(tax == CN_tax){}
        if(tax == US_tax){}
        if(tax == uk_tax){}
        if(tax == other){} //如果未来要加入新的国家的税法计算,就要去改源码,这个不符合开闭原则。什么叫开闭原则呢,就是对拓展开放,对更改封闭。
                            // 具体来说就是类模块应该尽可能用拓展的方式来支持未来的变化,而不是去更改源码。那什么叫扩展呢?就是继承多态的方式。
 
        else{}    
        
        
    }
}

//新的架构
class SalesOrder{
    private:
        TaxStrategy *strategy;    //一般放一个方法
        
    public:
        SalesOrder(StrategyFactory *strategyFactory)
        {
            this->strategy = strategyFactory->CreatNew(...);//或者是getInstance()方法
        }
        
        double Calculate(){    
        
            //...                
            Context context;            
            double val = strategy ->Calculate(context);//  其实是在工厂中做了这些判断,支持新的方式(这里是不对的)
        }
}

//这里还需要一个接口函数,接口函数呢,就是定义了方法,工具
class TaxStrategy {
public:
    virtual double Calculate(const Context& context) = 0;
    virtual ~TaxStrategy() {
    }
};
//上面的是不动的 ,支持未来的方法的拓展,下面的都是实例

class CN_tax : public TaxStrategy{
    //
    virtual double Calculate(const Context context) override{
        //重写
    }    
}

class USTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};

class DETax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //***********
    }
};

//扩展
//*********************************
class FRTax : public TaxStrategy {
public:
    virtual double Calculate(const Context& context) {
        //.........
    }
};


//那么factory应该怎么写,20231118 其实是理解上有问题,不是不同对象的不同方法,而是同一个对象的不同分支。
//可以参照:https://www.zhihu.com/question/21656093/answer/2397084894
class StrategyFactory{
    public:
        StrategyFactory();
        virtual ~StrategyFactory();
        
        virtual TaxStrategy *CreatNew()=0;
}

//可变的
class A :Publish StrategyFactory{
    public:
        A();
        virtual ~A();
        
        virtual TaxStrategy *CreatNew()
        {
            return A;
        }
        
}

//但是这里有问题,问题就是该怎么处理呢 就是其实去做项目的实体是一个 但是我搞出来了两个,所以怎么办呢:
//问题是工厂模式生产的是产品,行为是产品的行为,所以我觉得应该吧TaxStrategy就是产品。

//那么factory应该不变,工厂类不被继承?
class StrategyFactory{
    public:
        StrategyFactory();
        virtual ~StrategyFactory();
        
        virtual TaxStrategy *CreatNew()=0;    
        
}
 
 
//这么再串起来
class CN_tax : public TaxStrategy{
    //
    virtual double Calculate(const Context context) override{
        //重写
    }    
}

//---------------------------------另外一篇文章对策略模式的理解-https://www.zhihu.com/question/21656093/answer/2397084894----------------------------------------------------
1、策略模式

假设您目前正在从事一个电子商务商店

的项目。每个产品都有一个原价,我们可以称之为 originalPrice。但并非所有产品都以原价出售,我们可能会推出允许以折扣价出售商品的促销活动。商家可以在后台为产品设置不同的状态。然后实际售价将根据产品状态和原价动态调整。

具体规则如下:

部分产品已预售。为鼓励客户预订,我们将在原价基础上享受 20% 的折扣。

部分产品处于正常促销阶段。如果原价低于或等于100,则以10%的折扣出售;如果原价高于 100,则减 10 美元。

有些产品没有任何促销活动。它们属于默认状态,以原价出售。

如果你需要写一个getPrice函数

,你应该怎么写呢?

function getPrice(originalPrice, status){
  // ...
  return price
}

其实,面对这样的问题,如果不考虑任何设计模式,最直观的写法可能就是使用if-else

通过多个判断语句来计算价格。

有三种状态,所以我们可以快速编写如下代码:

function getPrice(originalPrice, status) {
  if (status === 'pre-sale

') {
    return originalPrice * 0.8
  }

  if (status === 'promotion') {
    if (origialPrice <= 100) {
      return origialPrice * 0.9
    } else {
      return originalPrice - 20
    }
  }

  if (status === 'default') {
    return originalPrice
  }
}

有三个条件;然后,我们写三个 if 语句,这是非常直观的代码。

但是这段代码并不友好。

首先,它违反了单一职责原则

。主函数 getPrice 做了太多的事情。这个函数不易阅读,也容易出现bug。如果一个条件有bug,整个函数就会崩溃。同时,这样的代码也不容易调试。

然后,这段代码很难应对变化。正如我在文章开头所说的那样,设计模式往往会在业务逻辑发生变化时表现出它的魅力。

假设我们的业务扩大了,现在还有另一个折扣促销:黑色星期五

,折扣规则如下:

    价格低于或等于 100 美元的产品以 20% 的折扣出售。价格高于 100 美元但低于 200 美元的产品将减少 20 美元。价格高于或等于 200 美元的产品将减少 20 美元。

这时候怎么扩展getPrice函数呢?

看起来我们必须在 getPrice 函数中添加一个条件。

function getPrice(originalPrice, status) {
  if (status === 'pre-sale') {
    return originalPrice * 0.8
  }

  if (status === 'promotion') {
    if (origialPrice <= 100) {
      return origialPrice * 0.9
    } else {
      return originalPrice - 20
    }
  }
  if (status === 'black-friday') {
    if (origialPrice >= 100 && originalPrice < 200) {
      return origialPrice - 20
    } else if (originalPrice >= 200) {
      return originalPrice - 50
    } else {
      return originalPrice * 0.8
    }
  }

  if(status === 'default'){
    return originalPrice
  }
}

每当我们增加或减少折扣时,我们都需要更改函数。这种做法违反了开闭原则

。修改已有函数很容易出现新的错误,也会让getPrice越来越臃肿。

那么我们如何优化这段代码呢?

首先,我们可以拆分这个函数以使 getPrice 不那么臃肿。

function preSalePrice(origialPrice) {
  return originalPrice * 0.8
}

function promotionPrice(origialPrice) {
  if (origialPrice <= 100) {
    return origialPrice * 0.9
  } else {
    return originalPrice - 20
  }
}

function blackFridayPrice(origialPrice) {
  if (origialPrice >= 100 && originalPrice < 200) {
    return origialPrice - 20
  } else if (originalPrice >= 200) {
    return originalPrice - 50
  } else {
    return originalPrice * 0.8
  }
}

function defaultPrice(origialPrice) {
  return origialPrice
}

function getPrice(originalPrice, status) {
  if (status === 'pre-sale') {
    return preSalePrice(originalPrice)
  }

  if (status === 'promotion') {
    return promotionPrice(originalPrice)
  }

  if (status === 'black-friday') {
    return blackFridayPrice(originalPrice)
  }

  if(status === 'default'){
    return defaultPrice(originalPrice)
  }
}

经过这次修改,虽然代码行数增加了,但是可读性有了明显的提升。我们的main函数

显然没有那么臃肿,写单元测试也比较方便。

但是上面的改动并没有解决根本的问题:我们的代码还是充满了if-else,当我们增加或减少折扣规则的时候,我们仍然需要修改getPrice。

想一想,我们之前用了这么多if-else,目的是什么?

实际上,使用这些 if-else 的目的是为了对应状态和折扣策略。

你可能感兴趣的:(笔记)