您的位置:首页 > 编程语言 > C#

群“模”乱舞之策略模式

2014-05-07 21:00 106 查看
         在上篇博客中我们提到了简单工厂模式,然而在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打交道就可完成相应的功能,降低了耦合性。我们再回过头去看策略模式的定义:
策略模式:定义了算法家族,分别封装起来,让它们之间可以相互替代,此模式让算法的变化,不会影响到使用此算法的客户。

   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式 c#