本例子下载地址:https://files.cnblogs.com/mengxin523/策略模式.rar
从现在开始我们要开始学习一下23种设计模式。如果在我们的项目中能够适当应用到设计模式,不仅能够有助于提高我们的代码健壮性,而且还能够增强项目的可复用性和扩展性,在后期维护的时候更加方便,谓之灵活性好。
23种设计模式可谓是集全世界优秀的软件开发人员的心血历经几十年总结而成的软件开发设计方法。今天我们应用到这些大师总结的方法,不用体验到前任的艰辛,算是站在巨人的肩膀上体验软件开发行业的精彩。既然前辈们给我们留下了这么宝贵的财富,我们自当收于囊中。
好,本文我们就来看看23种设计模式中的策略模式。
学习设计模式之前我们必须要对面向对象的概念和思想了如指掌。因为设计模式说白了其实就是对面向对象的灵活应用,所以我们要建高楼大厦就必须打好根基。
下面我们用一个例子来讲解策略模式的应用。(注:例子来自《大话设计模式》)
超市里面商品的价格随着节日的变化是不断变化的,比如节日来了超市做促销所有商品打八折,或者满多少钱就归还多少钱,或者两则并存等。所以超市的价格计算就分了3种:正常收费,打折收费,返利收费。
到了这里就要注意了,我们需要找出“变化点”,然后分析一下这些“变化点”的“共同点”。这里我们很容易发现:收费模式下分为3种(正常,打折,返利),也就是说超市无论做什么活动,最终都是属于一种收费的活动,只是表现形式不同。因此面向对象的思想来了:“收费”我们可以归为一个抽象类,然后正常收费,打折收费,返利收费都继承与这个抽象类。
收费类:
abstract class CashSuper //收费父类
{
//抽象方法:收取现金,参数为原价,返回为当前价
public abstract double acceptCash(double money);
}
然后三种收费模式都继承于收费类,然后重写里面的抽象方法(计算最后的费用):
class CashNormal : CashSuper //正常收费,继承CashSuper
{
public override double acceptCash(double money) //重写父类抽象方法
{
return money;
}
}
class CashReturn : CashSuper //返利收费,继承CashSuper
{
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
//初始化时必须要输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
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 CashRebate : CashSuper //打折收费,继承CashSuper
{
private double moneyRebate = 1d;
//初始化时,必需要输入折扣率,如八折,就是0.8
public CashRebate(string moneyRebate)
{
this.moneyRebate = double.Parse(moneyRebate);
}
public override double acceptCash(double money)
{
return money * moneyRebate;
}
}
至此我们的三个收费模式就都建好了,现在我们就要用到一个“工厂”(算是一个类)来归类一下这三个模式,就是将这三个收费模式用一个判断分支整合一起。
(到这里有人问为何不将这个逻辑判断写在客户端呢?其实这主要是考虑到项目软件的可维护性,如果写在客户端,以后新增了新的收费模式,比如打折兼返利,就要改一下客户端,这样对软件的维护成本将会增大。客户端主要就是收集用户信息和显示用户信息之用而已,最好不要“越界”处理其他的事物!)
工厂代码如下:
namespace 商场管理软件 //现金收取工厂
{
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); //这里实现的是抽象类的子类中重写的方法
}
}
}
由于有了工厂,现在客户端就好办了,因为客户端只要“认识”这个工厂就可以了,其他无论是什么样的收费模式都不管,因为工厂已经将他们都“打包”好了。所以我们的客户端的代码如下:
namespace 商场管理软件
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//客户端窗体程序(主要部分)
double total = 0.0d;
private void btnOk_Click(object sender, EventArgs e)
{
//利用简单工厂模式根据下拉选择框,生成相应的对象
CashContext csuper = new CashContext(cbxType.SelectedItem.ToString());
double totalPrices = 0d;
//通过多态,可以得到收取费用的结果
totalPrices = csuper.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 btnClear_Click(object sender, EventArgs e)
{
total = 0d;
txtPrice.Text = "0.00";
txtNum.Text = "0";
lbxList.Items.Clear();
lblResult.Text = "0.00";
}
}
}
在客户端,只要用户从下拉列表框选择一个活动的收费方式就可以达到促销的目的。后期如果要新增一个收费模式,则只要写一个类去继承收费的抽象类,然后修改一下工厂的分支就Ok了,客户端其实不用去动的(更客观的是项目可以从外部文件,比如XML读取收费模式到列表框,这样以后就修改这个文件就可以了)。
(转载请注明出处...谢谢!)