知道网站域名怎么联系,长春哪里做网站好,个人网站主页怎么做,大连小程序哪个开发公司好本篇文章#xff0c;来解读《大话设计模式》的第2章——策略模式。并通过Qt和C代码实现实例代码的功能。
1 策略模式
策略模式作为一种软件设计模式#xff0c;指对象有某个行为#xff0c;但是在不同的场景中#xff0c;该行为有不同的实现算法。
策略模式的特点#…本篇文章来解读《大话设计模式》的第2章——策略模式。并通过Qt和C代码实现实例代码的功能。
1 策略模式
策略模式作为一种软件设计模式指对象有某个行为但是在不同的场景中该行为有不同的实现算法。
策略模式的特点
定义了一组算法业务规则封装了每个算法这一类的算法可互换代替
策略模式的组成
抽象策略角色策略类 通常由一个接口或者抽象类实现具体策略角色包装了相关的算法和行为环境角色上下文持有一个策略类的引用或指针最终给客户端调用 策略模式Strategy它定义了算法家族分别封装起来让它们之间可以互相替换此模式让算法的变化不会影响到使用算法的客户。 2 收银软件实例 题目做一个商场收银软件营业员根据用户所购买商品的单价和数量向客户收费 我们联想策略模式对于收费行为在不同的场景中正常收费、打折收费、满减收费对应不同的算法或称策略实现。
下面先来看版本一还未使用策略模式仅实现基础的收费计算。
2.1 版本一基础收费
这里使用Qt设计一个收费系统的界面每次可以输入单价和数量点确定按钮之后会在信息框中展示此次的合计价格支持多个商品的多次计算多次计算的总价在最下面的总计栏中展示。 对应的代码实现如下
on_okBtn_clicked 为Qt点击确定按钮后的槽函数该函数实现为此次的价格合计等于价格x数量多次的价格累加是总计价格。on_resetBtn_clicked 为Qt点击重置按钮后的槽函数该函数实现为清空相关的显示和各种数据
void Widget::on_okBtn_clicked()
{// 此次的价格合计价格*数量float thisPrice ui-priceEdit-text().toFloat() * ui-numEdit-text().toInt();// 总计m_fTotalPrice thisPrice;// 窗口中展示明细ui-showPanel-append(price: ui-priceEdit-text() , num: ui-numEdit-text() - ( QString::number(thisPrice) ));// 显示总计ui-totalShow-setText(QString::number(m_fTotalPrice));
}void Widget::on_resetBtn_clicked()
{m_fTotalPrice 0;ui-showPanel-clear();ui-totalShow-clear();ui-priceEdit-clear();ui-numEdit-clear();
}
实际的演示效果如下仅实现单价x数量功能 如果在此基础上需要增加打折收费功能需要怎么做呢下面来看版本二。
2.2 版本二增加打折
对于打折功能在界面上只需要增加一个打折率的下拉框即可然后在计算公式上在加一步乘以打折率即可代码改动不大
void Widget::on_okBtn_clicked()
{// 根据下拉框获取对应的打折率float rebate 1.0;if (ui-calcSelect-currentIndex() 1) rebate 0.8;else if (ui-calcSelect-currentIndex() 2) rebate 0.7;else if (ui-calcSelect-currentIndex() 3) rebate 0.5;// 此次的价格合计价格*数量*打折率float thisPrice ui-priceEdit-text().toFloat() * ui-numEdit-text().toInt() * rebate;// 总计m_fTotalPrice thisPrice;// 窗口中展示明细ui-showPanel-append(price: ui-priceEdit-text() , num: ui-numEdit-text() , rebate: QString::number(rebate) - ( QString::number(thisPrice) ));// 显示总计ui-totalShow-setText(QString::number(m_fTotalPrice));
}演示效果如下可以支持正常收费、八折收费、七折收费和五折收费。 目前看起来代码也还可以但如果此时需要增加满减活动呢比如满300减100这种。
因为满减这种方式不像打折那样简单的乘以一个打折率就行了它需要两个参数**满减的价格条件**的满减的优惠值对于满300减100的方式如果是700满足了2次就要减200了这种计算方式需要单独再写一套计算逻辑。
下面来看版本三是如何实现的。
2.3 版本三简单工厂
联想上次介绍的简单工厂模式对于目前收费的需求实际可以将其分类三类
正常收费类不需要参数打折收费类需要1个参数打折率满减收费类返利收费类需要2次参数满减的价格条件的满减的优惠值
因此可以将这3钟方式分别封装为单独的收费类并通过简单工厂的方式在不同的收费需求下实例化对应的收费计算对象进行收费的计算。 2.3.1 收费类相关代码
对应的代码如下设计了现金收费类CashSuper以及对应的具体子类
正常收费类CashNormal将原价原路返回打折收费类CashRebate初始化时输入打折率计算时返回打折后的价格返利收费类CashReturn初始化时输入满减的条件和满减的值计算时返回满减后的值
// 现金收费类
class CashSuper
{
public:virtual float acceptCash(float money){return money;}
};// 正常收费类
class CashNormal : public CashSuper
{
public:// 原价返回float acceptCash(float money){return money;}
};// 打折收费类
class CashRebate : public CashSuper
{
private:float m_fMoneyRebate 1.0;public:// 初始化时输入打折率CashRebate(float rebate){m_fMoneyRebate rebate;}// 返回打折后的价格float acceptCash(float money){return money * m_fMoneyRebate;}
};// 返利收费类
class CashReturn : public CashSuper
{
private:float m_fMoneyCondition 0;float m_fMoneyReturn 0;
public:// 初始化时输入满减的条件和满减的值CashReturn(float moneyCondition, float moneyReturn){m_fMoneyCondition moneyCondition;m_fMoneyReturn moneyReturn;}public:// 返回满减后的值满足满减倍数按倍数满减float acceptCash(float money){float result money;if (money m_fMoneyCondition){result - ((int) money / (int) m_fMoneyCondition) * m_fMoneyReturn;}return result;}
};//现金收费工厂类
class CashFactory
{
public:CashSuper *createCashAccept(int combIdx) // 参数为下拉列表中的索引{CashSuper *pCS nullptr;switch (combIdx){case 0: // 正常收费{pCS (CashSuper *)(new CashNormal());break;}case 1: // 打8折{pCS (CashSuper *)(new CashRebate(float(0.8)));break;}case 2: // 满300返100{pCS (CashSuper *)(new CashReturn(float(300), float(100)));break;}default:break;}return pCS;}
};2.3.2 Qt界面上点击确定的槽函数的修改
Qt界面上点击确定客户端的处理逻辑如下
计算此次的价格原价价格x数量根据下拉框当前选择的策略获取对应的索引值目前代码中写了3种 索引0正常收费索引1打8折索引2满300返100 调用现金计算工厂传入索引值实例化对应的现金计算对象调用现金计算对象得到此次的计算结果展示在窗口明细中计算总计值显示在总计框
void Widget::on_okBtn_clicked()
{// 此次的价格原价价格*数量float thisPrice ui-priceEdit-text().toFloat() * ui-numEdit-text().toInt();// 下拉框不同计算策略的索引值int idx ui-calcSelect-currentIndex();// 现金计算工厂CashFactory cashFactory;CashSuper *pCS cashFactory.createCashAccept(idx);if (pCS ! nullptr){// 传入原价根据结算规则得到计算后的实际价格thisPrice pCS-acceptCash(thisPrice);delete pCS;}// 总计m_fTotalPrice thisPrice;// 窗口中展示明细ui-showPanel-append(price: ui-priceEdit-text() , num: ui-numEdit-text() , method: ui-calcSelect-currentText() - ( QString::number(thisPrice) ));// 显示总计ui-totalShow-setText(QString::number(m_fTotalPrice));
}演示效果如下可以支持正常收费、八折收费、满300减100收费。 上述代码使用了简单工厂模式后如果再需要增加一种新类型的促销手段比如满100元则有10个积分则只需要再增加一个现在收费类即可接收2个参数满足积分的条件和对应的积分值继承于CashSuper类。
不过虽然简单工厂模式实现了对不同的收费计算对象的创建管理但对于本案例商场可能经常更改打折的额度和返利额度而每次维护或扩展收费方式都要改动这个工厂然后代码需要重新编译部署好像不是一种很好的方式。
下面来看版本四是如何实现的。
2.4 版本四策略模式
版本四用到了本篇的主题——策略模式。 策略模式Strategy它定义了算法家族分别封装起来让它们之间可以互相替换此模式让算法的变化不会影响到使用算法的客户。 对于本例商场的促销手段打折、返利这些对应的就是算法。
用工厂来生成算法对象本身也没有问题但算法只是一种策略而这些策略是随时可能互相替换的这就是变化点。
策略模式的作用就是来封装变化点设计的UML类图如下与简单工厂的主要区别是将简单工厂类换成了上下文类
上下文类或称环境类维护对具体策略的引用现金收费类在这里对应的是策略类父类3种具体收费类在这里对应的是具体的策略类子类 策略模式和简单工厂模式初看可能比较像下面来看下代码实现的区别。
2.4.1 现金收费上下文类
收费类相关代码。相比较版本三收费类和具体的收费类都不需要动只需要把简单工厂类改为现金收费上下文类即可。
现金收费上下文类有一个CashSuper的指针实现对具体策略的引用
在初始化CashContext时传入CashSuper的指针的指针通过其提供的GetResult方法可以得到其算法的计算结果。
这里的GetResult方法调用的是具体策略的acceptCash方法。
//现金收费上下文类
class CashContext
{
private:CashSuper *m_pCS nullptr;public:CashContext(CashSuper *pCsuper){m_pCS pCsuper;}~CashContext(){if (m_pCS) delete m_pCS;}float GetResult(float money){return m_pCS-acceptCash(money);}
};2.4.2 Qt界面上点击确定的槽函数的修改
Qt界面上点击确定客户端的处理逻辑如下
计算此次的价格原价价格x数量根据下拉框当前选择的策略获取对应的索引值0正常收费1打8折2满300返100然后将具体的算法类作为参数来创建一个上下文类再调用上下文类的GetResult方法得到此次的计算结果展示在窗口明细中计算总计值显示在总计框
void Widget::on_okBtn_clicked()
{// 此次的价格原价价格*数量float thisPrice ui-priceEdit-text().toFloat() * ui-numEdit-text().toInt();// 下拉框不同计算策略的索引值int idx ui-calcSelect-currentIndex();CashContext *pCC nullptr;switch (idx){case 0: // 正常收费{pCC new CashContext(new CashNormal());break;}case 1: // 打8折{pCC new CashContext(new CashRebate(float(0.8)));break;}case 2: // 满300返100{pCC new CashContext(new CashReturn(float(300), float(100)));break;}default:break;}// 计算后的价格if (pCC ! nullptr){// 传入原价根据结算规则得到计算后的实际价格thisPrice pCC-GetResult(thisPrice);delete pCC;}// 总计m_fTotalPrice thisPrice;// 窗口中展示明细ui-showPanel-append(price: ui-priceEdit-text() , num: ui-numEdit-text() , method: ui-calcSelect-currentText() - ( QString::number(thisPrice) ));// 显示总计ui-totalShow-setText(QString::number(m_fTotalPrice));
}该代码的演示效果和版本三的一样这里不再贴图。
下面再来分析下版本四的策略模式和版本三的简单工厂模式的区别
简单工厂模式通过简单工厂来得到具体的计算对应对象调用具体对象的acceptCash方法得到结果。策略模式通过上下文类来维护对具体策略的引用调用上下文类的GetResult方法得到结果本质也是调用其维护的具体策略的acceptCash方法。
对比发现两种模式区别就在于;
简单工厂模式是根据你的需求给你创建一个对应的收费计算对象后续的收费计算你和这个对象来对接即可。而策略模式是根据你的需求上下文类帮你和具体的策略对象对接你需要计算时仍然通过上下文类的接口获取即可。
对于版本四的代码Qt界面上客户端的处理代码又变得复杂了如何将客户端的那些判断逻辑移走呢下面来看版本五。
2.5 版本五策略模式简单工厂
版本四的代码CashContext上下文类在初始化时接收的参数是具体的策略类的指针。
在版本五中将参数改为Qt界面收费类型下拉框的索引值然后在CashContext内部根据索引值利用简单工厂模式CashContext自己创建对应的策略对象代码如下;
2.5.1 在策略模式内加入简单工厂
//现金收费上下文类
class CashContext
{
private:CashSuper *m_pCS nullptr;public:CashContext(int combIdx){switch (combIdx){case 0: // 正常收费{m_pCS (CashSuper *)(new CashNormal());break;}case 1: // 打8折{m_pCS (CashSuper *)(new CashRebate(float(0.8)));break;}case 2: // 满300返100{m_pCS (CashSuper *)(new CashReturn(float(300), float(100)));break;}default:break;}}~CashContext(){if (m_pCS) delete m_pCS;}float GetResult(float money){if (m_pCS){return m_pCS-acceptCash(money);}return money;}
};2.5.2 Qt界面上点击确定的槽函数的修改
Qt界面上点击确定客户端的处理逻辑如下
计算此次的价格原价价格x数量根据下拉框当前选择的策略获取对应的索引值0正常收费1打8折2满300返100然后将索引值作为参数来创建一个上下文类再调用上下文类的GetResult方法得到此次的计算结果展示在窗口明细中计算总计值显示在总计框
可以看到如下代码中版本五的Qt确定按钮的逻辑又变得清爽起来。
但实际上只是把这部分判断的代码移动到了CashContext中如果后续需要新增一种算法还是要修改CashContext中的判断的但有需求就会有修改任何需求的变更都是有成本的只是变更成本高低的不同继续降低目前CashContext的修改成本可以利用反射技术这在后续介绍抽象工厂模式时会提到。
void Widget::on_okBtn_clicked()
{// 此次的价格原价价格*数量float thisPrice ui-priceEdit-text().toFloat() * ui-numEdit-text().toInt();// 下拉框不同计算策略的索引值int idx ui-calcSelect-currentIndex();CashContext cc CashContext(idx);// 传入原价根据结算规则得到计算后的实际价格thisPrice cc.GetResult(thisPrice);// 总计m_fTotalPrice thisPrice;// 窗口中展示明细ui-showPanel-append(price: ui-priceEdit-text() , num: ui-numEdit-text() , method: ui-calcSelect-currentText() - ( QString::number(thisPrice) ));// 显示总计ui-totalShow-setText(QString::number(m_fTotalPrice));
}版本五的演示结果与版本三、版本四的效果一样这里不再贴图。
3 总结
本篇介绍了设计模式中的策略模式并通过商场收费计算软件的实例使用Qt和C编程从基础的收费功能到后续需求的增加一步步修改代码来学习策略模式的使用以及对比策略模式与简单工厂模式的不同。