设计模式(1)(2):简单工厂模式和策略模式

来源:互联网 发布:吃肉 知乎 编辑:程序博客网 时间:2024/06/11 01:58

在这里以书中 商场促销 为例分析一下  简单工厂模式,所谓设计模式,我认为最主要的就是竟可能实现代码的封装,达到代码模块之间低耦合的目的,这就用到了继承和多态,看看代码实现。


首先,建立一个抽象类,用于子类继承,定义了抽象方法 acceptCash() 方法传入参数 money ,实现相应算法

<span style="font-size:14px;">public abstract class CashSuper{     public abstract double acceptCash(double money);}</span>

这里定义了三个子类,分别处理 正常收费 , 打折 , 满300返100 三种收费方法,复写 acceptCash()方法,这就是多态的体现吧

<span style="font-size:14px;">    /// <summary>    /// 正常收费子类,输入 money,无变化直接返回 money    /// </summary>    public class CashNormal : CashSuper    {        public override double acceptCash(double money)        {            return money;        }    }</span>
</pre><p></p><pre name="code" class="csharp"><span style="font-size:14px;">    /// <summary>    /// 打折收费子类,<span style="font-family: Arial, Helvetica, sans-serif;">CashRebate() 构造函数传入折扣率,如八折为0.8</span>    /// </summary>    public 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;        }       }</span>

<span style="font-size:14px;">    /// <summary>    /// 返现收费子类,<span style="font-family: Arial, Helvetica, sans-serif;">CashReturn() 两个参数,满300和返100</span>    /// </summary>    public 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;        }    }</span>


下面就是工厂类了

<span style="font-size:14px;">    /// <summary>    /// 现金收费工厂类    /// </summary>    public class CashFactory    {        public static CashSuper createCashAccept(string type)        {            CashSuper cs = null;            switch (type)            {                case "正常收费":                    cs = new CashNormal();                    break;                case "打八折":                    cs = new CashRebate("0.8");                    break;                case "打七折":                    cs = new CashRebate("0.7");                    break;                case "满300返100":                    cs = new CashReturn("300","100");                    break ;            }            return cs;        }    }</span>
简单工厂职责就是根据传入的参数判断返回的 CashSuper 对象,进而由该对象进行相应的操作,看看客户端代码就明白了


<span style="font-size:14px;">        private void button_收费_Click(object sender, EventArgs e)        {            CashSuper csuper = CashFactory.createCashAccept(cbxType.SelectedItem.ToString());            double totalPrice = 0d;            totalPrice = csuper.acceptCash(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));        }</span>

首先创建 CashSuper 对象,调用工厂类的 createCashAccept()方法,根据传入的字符串(正常收费,打八折……)返回相应的 CashSuper 对象,然后执行刚才复写的 acceptCash()方法,传入参数 money 进行计算。

引用一下百度的:

优点
工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点
由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。
当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利。

就像本例中一样,简单工厂模式虽然能解决功能问题,实现了代码的封装较低了模块间耦合性,但是这个设计模式只解决了对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性的更改打折额度和返利额度的,每次维护和扩展收费方式都要改动工厂,以至于代码需要重新编译部署,面对算法的时常改动 策略模式 或许是一种好的方法。


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

商场收银时候如何促销,用打折还是返利,其实都是一些算法,用工厂来实现算法本身没有错。但算法本身只是一种策略,最重要的是这些算法都是针对同一问题的不同解决方案,他们之间随时可以相互更换,这就是变化点,而封装变化点是面向对象的一种很重要的思维方式。


同样的例子,使用策略模式进行修改:

CashSuper和三种处理方法不用更改,只需在简单工厂基础上添加 CashContext 类

<span style="font-size:14px;">    class CashContext    {        private CashSuper cs;        public CashContext(CashSuper csuper)        {            this.cs = csuper;        }        public double GetResult(double money)        {            return cs.acceptCash(money);        }    }</span>
客户端代码如下,将相应的策略对象作为参数传入 CashContext 对象中

<span style="font-size:14px;">        private void button1_Click(object sender, EventArgs e)        {            CashContext cc = null ;            switch(cbxType .SelectedItem.ToString())            {                case "正常收费":                    cc = new CashContext (new CashNormal());                    break ;                case "打八折":                    cc = new CashContext (new CashRebate("0.8"));                    break ;                case "满300返100":                    cc = new CashContext (new CashReturn("300","100"));                    break ;            }            double totalPrice = 0d;            totalPrice = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));        }</span>
通过对 Context 的 GetResult() 方法的调用,可以得到收取费用的结果,让具体的算法与客户端进行了分离。现在判断是在客户端实现的,并没有减轻客户端需要选择判断的压力,再次改进,仿照简单工厂方法将客户端判断移走,修改 CashContext 类

<span style="font-size:14px;">    class CashContext    {        private CashSuper cs;        public CashContext(string type)        {            switch (type)            {                case "正常收费":                    cs = new CashNormal();                    break;                case "打八折":                    cs = new CashRebate("0.8");                    break;                case "满300返100":                    cs = new CashReturn("300", "100");                    break;            }        }        public double GetResult(double money)        {            return cs.acceptCash(money);        }    }</span>
客户端代码如下

<span style="font-size:14px;">CashContext csuper = new CashContext(cbxType.SelectedItem.ToString());            double totalPrice = 0d;            totalPrice = csuper.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));</span>
比较简单工厂的方法知道,简单工厂需要客户端认识两个类 CashSuper 和 CashFactory ,而策略模式客户端只需要认识 CashContext 类就可以了,使得具体的收费算法彻底与客户端分离,耦合度更加降低。


策略模式就是用来封装算法的,只要在分析中听到需要在不同时间响应不同的业务规则,就可以考虑使用策略模式处理这种变化。

策略模式是一种定义了一系列算法的方法,从概念上看,所有的方法都完成了相同的工作,只是实现方法不同,他们可以以相同的方式调用所有的算法,减少了各种算法类与算法实用类之间的耦合。另外策略模式还有简化了单元测试的优点,修改任何一个算法都不会影响其他。


初次学习设计模式,心得写下来和大家分享,如有错误还望大家不吝赐教。

0 0