首页 >> 大全

读《大话设计模式》——②商场促销[策略模式]

2023-12-19 大全 27 作者:考证青年

题目: 做一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。

方案一: 用两个文本框来输入单价和数量,一个确定按钮来算出每种商品的费用,用个列表框来记录商品的清单,一个标签来记录总计,还需要一个重置按钮来重新开始。

商场收银系统v1.0关键代码

double total = 0.0d;
private void btnOK_Click(object sender, EventArgs e)
{double totalPrice = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text);total = total + totalPrice;lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " 合计:" + totalPrice.ToString());lblResult.Text = total.ToString();
}

存在问题

新增需求,商场搞活动,所有商品打八折,如何?若只在乘以0.8,则活动结束难道还要再改一遍代码,重新给所有机器安装一次?显然是不合适的。那么考虑加个下拉选择框呢?

方案二

商场收银系统v1.1关键代码

double total = 0d;
private void Form2_Load(object sender, EventArgs e)
{cmbType.Items.AddRange(new object[] { "正常收费", "打八折", "打七折", "打五折" });cmbType.SelectedIndex = 0;
}private void btnOK_Click(object sender, EventArgs e)
{double totalPrice = 0d;switch (cmbType.SelectedIndex){case 0:totalPrice = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text);break;case 1:totalPrice = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text) * 0.8;break;case 2:totalPrice = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text) * 0.7;break;case 3:totalPrice = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text) * 0.5;break;default:break;}total = total + totalPrice;lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " " + cmbType.SelectedItem + " 合计:" + totalPrice.ToString());lblResult.Text = total.ToString();
}

存在问题:

促销活动模式__促销的模式

灵活性好多了,但重复代码很多,如.()4个分支除折扣不一样以外几乎没有不同,应该考虑重构新增需求,活动加大,需要有满300返100的促销算法,怎么办?

方案三: 用前面学过的简单工厂模式,写一个父类和多个打折和返利的子类,利用多态实现。

注意不是写多个子类,只写两个子类即可,打折和返利只是传参不同。

面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。

/// 
/// 现金收费抽象类
/// 
abstract class CashSuper
{/// /// 现金收取/// /// 原价/// 当前价public abstract double acceptCash(double money);
}/// 
/// 正常收费子类
/// 
class CashNormal : CashSuper
{public override double acceptCash(double money){return money;}
}/// 
/// 打折收费子类
/// 
class CashRebate : CashSuper
{private double moneyRebate = 1d;/// /// 实例化打折类/// /// 折扣率,如八折就是0.8public CashRebate(double moneyRebate){this.moneyRebate = moneyRebate;}public override double acceptCash(double money){return money * moneyRebate;}
}/// 
/// 返利收费子类
/// 
class CashReturn : CashSuper
{private double moneyCondition = 0d;private double moneyReturn = 0d;/// /// 实例化返利类/// /// 返利条件/// 返利值/// 比如满300返100,则moneyCondition=300,moneyReturn=100public CashReturn(double moneyCondition, double moneyReturn){this.moneyCondition = moneyCondition;this.moneyReturn = moneyReturn;}public override double acceptCash(double money){double result = money;if (money >= moneyCondition)result = money - Math.Floor(money / moneyCondition) * moneyReturn;return result;}
}/// 
/// 现金收费工厂类
/// 
class CashFactory
{public static CashSuper createCashAccept(string type){CashSuper cs = null;switch (type){case "正常收费":cs = new CashNormal();break;case "满300返100":cs = new CashReturn(300, 100);break;case "打八折":cs = new CashRebate(0.8);break;default:break;}return cs;}
}

客户端关键代码

double total = 0d;
private void Form3_Load(object sender, EventArgs e)
{cmbType.Items.AddRange(new object[] { "正常收费", "打八折", "满300返100" });cmbType.SelectedIndex = 0;
}private void btnOK_Click(object sender, EventArgs e)
{CashSuper csuper = CashFactory.createCashAccept(cmbType.SelectedItem.ToString());double totalPrice = csuper.acceptCash(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));total = total + totalPrice;lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " " + cmbType.SelectedItem + " 合计:" + totalPrice.ToString());lblResult.Text = total.ToString();
}

存在问题: 简单工厂模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性的更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需要重新编译部署。

方案四:策略模式() 定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户。

策略模式结构图

因此,、、、不用改,添加策略类,修改客户端即可

/// 
/// 现金收费策略类
/// 
class CashContext
{private CashSuper cs;public CashContext(CashSuper cspuer){this.cs = cspuer;}public double GetResult(double money){return cs.acceptCash(money);}
}

客户端

private void Form4_Load(object sender, EventArgs e)
{cmbType.Items.AddRange(new object[] { "正常收费", "打八折", "满300返100" });cmbType.SelectedIndex = 0;
}private void btnOK_Click(object sender, EventArgs e)
{CashContext cc = null;switch (cmbType.SelectedIndex){case 0:cc = new CashContext(new CashNormal());break;case 1:cc = new CashContext(new CashRebate(0.8));break;case 2:cc = new CashContext(new CashReturn(300, 100));break;default:break;}double totalPrice = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));total = total + totalPrice;lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " " + cmbType.SelectedItem + " 合计:" + totalPrice.ToString());lblResult.Text = total.ToString();
}

存在问题: 又回到方案二的情况,在客户端去判断使用哪个算法,怎么样才能把判断过程从客户端移走呢?

方案五:策略模式() +工厂模式

/// 
/// 改造后的现金收费策略类
/// 
class CashContext1
{private CashSuper cs;public CashContext1(string type){CashSuper cs1 = null;switch (type){case "正常收费":cs = new CashNormal();break;case "满300返100":cs = new CashReturn(300, 100);break;case "打八折":cs = new CashRebate(0.8);break;default:break;}this.cs = cs1;}public double GetResult(double money){return cs.acceptCash(money);}
}

客户端代码

double total = 0d;
private void Form4_Load(object sender, EventArgs e)
{cmbType.Items.AddRange(new object[] { "正常收费", "打八折", "满300返100" });cmbType.SelectedIndex = 0;
}private void btnOK_Click(object sender, EventArgs e)
{CashContext1 cc = new CashContext1(cmbType.SelectedItem.ToString());double totalPrice = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));total = total + totalPrice;lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " " + cmbType.SelectedItem + " 合计:" + totalPrice.ToString());lblResult.Text = total.ToString();
}

总结: 对比一下简单工厂模式和策略模式,会发现,简单工厂模式需要客户端认识和两个类,而策略模式只需要客户端认识即可。

策略模式解析:

综上所述,除了策略模式比简单工厂模式耦合略低(需要认识的类少)外,其他优点笔者还体会不到,有待深入理解,欢迎各位大神指点。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了