策略(Strategy)模式

 
面向对象编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类
我们在Martin编写的《代码整洁之道》中对类的看法,类应该短小(长度不应该容纳一个if嵌套语句,20行封顶),而且只做一件事,做好这件事。强调的是简洁和优雅,但是不没说类越多越好。
这里我们可以明白我们应该用什么态度来看待和创建类,对我们的工程很重要。
策略模式的定义:
Image
 
策略模式UML:
SpxImage
 
 
主要的东西还是Context的配置,传入具体的策略,然后返回出对应策略的的结果。
下面是收费策略的一个事例
View Code     //收费策略Context
class CashContext
{
    //声明一个现金收费父类对象
    private CashSuper cs;


    //设置策略行为,参数为具体的现金收费子类(正常,打折或返利)
    public CashContext(CashSuper csuper)
    {
        this.cs = csuper;
    }


    //得到现金促销计算结果(利用了多态机制,不同的策略行为导致不同的结果)
    public double GetResult(double money)
    {
        return cs.acceptCash(money);
    }
}
简单工厂与策略模式的结合是比较常见的。
 
简单工厂与策略模式结合事例(把客户端的switch给封装了,封装了变化的体现)
 
View Code     //现金收取工厂
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);
    }
}


但是单是试用策略模式客户端如下:
 
View Code  private void btnOk_Click(object sender, EventArgs e)
        {
            CashContext cc = null ;
            switch (cbxType.SelectedItem.ToString())
            {
                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 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();
        }


如果与简单工厂结合客户端就有交大改善:
View Codeprivate 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();
        }
 
对比一下可以知道后则只暴露了一个配置类Content而前者必须要客户端知道除了配置类其他的类,这样前者耦合性更低。
后则也把判断从客户端的判断条件给移除了,这样客户端判断压力减小了。
在定义上该模式是封装算法的,但是我几个人建议学习设计模式千万不要因为模式而模式,要因需求和设计而模式。因此
只要是不同时间要应用不同的业务那么这时候就该考虑策略模式了 。
总结:
策略模式与简单工厂的区别:
主要就是用Content上下文配置,客户端通过条件判断传递一个事例,然后实 化对应对像,
加上一个GetResult方法算出结果,客户端调用该方法返回结果得到值。 这里对算法进行了封装
--------------------------------------------------------------------------------------------------------------
而简单工厂就是客户端只需要传递一个判断标识,然后工厂根据标识做出判断应该返回什么 事例,
客户端拿到返回 事例对象,通过调用该对象得到返回结果。 这里对判断后置了减少了客户端判断压力
策略模式与简单工厂策略模式区别:
就是对前者优点的结合。
既封装了算法也减少了客户端判断压力
---------------------------------------------------------------------------------------------------------------
到此为止如果需求变动(增加一种算法)那么我们还是需要修改switch 条件
增加一个具体的算法类。
 
那么该如何让其不改动代码呢?对就是反射
我们以一种数据驱动的形式来处理这个问题,下拉框内存我们通过读取配置文件获取
客户端只需要传入类名和需要的参数即可如下:
 
View CodeDataSet 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(dr["class"].ToString(), args);
           
            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();
        }
 
而我们的配置类就应该是实现反射的类如下:
View Codeclass CashContext
{
    private CashSuper cs;

    public void setBehavior(string className, object[] args)
    {
        this.cs = (CashSuper)Assembly.Load("商场管理软件").CreateInstance("商场管理软件." + className, false, BindingFlags.Default, null, args, null, null);
    }

    public double GetResult(double money)
    {
        return cs.acceptCash(money);
    }
}
到此为止我们使用简单工厂策略模式+反射实现了一种数据驱动的方式实现了代码0修改。
数据驱动是我们常用的一种设计方案,这是一种伟大的进步,从数据驱动直到今天的脚本游戏引擎比如说unity3d............扯远了....下一篇来吧....................................
 
 
 

转载于:https://www.cnblogs.com/Wonder1989/archive/2013/03/27/2985275.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值