该文章很清晰的描述了策略,转载地址:https://blog.youkuaiyun.com/weixin_42341986/article/details/94736312
策略模式定义为:定义算法家族,封装每一个算法,并使它们可以替换。策略可以让算法独立于使用它的客户端。
这个模式涉及到三个角色:
环境(Context)角色:持有一个Strategy的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
案例展示:
在商场收银系统中,可以正常收费,即按照单价*数量得出价格,遇到活动时可以打折收费和返利收费,从而计算价格。
运用策略模式,案例大概可以分为几个部分:
环境角色:环境类(CashContext)
抽象策略角色:收费父类(CashSuper)
具体策略角色:正常收费类(CashNormal),打折收费类(CashRebate),返利收费类(CashReturn)
客户端程序
类图为
设计了类图之后,便可以根据类图写具体的实现代码了~
CashSuper类:接收现金,作为一个父类,acceptCash方法为正常收费,打折收费,返利收费都需用到的方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cash_System
{
abstract class CashSuper
{
//收取现金,参数为当前价格
public abstract double acceptCash(double money);
}
}
CashNormal类:继承父类,重载父类方法,计算价格。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cash_System
{
//正常收费,继承父类,重载父类的方法,计算出价格,返回计算后的价格
class CashNormal:CashSuper
{
public override double acceptCash(double money)
{
return money;
}
}
}
CashReturn类:需要定义自己的返利方法CashReturn,为自己独有,并且重载父类方法计算价格。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cash_System
{
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;
}
}
}
CashRebate类:同CashReturn类类似。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cash_System
{
class CashRebate:CashSuper
{
private double moneyRebate = 0.0d; //定义折扣
public CashRebate(string moneyRebate)
{
this.moneyRebate = double.Parse(moneyRebate);
}
public override double acceptCash(double money)
{
return money*moneyRebate;
}
}
}
CashContext类:提供接口,与客户端交互。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Cash_System
{
class CashContext
{
//声明父类对象
private CashSuper cs;
//设置策略行为,参数为收费子类
public void setBehavior(CashSuper csuper)
{
this.cs = csuper;
}
//得到收费计算的结果
public double GetResult(double money)
{
return cs.acceptCash(money);
}
}
}
客户端代码为:
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;
using System.Reflection; //运用反射实例化
using System.Xml;
namespace Cash_System
{
public partial class Form1 : Form
{
private double total = 0.0d;
private DataSet ds;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ds = new DataSet();
ds.ReadXml(Application.StartupPath + "\\CashAcceptType.xml"); //Xml的路径 /debug/bin/CashAcceptType.xml
//从配置文件中读取的记录绑定到下拉列表
foreach (DataRowView dr in ds.Tables[0].DefaultView)
{
cbxType.Items.Add(dr["name"].ToString());
}
cbxType.SelectedIndex = 0;
}
private void button1_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("Cash_System").CreateInstance("Cash_System."+dr["class"].ToString(), false, BindingFlags.Default, null, args, null, null));
double totalPrice = 0.0d;
totalPrice = cc.GetResult(Convert.ToDouble(txtPrice.Text)*Convert.ToDouble(txtNum.Text));
total = total + totalPrice;
lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " 类型:" + cbxType.SelectedItem + " 合计:" + totalPrice.ToString());
lblResult.Text = total.ToString();
}
private void clear_Click(object sender, EventArgs e)
{
total = 0d;
txtPrice.Text = "0.00";
txtNum.Text = "1";
lbxList.Items.Clear();
lblResult.Text = "0.00";
}
}
}
**反射:**利用程序集的元数据信息。 反射可以有很多方法,编写程序时先导入 System.Reflection 命名空间。
CashAcceptType.xml 内容为:
<?xml version="1.0" encoding="utf-8" ?>
<CashAcceptType>
<type>
<name>正常收费</name>
<class>CashNormal</class>
<para></para>
</type>
<type>
<name>满300返100</name>
<class>CashReturn</class>
<para>300,100</para>
</type>
<type>
<name>满200返50</name>
<class>CashReturn</class>
<para>200,50</para>
</type>
<type>
<name>打8折</name>
<class>CashRebate</class>
<para>0.8</para>
</type>
<type>
<name>打7折</name>
<class>CashRebate</class>
<para>0.7</para>
</type>
</CashAcceptType>
假如需要增加新的收银方式,只需要在CashSuper下建立新的子类,并在xml文件中增加相应的名称,便不会影响其他子类,也不会影响客户端程序的使用,这便是策略模式的最大优点了。
执行程序的结果为: