C# 设计模式一一策略模式
一、定义
它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。
二、结构图
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象负责。策略模式通常把一系列的算法包装到一系列的策略类里面。用一句话慨括策略模式就是——“将每个算法封装到不同的策略类中,使得它们可以互换”。
下面是策略模式的结构图:
该模式涉及到三个角色:
- 环境角色(Context):持有一个Strategy类的引用
- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类来实现。此角色给出所有具体策略类所需实现的接口。
- 具体策略角色(ConcreteStrategy):包装了相关算法或行为。
三、实现
实现如下收费应用:
(1) 简单工厂模式实现
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 = 0;
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;
private double moneyReturn = 0;
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;
}
}
构造简单工厂模式:负责生产对象的一个类,本例负责生产收费方式的一个类
/// <summary>
/// 简单工厂模式:负责生产对象的一个类,本例负责生产收费方式的一个类
/// </summary>
class CashFactory
{
public static CashSuper createCashAccept( string type )
{
CashSuper cs = null;
switch( type )
{
case "正常收费":
cs = new CashNormal( );
break;
case "满300返100":
cs = new CashReturn( "300","100" );
break;
case "打8折":
cs = new CashRebate( "0.8" );
break;
}
return cs;
}
}
界面显示及调用:
private void Form1_Load( object sender, EventArgs e )
{
cbxType.Items.AddRange( new object[] {"正常收费","打八折","打九折","满300返100"});
cbxType.SelectedIndex = 0;
}
double total = 0;
private void btnOk_Click( object sender, EventArgs e )
{
//简单工厂模式需要让客户端认识CashSuper类和CashFactory类
CashSuper csuper = CashFactory.createCashAccept( cbxType.SelectedItem.ToString());
double totalPrice = 0;
totalPrice = csuper.acceptCash( Convert.ToDouble(textPrice.Text) * Convert.ToDouble(textNum.Text));
total += totalPrice;
lbxList.Items.Add( "单价:" + textPrice.Text + "数量:" + textNum.Text + " "
+ cbxType.SelectedItem + "合计:" + totalPrice.ToString());
lblResult.Text = total.ToString( );
}
(2) 策略模式实现
- 上述CashSuper:这是一个抽象角色,通常由一个接口或抽象类来实现。此角色给出所有具体策略类所需实现的接口。
- 如下CashContext类:环境角色(Context):持有一个Strategy类的引用
/// <summary>
/// 策略模式
/// </summary>
class CashContext
{
//声明一个CashSuper对象
private CashSuper cs;
public CashContext( CashSuper csuper ) //通过构造方法,传入具体的收费策略
{
this.cs = csuper;
}
public double GetResult( double money )
{
return cs.acceptCash( money ); //根据收费策略的不同获取收费结果
}
}
- 上述CashSuper的子类:具体策略角色(ConcreteStrategy):包装了相关算法或行为。
策略模式调用:
double total = 0;
private void btnOk_Click( object sender, EventArgs e )
{
//策略模式需要传入引用Strategy类的引用(此处引用的是各CashSuper对象,由各子类传入)
CashContext cc = null;
string type = cbxType.SelectedItem.ToString( );
switch( type )
{
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 totalPrice = 0;
//
totalPrice = cc.GetResult( Convert.ToDouble( textPrice.Text ) * Convert.ToDouble( textNum.Text ) );
total += totalPrice;
lbxList.Items.Add( "单价:" + textPrice.Text + "数量:" + textNum.Text + " "
+ cbxType.SelectedItem + "合计:" + totalPrice.ToString( ) );
lblResult.Text = total.ToString( );
}
(3) 策略模式+简单工厂模式实现
为了把策略模式中的判断过程从客户端移走(即从btnOk_Click函数中移走),需要利用简单工厂模式将负责生产收费对象整合到CashContext类中去,如下所示:
/// <summary>
/// 策略模式+简单工厂模式
/// </summary>
class CashContext
{
//声明一个CashSuper对象
private CashSuper cs;
public CashContext( string type ) //傳入收費類型
{
switch( type )
{
case "正常收费":
cs = new CashNormal( );
break;
case "满300返100":
cs = new CashReturn( "300", "100" );
break;
case "打8折":
cs = new CashRebate( "0.8" );
break;
}
}
public double GetResult( double money )
{
return cs.acceptCash( money ); //根据收费策略的不同获取收费结果
}
}
这样,客户端调用只要认识CashContext类就可以了,耦合度相比简单工厂模式就大大降低了。(简单工厂模式需要让客户端认识CashSuper类和CashFactory类)。
double total = 0;
private void btnOk_Click( object sender, EventArgs e )
{
double totalPrice = 0;
//客户端调用只要认识CashContext类就可以了,耦合度相比简单工厂模式就大大降低了
CashContext csuper = new CashContext(cbxType.SelectedItem.ToString());
totalPrice = csuper.GetResult( Convert.ToDouble( textPrice.Text ) * Convert.ToDouble( textNum.Text ) );
total += totalPrice;
lbxList.Items.Add( "单价:" + textPrice.Text + "数量:" + textNum.Text + " "
+ cbxType.SelectedItem + "合计:" + totalPrice.ToString( ) );
lblResult.Text = total.ToString( );
}
四、策略者模式的优缺点
优点:
- 策略类之间可以自由切换。由于策略类都实现同一个接口,所以使它们之间可以自由切换。
- 易于扩展。增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码。
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
五、策略者模式的适用场景
在下面的情况下可以考虑使用策略模式:
一个系统需要动态地在几种算法中选择一种的情况下。那么这些算法可以包装到一个个具体的算法类里面,并为这些具体的算法类提供一个统一的接口。