群“模”乱舞之策略模式

2019-04-13 17:29发布

         在上篇博客中我们提到了简单工厂模式,然而在GoF总结的23个设计模式中并没有她的影子,严格意义上来讲,简单工厂模式并不是一种设计模式,因为她根本不符合设计模式的开放—封闭原则,即软件实体如类、模块等应该可以扩展,但是不可修改。对于设计模式的设计原则,将在下一篇博文中介绍,这里我们先来看一个例子:          周所周知,超市收银系统都具有计费功能,那么要你做一个能够根据输入的商品单价和数量,以及计费方式进行计算的模块,你如何做?计费方式也就是打不打折,有没有返利优惠等等。那么我们先用简单工厂模式去实现一下。          简单工厂实现          using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 商场收银软件 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } double total = 0.0d; private void textBox1_TextChanged(object sender, EventArgs e) { } private void label5_Click(object sender, EventArgs e) { } private void Form1_Load(object sender, EventArgs e) { cbxType.Items.AddRange(new object[] { "正常收费", "满300返100", "打8折" }); cbxType .SelectedIndex =0; } private void buttonOK_Click(object sender, EventArgs e) { CashSuper csuper = CashFactory.createCashAccept(cbxType.SelectedItem.ToString()); double totalPrices = 0d; totalPrices = csuper.acceptCash(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)); total = total + totalPrices; lbxList.Items.Add("单价:" + txtPrice.Text + "数量:" + txtNum.Text +"计费方式:"+ cbxType.SelectedItem + "合计:" + totalPrices.ToString()); lblResult.Text = total.ToString(); } private void button2_Click(object sender, EventArgs e) { txtNum.Text = ""; txtPrice.Text = ""; lbxList.Items.Clear(); lblResult.Text = "0.00"; } } //现金收费抽象类 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; public CashRebate(string moneyRebate) { this.moneyRebate = double.Parse(moneyRebate); } public override double acceptCash(double money) { return money * moneyRebate; } } //返利收费子类 class CashReturn : CashSuper { private double moneyCondition = 0.0d; private double moneyReturn = 0.0d; public CashReturn(string moneyCondition, string moneyReturn) { this.moneyCondition = double.Parse(moneyCondition); this.moneyReturn = double.Parse(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": CashReturn cr1 = new CashReturn("300", "100"); cs = cr1; break; case "打8折": CashRebate cr2 = new CashRebate("0.8"); cs = cr2; break; } return cs; } } }
           我们看到,这种方案的特点就是将计费方式的实例化放到了工厂里,从而在需要添加新的计费方式的时候,只需在工厂里加一下条件,完了再设计一个相应的类即可。但是这样做同样也破坏了开放—封闭原则,每一次的扩展都要大动干戈,实在是不好,不好。面对算法的时常变动,如何应对?下面我们用另一种方式试试:          策略模式实现          using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 商场收银软件 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } double total = 0.0d; private void textBox1_TextChanged(object sender, EventArgs e) { } private void label5_Click(object sender, EventArgs e) { } private void Form1_Load(object sender, EventArgs e) { cbxType.Items.AddRange(new object[] { "正常收费", "满300返100", "打8折" }); cbxType .SelectedIndex =0; } private void buttonOK_Click(object sender, EventArgs e) { //简单工厂模式的客户端代码 //CashSuper csuper = CashFactory.createCashAccept(cbxType.SelectedItem.ToString()); //double totalPrices = 0d; //totalPrices = csuper.acceptCash(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)); //total = total + totalPrices; //lbxList.Items.Add("单价:" + txtPrice.Text + "数量:" + txtNum.Text +"计费方式:"+ cbxType.SelectedItem + "合计:" + totalPrices.ToString()); //lblResult.Text = total.ToString(); //策略模式的客户端代码 CashContext cc = null; switch (cbxType.SelectedItem.ToString()) { case "正常收费": cc = new CashContext(new CashNormal()); break; case "满300返100": cc = new CashContext(new CashReturn("300", "100")); break; case "打8折": cc = new CashContext(new CashRebate("0.8")); break; } double totalPrices = 0d; totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)); total = total + totalPrices; lbxList.Items.Add("单价:" + txtPrice.Text + "数量:" + txtNum.Text + "计费方式:" + cbxType.SelectedItem + "合计:" + totalPrices.ToString()); lblResult.Text = total.ToString(); } private void button2_Click(object sender, EventArgs e) { txtNum.Text = ""; txtPrice.Text = ""; lbxList.Items.Clear(); lblResult.Text = "0.00"; } } //现金收费抽象类 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; public CashRebate(string moneyRebate) { this.moneyRebate = double.Parse(moneyRebate); } public override double acceptCash(double money) { return money * moneyRebate; } } //返利收费子类 class CashReturn : CashSuper { private double moneyCondition = 0.0d; private double moneyReturn = 0.0d; public CashReturn(string moneyCondition, string moneyReturn) { this.moneyCondition = double.Parse(moneyCondition); this.moneyReturn = double.Parse(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": // CashReturn cr1 = new CashReturn("300", "100"); // cs = cr1; // break; // case "打8折": // CashRebate cr2 = new CashRebate("0.8"); // cs = cr2; // break; // } // return cs; // } //} //CashContext类 class CashContext { private CashSuper cs; public CashContext(CashSuper csuper) { this.cs = csuper; } public double GetResult(double money) { return cs.acceptCash(money); } } }
           对比两个方案,我们可以看到不同之处就是策略模式将工厂类撤了,取而代之的是CashContext类,然而又将实现哪一个算法的判断交给了客户端,这样客户端承担的责任又大了,不好,不好……          怎么办?要不将简单工厂和策略模式混搭?试试看。对于他们的结合,只需将CashContext类改造一下即可,如下: //改造后的CashContext类 class CashContext { CashSuper cs = null ; public CashContext(string type) { switch (type) { case "正常收费": CashNormal cs0 = new CashNormal(); cs = cs0; break; case "满300返100": CashReturn cr1 = new CashReturn("300", "100"); cs = cr1; break; case "打8折": CashRebate cr2 = new CashRebate("0.8"); cs = cr2; break; } } public double GetResult(double money) { return cs.acceptCash(money); } } 这样一来,客户端的代码就要简单的多了,简单工厂模式需要让客户端和两个类CashSuper和CashFactory打交道,而策略模式与简单工厂结合后的用法,客户端就只需和CashContext打交道就可完成相应的功能,降低了耦合性。我们再回过头去看策略模式的定义: 策略模式:定义了算法家族,分别封装起来,让它们之间可以相互替代,此模式让算法的变化,不会影响到使用此算法的客户。