【DM】设计模式再回顾---策略模式

写在前面的

  • 上一篇文章我们说到,如果我们需要在原本已经整理好的代码中添加新的内容(包括算法或者功能性模块),我们可以应用简单工厂来实现,比如添加算法(活学活用嘛)、在已有的功能模块中再添加新的功能等。但是,我们在上一篇文章中提到过开放-封闭原则,试想一下,如果我们写出来的代码在应用过程中一直需要不断的调整和增加新的功能,那么每次维护都要打开这个工厂,添加相应的功能或算法之后再重新部署,这样无意间会增加我们的工作量,所以简单工厂在某种意义上就不适用了。

  • 了解到了简单工厂模式的局限性,那么我们就要学习新的设计模式了,也就是我接下来要讲到的“策略模式”

策略模式

概念

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

  • 从定义可以看出,我们所要往原来的代码中添加的某种功能或者算法在一定的情景下是可以“替换”的,具体的实例我们后面会讲到。

  • 来看一下策略模式的UML图:

这里写图片描述

  • 需要注意的是,Context类与与Strategy之间是聚合的关系

具体实例

  • 原来打算是将上一篇中的计算器用策略模式来实现,但是感觉这样的话不是能够很好的体现封装的思想,所以还是决定按照书上的例子再加上我的总结来说明策略模式。

  • 首先,我们来创建上述的几个类,UML图如下:

这里写图片描述

 //超类,需要被具体算法类继承来重写
abstract class CashSuper
{
    public abstract double acceptCash(double money)
    {
        return money;
    }
}

//正常收费子类
class CashNormal : CashSuper
{
    public override double acceptCash(double money)
    {
        return money;
    }
}

//打折收费子类
class CashRebate : CashSuper 
{
    private double moneyRebate = 1d; //初始化打折率
    public CashRebate(string moneyRebate)
    {
        //从CLIENT获取打折率
        this.moneyRebate=double.Parse(moneyRebate);
    }

    public override double acceptCash(double money)
    {
        //将CLIENT传来的参数进行计算
        return money * moenyRebate;
    }
}


class CashReturn : CashSuper
{
    //初始化参数
    private double moenyCondition = 0.0d;
    private double moneyReturn= 0.0d;

    //从CLIENT获取参数值
    public CashReturn(string mC,string mR)
    {
        this.moneyCondition=double.Parse(mC);
        this.moneyReturn=double.Parse(mR);
    }

    //其实这里无论计算的方法是什么样子的都无所谓,我们只需要理解这样一个思路
    public override couble acceptCash(double money)
    {
        double result =money;
        if (money >= moneyCondition)
            result = money - Math.Floor(money/moneyCondition) * moneyReturn; //Math.Floor()表示取出该值最接近的整数

        return result;
    }
}
  • 然后,就是最关键的地方了,我们需要在CashContext中体现商场的现阶段的所有促销活动,创建CashContext类:
//在这类加入简单工厂模式,方便引用
class CashContext
{
    //由于我们需要调用CashSuper下的所有的方法,并且需要与CashSuper有“聚合”的关系,所以我们需要初始化CashSuper
    //对于一些需要存值的对象实例化时一定要初始化,否则会吃很大的亏,最要命的一点就是“空值”和NULL
    CashSuper cs = null;

    public CashContext(string type)
    {
        switch (type)
        {
            case"正常收费":
                cashNormal cn =new CashNormal();
                cs=cs0;
                break
            case"满300返100":
                CashReturn cr1 =new CashReturn("300","100");
                cs=cr1;
                break;
            case"打八折":
                CashRebate cr2=new CashRebate("0.8");   
                cs=cr2;
                break;  
        }
    }

    public double GetResult(double money)
    {
        return cs.acceptCash(moeny);
    }
}
  • 最后,我们来写一下CLIENT代码,由于实现的方法有很多,所以我之将重要的一部分写出来,其他的部分由文字代替。
//初始化参数
double total=0.0d;
private void Main()
{
    //获取促销活动类型
    CashContext csuper=new CashContext("这里传入需要进行的促销活动,如打八折")

    //获取顾客消费金额
    double totalPrices=0d;
    totalPrices=cspuer.GetResult("传入本次消费的金额");

    //计算并显示消费金额
    total=total+totalPrices;
    Console.WriteLine("本次消费金额是:[0]",total);
}

比较和理解

  • 首先,我们比较一下简单工厂模式策略模式+简单工厂在代码量上的区别。在简单工厂中,如果我们要在客户端实例化计算类,就需要实现工厂类以及算法类,但是在策略模式+简单工厂中,我们只是实现了一个CashContext类,所以我们在代码量上有了非常明显的减少。

  • 其次,我们来分析一下这两个设计模式在处理具体问题上有什么不同。我们的所写的代码并不是写完了之后就不需要维护了,事实上我们需要一直对代码进行功能或者算法的增加或者修改等操作。试想一下,如果我们的超市一直推出新的促销活动,如果在这样的情况下我们使用简单工厂(并不是不能用),那么在每一次的维护中,我们都要打开工厂类去添加新的CASE,然后再添加一个与之对应的算法。如果在某一次的维护中,有人不小心把之前的代码做了改动,那么损失就不只是一块两块了,同时这样也违背了我们的开放-封闭原则。但是对于策略模式而言,由于我们之前所写的CashContext类已经把原有的算法做了封装,所以我们再增加新的功能或者算法的时候只需要再新增添一个CashContextNew类,然后在这个新的CashContextNew类中增加CASE,再写出与之对应的算法类就可以了,然后还可以无限制的增加增加下去。为什么可以这样?就是因为我们的CashContext类与CashSuper之间是聚合关系,所以我们可以继续无限制增加新的“促销活动”而不用修改原来的

  • 通过上面的对比总结,我们可以看出,通过一个聚合关系,我们可以在这里问题中完美的实现开放-封闭原则,同时也为我们的代码实现了“易维护”的特性。

