第七课 策略模式

第七课 策略模式

 

       今天来看看策略模式吧。首先,策略模式定义:

       策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。

       简单的来说呢,可以这么想想,现在由一个类,里面有个方法function,我想在实例化这个类的时候,能够传一个方法给function,让实例类使用传入方法。这样,就相当于把这个方法提取出来,有点像是方法指针的这么一个东西。

       其实.net里面方法是可以直接传值的,通过delegate 来实现,但是咱们这里主要讨论面向对象通用的处理方法,就是策略模式。

   来看一下策略模式的基本实现思路:

1.        using System;

2.        using System.Collections.Generic;

3.        using System.Text;

4.         

5.        namespace 策略模式

6.        {

7.            class Program

8.            {

9.                static void  Main (string[] args)

10.             {

11.                 Context context;

12.      

13.                 context = new Context(new ConcreteStrategyA());

14.                 context.ContextInterface();

15.      

16.                 context = new Context(new ConcreteStrategyB());

17.                 context.ContextInterface();

18.      

19.                 context = new Context(new ConcreteStrategyC());

20.                 context.ContextInterface();

21.      

22.                 Console.Read();

23.             }

24.         }

25.      

26.         //抽象算法类

27.         abstract class Strategy

28.         {

29.             //算法方法

30.             public abstract void AlgorithmInterface();

31.         }

32.         //具体算法A

33.         class ConcreteStrategyA : Strategy

34.         {

35.             //算法A实现方法

36.             public override void AlgorithmInterface()

37.             {

38.                 Console.WriteLine("算法A实现");

39.             }

40.         }

41.         //具体算法B

42.         class ConcreteStrategyB : Strategy

43.         {

44.             //算法B实现方法

45.             public override void AlgorithmInterface()

46.             {

47.                 Console.WriteLine("算法B实现");

48.             }

49.         }

50.         //具体算法C

51.         class ConcreteStrategyC : Strategy

52.         {

53.             //算法C实现方法

54.             public override void AlgorithmInterface()

55.             {

56.                 Console.WriteLine("算法C实现");

57.             }

58.         }

59.         //上下文

60.         class Context

61.         {

62.             Strategy strategy;

63.      

64.             public Context(Strategy strategy)

65.             {

66.                 this.strategy = strategy;

67.             }

68.             //上下文接口

69.             public void ContextInterface()

70.             {

71.                 strategy.AlgorithmInterface();

72.             }

73.         }

74.      

75.     }

76.      

 

1.       抽象算法类:Strategy

来看下这个类,里面只定义了一个抽象方法AlgorithmInterface ,可以把它想象成一个方法指针,指向了一个方法的起始点。

2.       再来看具体的实现类对象:ConcreteStrategyAConcreteStrategyBConcreteStrategyC

他们集成Strategy ,并给予了AlgorithmInterface这个方法不同的实现。

3.       接下来是:Context 类,

它包含一个 Strategy 成员 ,拥有一个构造函数,传入一个Strategy 来初始化自己的成员。最后定义了一个ContextInterface()方法,里面只是调用了Strategy 成员的AlgorithmInterface 这个方法。(打眼看起来有点像是工厂方法加模板模式的应用哦)

4.       最后看程序主方法的使用方式:

5.       context = new Context(new ConcreteStrategyA());

6.                   context.ContextInterface();

ConcreteStrategyA构造了Context对象context,直接调用方法ContextInterface

现在考虑,这调用的是那个方法呢?会输出什么呢?

答案揭晓~

Console.WriteLine("算法A实现");

程序会走这一句,输出“算法A实现”。相信大家看到这里后,应当对继承的应用有一定了解了吧,里氏代换原则哦~

我如果用ConcreteStrategyA构造Context的话,ContextInterface则会调用

Console.WriteLine("算法B实现");

依此类推了,相信C就不用我说了。

现在实际就相当于那Context类当作一个方法指针来用。我用哪个实现类来初始化它,它就指向那个类实现的方法。借此达到了对方法(策略)的封装。

 

那么策略方法有什么用呢,这里我觉得大话设计模式中的例子就很好,来看一下。

 

    例子:实现一个商场管理软件,界面如下

   

    输入单价数量,选择计算方式,然后输出价格。

    来看实现代码:

1.        using System;

2.        using System.Collections.Generic;

3.        using System.Text;

4.         

5.        namespace 商场管理软件

6.        {

7.            //现金收取父类

8.            abstract class CashSuper

9.            {

10.             //抽象方法:收取现金,参数为原价,返回为当前价

11.             public abstract double acceptCash(double money);

12.         }

13.      

14.     }

15.      

这是策略类的基类,封装了我们要的付款算法。

 

1.        using System;

2.        using System.Collections.Generic;

3.        using System.Text;

4.         

5.        namespace 商场管理软件

6.        {

7.            //正常收费,继承CashSuper

8.            class CashNormal : CashSuper

9.            {

10.             public override double acceptCash(double money)

11.             {

12.                 return money;

13.             }

14.         }

15.      

16.     }

17.      

这是正常收费的算法。直接返回钱数。

 

