策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。
- 举例:
- 从北京到青岛的方式?
1)飞机
2)高铁
3)火车
4)长途汽车
5)私家车
6)自行车
7)走路
- 从北京到青岛的方式?
通过以上方式,我们都可以达到去青岛的目的,那么列出的这些方法,就是我们选择的策略。 策略模式对策略进行抽象,策略的意思就是方法,所以也就是对方法的抽象。
概述
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装到具有公共接口的独立的类中,使每个算法可以相互替代,也使算法本身和使用算法的客户端分割开来,相互独立”。
模式中的角色
- —抽象策略角色(Stratege)
策略类,通常由一个接口或者抽象类实现。此角色给出所有的具体策略类所需的接口。用来约束一系列具体的策略算法,策略上下文角色ConcreteStrategy使用此策略接口来调用具体的策略所实现的算法 - —具体策略角色(Concrete Stratege)
包装了相关的算法和行为。 - —环境角色(Context)
持有一个策略类的引用,最终给客户端调用。策略上下文,负责和具体的策略实现交互,通常策略上下文对象会持有一个真正的策略实现对象,策略上下文还可以让具体的策略实现从其中获取相关数据,回调策略上下文对象的方法。
代码
//策略类
class CashContext
{
//声明一个CashSuper对象
private CashSuper cs;
public CashContext()
{
}
//通过构造方法,传入具体的收费策略
public CashContext(CashSuper csuper)
{
this.cs = csuper;
}
public double GetResult(double money)
{
//根据收费策略的不同,获得计算结果
return cs.acceptCash(money);
}
internal void setBehavior(CashSuper cashSuper)
{
throw new NotImplementedException();
}
}
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;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
DataSet ds;//用于存放配置文件信息
double total = 0.0d;//用于总计
private void Form1_Load(object sender, EventArgs e)
{
//配置文件
ds = new DataSet();
ds.ReadXml(Application.StartupPath + "\\CashAcceptType.xml");
//将读取到的记录绑定到下拉列表框中
foreach (DataRowView dr in ds.Tables[0].DefaultView)
{
cbxType.Items.Add(dr["name"].ToString());
}
cbxType.SelectedIndex = 0;
}
private void btnOk_Click(object sender, EventArgs e)
{
CashContext cc = new CashContext();
//根据用户的选项,查询用户选择项的相关行
DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + cbxType.SelectedItem.ToString() + "'"))[0];
//声明一个参数的对象数组
object[] args = null;
//若有参数,则将其分割成字符串数组,用于实例化时所用的参数
if (dr["para"].ToString() != "")
args = dr["para"].ToString().Split(',');
//通过反射实例化出相应的算法对象
cc.setBehavior((CashSuper)Assembly.Load("商场管理软件").CreateInstance("商场管理软件." + dr["class"].ToString(), false, BindingFlags.Default, null, args, null, null));
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 btnClear_Click(object sender, EventArgs e)
{
total = 0d;
txtPrice.Text = "0.00";
txtNum.Text = "1";
lbxList.Items.Clear();
lblResult.Text = "0.00";
}
}
将 Strategy与Simple Factory 结合起来在使用, 这样做比单独用 Strategy 的好处是將switch判断从客户端移到了CashContext里,对客户端与业务逻辑层进行了解耦; 同时,Strategy模式又简化了客户端的调用,,从原来Simple Factory模式时必须认识两个类:工厂类和产品抽象类, 简化为只需要认识 CashContext 即可.。即使后续需要扩展收费方式,也不需要改动了客户端代码。
开闭原则
对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。