如果不同的税法的计算,会有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 的目的是为了对应状态和折扣策略。