1.        using System;

2.        using System.Collections.Generic;

3.        using System.Text;

4.         

5.        namespace 商场管理软件

6.        {

7.            //打折收费,继承CashSuper

8.            class CashRebate : CashSuper

9.            {

10.             private double moneyRebate = 1d;

11.             //初始化时,必需要输入折扣率,如八折,就是0.8

12.             public CashRebate(string moneyRebate)

13.             {

14.                 this.moneyRebate = double.Parse(moneyRebate);

15.             }

16.      

17.             public override double acceptCash(double money)

18.             {

19.                 return money * moneyRebate;

20.             }

21.         }

22.     }

23.      

这是打折时的算法,需要初始化折扣,返回打折后钱数。

 

1.        using System;

2.        using System.Collections.Generic;

3.        using System.Text;

4.         

5.        namespace 商场管理软件

6.        {

7.            //返利收费,继承CashSuper

8.            class CashReturn : CashSuper

9.            {

10.             private double moneyCondition = 0.0d;

11.             private double moneyReturn = 0.0d;

12.             //初始化时必须要输入返利条件和返利值,比如满300100,则moneyCondition300moneyReturn100

13.             public CashReturn(string moneyCondition, string moneyReturn)

14.             {

15.                 this.moneyCondition = double.Parse(moneyCondition);

16.                 this.moneyReturn = double.Parse(moneyReturn);

17.             }

18.      

19.             public override double acceptCash(double money)

20.             {

21.                 double result = money;

22.                 //若大于返利条件,则需要减去返利值

23.                 if (money >= moneyCondition)

24.                     result = money - Math.Floor(money / moneyCondition) * moneyReturn;

25.      

26.                 return result;

27.             }

28.         }

29.     }

30.      

这是返利的算法,设定多少返回多少哦。

 

然后看Context类:

1.        using System;

2.        using System.Collections.Generic;

3.        using System.Text;

4.         

5.        namespace 商场管理软件

6.        {

7.            //现金收取工厂

8.            class CashContext

9.            {

10.             CashSuper cs = null;

11.      

12.             //根据条件返回相应的对象

13.             public CashContext(string type)

14.             {

15.                 switch (type)

16.                 {

17.                     case "正常收费":

18.                         CashNormal cs0 = new CashNormal();

19.                         cs = cs0;

20.                         break;

21.                     case "300100":

22.                         CashReturn cr1 = new CashReturn("300""100");

23.                         cs = cr1;

24.                         break;

25.                     case "8":

26.                         CashRebate cr2 = new CashRebate("0.8");

27.                         cs = cr2;

28.                         break;

29.                 }

30.             }

31.      

32.             public double GetResult(double money)

33.             {

34.                 return cs.acceptCash(money);

35.             }

36.         }

37.     }

38.      

 

注意到没有,这里不是简单的策略算法,他结合了简单工厂模式哦。将使用算法的判断逻辑也封装了进来。通过GetResult()方法来计算哦。

 

最后看一下界面实现吧。(为了能完整运行例子)

1.        using System;

2.        using System.Collections.Generic;

3.        using System.ComponentModel;

4.        using System.Data;

5.        using System.Drawing;

6.        using System.Text;

7.        using System.Windows.Forms;

8.         

9.        namespace 商场管理软件

10.     {

11.         public partial class Form1 : Form

12.         {

13.             public Form1()

14.             {

15.                 InitializeComponent();

16.             }

17.      

18.             //客户端窗体程序(主要部分)

19.             double total = 0.0d;

20.             private void btnOk_Click(object sender, EventArgs e)

21.             {

22.                 //利用简单工厂模式根据下拉选择框,生成相应的对象

23.                 CashContext csuper = new CashContext(cbxType.SelectedItem.ToString());

24.                 double totalPrices = 0d;

25.                 //通过多态,可以得到收取费用的结果

26.                 totalPrices = csuper.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));

27.                 total = total + totalPrices;

28.                 lbxList.Items.Add("单价:" + txtPrice.Text + 数量:" + txtNum.Text + " "

29.                     + cbxType.SelectedItem + 合计:" + totalPrices.ToString());

30.                 lblResult.Text = total.ToString();

31.             }

32.      

33.             private void btnClear_Click(object sender, EventArgs e)

34.             {

35.                 total = 0d;

36.                 txtPrice.Text = "0.00";

37.                 txtNum.Text = "0";

38.                 lbxList.Items.Clear();

39.                 lblResult.Text = "0.00";

40.             }

41.         }

42.     }

相关的UI生成那部分我就不贴了,相信大家都能看懂了。

 

知道为什么要结合简单工厂了吧,就是为了简化UI曾逻辑。进一步降低耦合性哦。

43.     CashContext csuper = new CashContext(cbxType.SelectedItem.ToString());

这一句很有用吧,哈哈。

你还发现什么好处没有,我们定义了这么多算法类,但是客户端呢,只需要告诉她CashContext这个简单工厂+策略的实现类就可以了,耦合性很低吧,通用性很高吧~

 