总结

  • 一路看过来,通过策略模式的一个聚合关系,就完美的实现了“高内聚,低耦合”的代码设计理念,打到了代码的易扩展与易维护,可见设计模式的力量还是很大的。

  • 其实,在一开始的时候我并不倾向于把所有的代码都总结并写在这里,但是为了自己再练习回顾一遍,这个懒还是不偷了,因为设计模式不能只停留在自己的脑海中,一定要自己亲自实现一遍这样的印象才会更加深刻。

TO BE CONTINUE……

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
课程概要:          讲解 Struts2 中数据封装的三种方式以及具体实现原理   一、 Struts2 数据封装机制之属性驱动   我们先来看一下传统的 servlet 是如何处理从页面传递过来的数据的。 首先我们在表单发送了对应的数据到 servlet 中去 form action="%=path%/loginservlet"method="post" 账号:inputtype="text"name="username"/br 密码:inputtype="password"name="pas
Spring MVC框架是有一个MVC框架,通过实现Model-View-Controller模式来很好地将数据、业务与展现进行分离。从这样一个角度来说,Spring MVC和Struts、Struts2非常类似。Spring MVC的设计是围绕DispatcherServlet展开的,DispatcherServlet负责将请求派发到特定的handler。通过可配置的handler mappings、view resolution、locale以及theme resolution来处理请求并且转到对应的

通过WSDL生成客户端代码 - 2016-07-25 14:07:03

目录 1.WSDL2Java:Building stubs,skeletons,and data types from WSDL . 1 1.1示例 ... 1 1.2测试 ... 4 1.2.1异常:没有定义com.pan.model.User的序列化的实现 ... 5 1.3WSDL与生成的客户端代码结构分析 ... 5 1.3.1Types . 6 1.3.2Holders . 12 1.3.3PortTypes . 13 1.3.4Bindings . 13 1.3.5 Services . 14
                                               Ofbiz:数据库移植mysql并创建自己的mysql            Ofbiz原生数据库是derby,而作为开发使用,其就不能满足我们需求,ofbiz支持多种数据库,我们就可以将数据移植到mysql.            第一步:找到framework\entity\config\entityengine.xml这个文件,找到之后进行下面相关操作.          1、添加或者修改datasourc

linux的一些简单命令 - 2016-07-25 14:07:59

这里只是列出实际中使用频率较高的,可以通过 man 命令或者 命令 –help 来查看更为详细的内容 文件有关的 1:【ls命令】 ls [option] …[file]… -a all 列出所有的文件 包括隐藏文件 [eg ls -a /home] -l 列出详细的文件信息 可以简写为ll filename [eg: ls -l /home or ll /home ] -h –human-readable 将文件的大小通过字节的方式列出来 -R 递归显示出该目录所有的文件 -d 只显示本文件下面 可以通

Nginx下的rewrite规则 - 2016-07-25 14:07:08

正则表达式匹配,其中: * ~ 为区分大小写匹配 * ~* 为不区分大小写匹配 * !~和!~* 分别为区分大小写不匹配及不区分大小写不匹配 文件及目录匹配,其中: * -f 和! -f 用来判断是否存在文件* -d 和! -d 用来判断是否存在目录* -e 和! -e 用来判断是否存在文件或目录* -x和!-x 用来判断文件是否可执行 rewrite指令的最后一项参数为flag标记,flag标记有: 1. last 相当于apache里面的[L]标记,表示rewrite。 2. break 本条规则匹配

Apache Flink Client生成StreamGraph - 2016-07-25 04:07:11

概述 上文我们分析提交流程时, RemoteStreamEnvironment 类的 execute 方法的第一步就是生成 StreamGraph 。 StreamGraph 是用于表示流的拓扑结构的数据结构,它包含了生成 JobGraph 的必要信息。它的类继承关系图如下: 如果你按照 StreamGraph 的继承链向上追溯,最终会发现它实现了接口 FlinkPlan 。Flink在这里效仿的是数据库的执行SQL是产生执行计划的机制, FlinkPlan 定义在Flink的优化器相关的包中,针对流应用

zabbix wechat 报警 - 2016-07-24 22:07:29

监控在运维工作中是比不可少的一环,那伴随着监控也同时会涉及到告警机制,一般的监控到的结果是成功或者失败,如Ping不通、访问网页出错、连接不到Socket,发生时这些称之为故障,故障是最优先的告警。那针对于 zabbix 的告警可以有多种方式去做: zabbix三种常见报警介质: 短信:它的好处是不用联网手机有信号就行,但是需要有短信网关,需要花钱。 邮件:它也可以做到手机短信通知,基本现在邮箱都有这个功能(如果你使用的是移动的手机号,可以让zabbix将报警信息发送到139邮箱,再通过139绑定到手机号
J2EE进阶(十一)SSH框架整合常见问题汇总(二) 问题 8       java.lang.ClassCastException : java.lang.String cannot be cast to java.lang.Boolean      解决       数据库中userdetail表的映射文件如下,可见xb字段数据类型为boolean类型,而自己在userdetail模型类中定义的类型为String类型。为此可以得出这样的结论。模型类中的数据及类型必须与数据表映射文件中的字段信息保持一致
背景: 为了方便整体产品的发布,希望通过docker实现增量发布。大致的思路如下: is-there-a-way-to-add-only-changed-files-to-a-docker-image-as-a-new-layer-with 。本博文对这种方式进行了尝试,与此同时简单介绍如何通过Dockerfile来创建Docker镜像。 前期准备: 解决centos的网络问题 【can not find a valid baseurl for repo: base/7/x86_64】 ,使用dhclie