策略模式介绍到此结束,还是老话,设计模式需要自己体会其中的好处,当你感受到了,自然就会理解了。试着实现下这个商场管理软件吧。(最好根据思路自己来敲代码哦)

 

本课到此结束。

 

作者:王文斌

转载请注明出处哦~

 

 

       突然想到,这里其实还是可以结合工厂方法理解少的那种动态反射方法的吗,改进一下哦。

       首先这里CashContext这个类要改一下。

1.        using System;

2.        using System.Collections.Generic;

3.        using System.Text;

4.         

5.        namespace 商场管理软件

6.        {

7.            class CashContext

8.            {

9.                private CashSuper cs;

10.      

11.             public void setBehavior(CashSuper csuper)

12.             {

13.                 this.cs = csuper;

14.             }

15.      

16.             public double GetResult(double money)

17.             {

18.                 return cs.acceptCash(money);

19.             }

20.         }

21.     }

22.      

 

这里用了纯正的策略实现。

你可能要有疑问了,那我怎么传入具体策略的实现对象呢?

接着往下瞧哦。

 

首先,我们要想最大程度的提高可扩展性,减少代码修改工作,就应该考虑能都太配置。简单的说就是把后台代码转移到程序的配置文件的明文。这在工厂模式哪里介绍过了。,netjava都是支持的。动态Load一个类对象出来。

下面看本例的实现方式。

界面UI类:

1.        using System;

2.        using System.Collections.Generic;

3.        using System.ComponentModel;

4.        using System.Data;

5.        using System.Drawing;

6.        using System.Text;

7.        using System.Windows.Forms;

8.        using System.Reflection;

9.         

10.     namespace 商场管理软件

11.     {

12.         public partial class Form1 : Form

13.         {

14.             public Form1()

15.             {

16.                 InitializeComponent();

17.             }

18.      

19.             DataSet ds;//用于存放配置文件信息

20.             double total = 0.0d;//用于总计

21.      

22.             private void Form1_Load(object sender, EventArgs e)

23.             {

24.                 //读配置文件

25.                 ds = new DataSet();

26.                 ds.ReadXml(Application.StartupPath + "//CashAcceptType.xml");

27.                 //将读取到的记录绑定到下拉列表框中

28.                 foreach (DataRowView dr in ds.Tables[0].DefaultView)

29.                 {

30.                     cbxType.Items.Add(dr["name"].ToString());

31.                 }

32.                 cbxType.SelectedIndex = 0;

33.             }

34.      

35.             private void btnOk_Click(object sender, EventArgs e)

36.             {

37.                 CashContext cc = new CashContext();

38.                 //根据用户的选项,查询用户选择项的相关行

39.                 DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + cbxType.SelectedItem.ToString()+"'"))[0];

40.                 //声明一个参数的对象数组

41.                 object[] args =null;

42.                 //若有参数,则将其分割成字符串数组,用于实例化时所用的参数

43.                 if (dr["para"].ToString() != "")

44.                     args = dr["para"].ToString().Split(',');

45.                 //通过反射实例化出相应的算法对象

46.                 cc.setBehavior((CashSuper)Assembly.Load("商场管理软件").CreateInstance("商场管理软件." + dr["class"].ToString(), false, BindingFlags.Default, null, args, nullnull));

47.                 

48.                 double totalPrices = 0d;

49.                 totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));

50.                 total = total + totalPrices;

51.                 lbxList.Items.Add("单价:" + txtPrice.Text + 数量:" + txtNum.Text + " "+cbxType.SelectedItem+ 合计:" + totalPrices.ToString());

52.                 lblResult.Text = total.ToString();

53.             }

54.      

55.             private void btnClear_Click(object sender, EventArgs e)

56.             {

57.                 total = 0d;

58.                 txtPrice.Text = "0.00";

59.                 txtNum.Text = "1";

60.                 lbxList.Items.Clear();

61.                 lblResult.Text = "0.00";

62.             }

63.      

64.             

65.         }

66.     }

 

看出点门道来了吗,这里通过读取一个文件来确定用什么类。这样我们就吧逻辑判断转移到了配置文件中。相应的增加一个配置文件,这里采取的是xml文件,如下:

1.        <?xml version="1.0" encoding="utf-8" ?>

2.        <CashAcceptType>

3.            <type>

4.                <name>正常收费</name>

5.                <class>CashNormal</class>

6.                <para></para>

7.            </type>

8.            <type>

9.                <name>300100</name>

10.             <class>CashReturn</class>

11.             <para>300,100</para>

12.         </type>

13.         <type>

14.             <name>20050</name>

15.             <class>CashReturn</class>

16.             <para>200,50</para>

17.         </type>

18.         <type>

19.             <name>8</name>

20.             <class>CashRebate</class>

21.             <para>0.8</para>

22.         </type>

23.         <type>

24.             <name>7</name>

25.             <class>CashRebate</class>

26.             <para>0.7</para>

27.         </type>

28.     </CashAcceptType>

29.      

效果是什么呢?

你自己试试吧,我就给你个图~

 

好的,补充到此结束。

谢谢捧场~

 

作者:王文斌

转载请注明出处~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值