`

策略模式

阅读更多
其实对于策略模式它满足了 开闭原则。对扩展开放,对修改关闭。

商场促销----策略模式

2.1商场收银软件

       “小菜,给你出个作业,做一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。”
       “就这个?木问题。”小菜说,“用两个文本框来输入单价和数量,一个确定按键来算出每种商品的费用,用个列表框来记录商品的清单,一个标签来记录总计,对,还需要一个重置控制来重新开始,不就行了?!”


商场收银系统v1.0关键代码如下:
public class Cash  
{  
    private double  total   = 0;  
  
    public void submit(int num, double price)  
    {  
        double totalPrices = num * price;  
        total += totalPrices;  
  
        System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices);  
    }  
  
    public double getTotal()  
    {  
        return total;  
    }  
  
    public void setTotal(double total)  
    {  
        this.total = total;  
    }  
}  


“大鸟,”小菜叫道,“来看看,这不就是你要的收银软件吗?我不到半个小时就搞定了啦。”
“哈哈,挺快的嘛。”大鸟说着,看了看小菜的代码。接着说:“现在我要求商场对商品搞活动,所有的商品打8折。”
“那不就是在totalPrices后面乘以个0.8吗?”
“小子,难道商场活动结束,不打折了,你还要再改一遍代码,然后再用改后的程序把所有的机器全部安装一次吗?再说,还有可能因为周年庆,打五折的情况,怎么办?”
小菜不好意思道:“啊,我想的是简单了点。其实呢,只要增加一个下拉菜单选项框就可以解决你说的问题啦。”
大鸟笑而不语。

2.2增加打折
商场收银系统v1.1关键代码如下:
public class Cash  
{  
    private double  total           = 0;  
    private int     selectedIndex   = 0;  
  
    public void selectFormLoad()  
    {  
        String[] selectForm = { "正常收费", "打8折", "打7折", "打5折" };  
        selectedIndex = 0;  
    }  
  
    public void submit(int num, double price)  
    {  
        double totalPrices = 0;  
        switch (selectedIndex)  
        {  
            case 0:  
                totalPrices = num * price;  
                break;  
            case 1:  
                totalPrices = num * price * 0.8;  
                break;  
            case 2:  
                totalPrices = num * price * 0.7;  
                break;  
            case 3:  
                totalPrices = num * price * 0.5;  
                break;  
        }  
        total += totalPrices;  
        System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices);  
    }  
  
    public double getTotal()  
    {  
        return total;  
    }  
  
    public void setTotal(double total)  
    {  
        this.total = total;  
    }  
  
    public int getSelectedIndex()  
    {  
        return selectedIndex;  
    }  
  
    public void setSelectedIndex(int selectedIndex)  
    {  
        this.selectedIndex = selectedIndex;  
    }  
}  


“这下可以了吧,只要我事先把商场可能的打折都做成下拉菜单的样子,就可以了”小菜说道。

“这比刚才灵活性上是好了,不过重复代码很多,4个分支语句除了打折多少不同以外几乎完全一样,应该考虑重构一下。不过这还不是最主要的,现在我的需求又来了,商场的活动加大,需要有满300返100的促销算法,你说该怎么办?”
“满300返100,那要是700就要返200了?这个必须要写成函数了吧?”
“小菜啊,看来之前教你的都白教了,这里面看不出来什么名堂吗?”
“哦!我想起来了,你的意思是简单工厂模式,对对对,我可以先写一下父类,再继承它实现多个打折和反利的子类,复用多态性来完成这个代码。”
“那你打算写几个子类?”
“根据需求嘛,比如8折、7折、5折、满300送100、满200送50…要几个写几个。”
“小菜又不动脑子了,有必要这样写吗?如果我现在是3折,我要满300送80,你难道再去增加子类?你不想想看,这当中哪些是相同的,哪些是不同的?”
2.3简单工厂实现

大鸟:“对的,这里打折基本都是一样的,只要有个初始化参数就可以了。满几送几的,需要两个参数才行,明白,现在看来不麻烦了。”
大鸟:“面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同的属性和功能的对象的抽象集合才是类。打1折和打9折只是形式不同,抽象分析出来,所有的打折算法都是一样的,所以打折算法应该是一个类。好了,空话说的太多了,写出来才是硬道理。”
大约1个小时后,小菜交出了第三份作业。
代码结构图

//现金收费接口  
public interface CashSuper  
{  
    public double acceptCash(double money);  
}  
//正常收费子类  
public class CashNormal implements CashSuper  
{  
  
    public double acceptCash(double money)  
    {  
        return money;  
    }  
}  
//打折收费子类  
public class CashRebate implements CashSuper  
{  
    private double  moneyRebate = 1;  
  
    public CashRebate(double moneyRebate)  
    {  
        this.moneyRebate = moneyRebate;  
    }  
  
    public double acceptCash(double money)  
    {  
        return money * moneyRebate;  
    }  
}  
//返利收费子类  
public class CashReturn implements CashSuper  
{  
    private double  moneyCondition  = 0;  
    private double  moneyReturn     = 0;  
  
    public CashReturn(double moneyCondition, double moneyReturn)  
    {  
        this.moneyCondition = moneyCondition;  
        this.moneyReturn = moneyReturn;  
    }  
  
    public double acceptCash(double money)  
    {  
        double result = money;  
        if (money >= moneyCondition)  
        {  
            result = money - money / moneyCondition * moneyReturn;  
        }  
        return result;  
    }  
}  
//现金收费工厂类  
public class CashFactory  
{  
    public static CashSuper createCash(String type)  
    {  
        CashSuper cs = null;  
        if ("正常收费".equals(type))  
        {  
            cs = new CashNormal();  
        }  
        else if ("满300返100".equals(type))  
        {  
            cs = new CashReturn(300, 100);  
        }  
        else if ("打8折".equals(type))  
        {  
            cs = new CashRebate(0.8);  
        }  
          
        return cs;  
    }  
}  
//客户端代码  
public class Main  
{  
    private static double   total   = 0;  
  
    public static void main(String[] args)  
    {  
        consume("正常收费", 1, 1000);  
        consume("满300返100", 1, 1000);  
        consume("打8折", 1, 1000);  
  
        System.out.println("总计:" + total);  
    }  
  
    public static void consume(String type, int num, double price)  
    {  
        CashSuper csuper = CashFactory.createCash(type);  
        double totalPrices = 0;  
        totalPrices = csuper.acceptCash(num * price);  
        total += totalPrices;  
        System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices);  
    }  
}  


“搞定,这次无论你要怎么改,我都可以简单处理完成。”小菜自信满满地说。
“是吗?我要的是需要打5折和满500送200的促销活动,如何办?”
“只要在现金工厂中加两个条件,在在界面的下拉菜单中增加两项即可。”
“说的不错,如果我现在需要增加一种商场促销手段,满100积分10点,以后积分到一定时候可以领取奖品如何做?”
“有了工厂,何难?加一个积分算法,构造方法有两个参数:条件和返点,让它继承CashSuper,再到现金工厂增加满100积分10点的分支条件,再到界面稍加改动就行了。”
“嗯,不错,你的简单工厂模式运用的很熟练了嘛,简单工厂模式虽然也能解决这个问题,但是这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性地更改打折额度和返利额度的,每次维护或扩展收费方式都要改动这个工厂,以致代码需要重新编译部署,这是非常糟糕的处理方式,所以用它不是最好的办法。面对算法的时常变动,应该有更好的办法。好好去研究一下其他的设计模式,你会找到答案的。”
小菜进入沉思状态…

2.4策略模式
小菜次日找到大鸟:“我找到相关的设计模式了,应该是策略模式(Strategy)。策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的,不会影响到使用算法的客户。看来商场收银系统应该考虑用策略模式?”
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
“你问我?你说呢?”大鸟笑道:“商场收银时,如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,就这点变化,而封装变化点是我们面向对象的一种很重要的思维方式。我们来看看策略模式的结构图和基本代码。”
策略模式(Strategy)结构图

//Strategy类,定义所有支持的算法的公共接口  
public interface Strategy  
{  
    public void algorithmInterface();  
}  
//ConcreteStrategy封装了具体的算法或行为,继承于Strategy  
public class ConcreteStrategyA implements Strategy  
{  
    public void algorithmInterface()  
    {  
        System.out.println("算法A实现");  
    }  
}  
public class ConcreteStrategyB implements Strategy  
{  
    public void algorithmInterface()  
    {  
        System.out.println("算法A实现");  
    }  
}  
public class ConcreteStrategyC implements Strategy  
{  
    public void algorithmInterface()  
    {  
        System.out.println("算法C实现");  
    }  
}  
//Context用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用  
public class Context  
{  
    private Strategy    strategy;  
  
    public Context(Strategy strategy)  
    {  
        this.strategy = strategy;  
    }  
  
    public void contextInterface()  
    {  
        strategy.algorithmInterface();  
    }  
}  
//客户端代码  
public class Main  
{  
    public static void main(String[] args)  
    {  
        Context context;  
        context = new Context(new ConcreteStrategyA());  
        context.contextInterface();  
          
        context = new Context(new ConcreteStrategyB());  
        context.contextInterface();  
          
        context = new Context(new ConcreteStrategyC());  
        context.contextInterface();  
    }  
}  


2.5策略模式实现
小菜:“我明白了,我昨天写的CashSuper就是抽象策略,而正常收费CashNormal、打折收费CashRebate和返利收费CashReturn就是三个具体策略,也就是策略模式中说的具体算法,对吧?”
“是的哇,来吧,你模仿策略模式的基本代码,改动一下你的程序。”
“其实不麻烦的说,原来写的CashSuper、CashNormal、CashRebate和CashReturn都不用改了,只要加一个CashContext类,并改一下客户端就可以了。”
商场收银系统v1.2
代码结构图

//CashContext类  
public class CashContext  
{  
    CashSuper   cashSuper;  
  
    public CashContext(CashSuper cashSuper)  
    {  
        this.cashSuper = cashSuper;  
    }  
  
    public double acceptCash(double money)  
    {  
        return cashSuper.acceptCash(money);  
    }  
}  
//客户端代码  
public class Main  
{  
    private static double   total   = 0;  
  
    public static void main(String[] args)  
    {  
        consume("正常收费", 1, 1000);  
        consume("满300返100", 1, 1000);  
        consume("打8折", 1, 1000);  
  
        System.out.println("总计:" + total);  
    }  
  
    public static void consume(String type, int num, double price)  
    {  
        CashContext cashContext = null;  
  
        if ("正常收费".equals(type))  
        {  
            cashContext = new CashContext(new CashNormal());  
        }  
        else if ("满300返100".equals(type))  
        {  
            cashContext = new CashContext(new CashReturn(300, 100));  
        }  
        else if ("打8折".equals(type))  
        {  
            cashContext = new CashContext(new CashRebate(0.8));  
        }  
  
        double totalPrices = cashContext.acceptCash(num * price);  
        total += totalPrices;  
          
        System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices);  
    }  
}  


“大鸟,代码虽然是模仿出来了,但我感觉还是回到原来的老路子了,在客户端判断用哪一个算法。”
“是的,但是你有没有什么好办法,把这个判断过程从客户端程序中转移走呢?”
“转移?不明白,原来我用简单工厂模式可以转移,现在这样子如何做?”
“难道简单工厂就一定要是一个单独的类吗?难道不可以与策略模式的Context结合?”
“我明白了,试试。”
2.6策略与简单工厂结合

//改造后的CashContext  
public class CashContext  
{  
    CashSuper   cashSuper;  
  
    public CashContext(CashSuper cashSuper)  
    {  
        this.cashSuper = cashSuper;  
    }  
      
    public CashContext(String type)  
    {  
        if ("正常收费".equals(type))  
        {  
            cashSuper = new CashNormal();  
        }  
        else if ("满300返100".equals(type))  
        {  
            cashSuper = new CashReturn(300, 100);  
        }  
        else if ("打8折".equals(type))  
        {  
            cashSuper = new CashRebate(0.8);  
        }  
    }  
  
    public double acceptCash(double money)  
    {  
        return cashSuper.acceptCash(money);  
    }  
}  
//客户端代码  
public class Main  
{  
    private static double   total   = 0;  
  
    public static void main(String[] args)  
    {  
        consume("正常收费", 1, 1000);  
        consume("满300返100", 1, 1000);  
        consume("打8折", 1, 1000);  
  
        System.out.println("总计:" + total);  
    }  
  
    public static void consume(String type, int num, double price)  
    {  
        CashContext cashContext = new CashContext(type);  
  
        double totalPrices = cashContext.acceptCash(num * price);  
        total += totalPrices;  
  
        System.out.println("单价:" + price + " 数量:" + num + "合计:" + totalPrices);  
    }  
}  


“嗯,原来简单工厂模式并非只有建一个工厂类的做法,还可以这样子做。此时比刚才的模仿策略模式的写法要清楚多了,客户端代码简单明了。”
“那和你写的简单工厂客户端代码相比,观察一下,找出它们的不同之外。”

[java] view plaincopy
//简单工厂模式的用法 
CashSuper csuper = CashFactory.createCash(type); 
... 
totalPrices = csuper.acceptCash(num * price); 
 
//策略模式与简单工厂模式结合的用法 
CashContext cashContext = new CashContext(type); 
double totalPrices = cashContext.acceptCash(num * price); 
“你的意思是,简单工厂模式我需要让客户端认识两个类,CashSuper和CashFactory,而策略模式与简单工厂模式结合的用法,客户端就只需要认识一个类CashContext。耦合度更加降低。”
“说的木有错,我们在客户端实例化的是CashContext的对象,调用的是CashContext的方法,这使得具体的收费算法彻底地与客户端分离。连算法的父类CashSuper都不让客户端认识了。相当于创建了一个句柄类。”
2.7策略模式解析

大鸟:“回过头来反思一下策略模式,策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法与使用算法之间的耦合。”
小菜:“策略模式还有什么优点?”
大鸟:“策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。对于打折、返利或者其他的算法,其实都是对实际商品收费的一种计算方式,通过继承,可以得到它们的公共功能,你说这公共功能指虾米?”
小菜:“公共的功能就是获得计算费用的结果getResult,这使得算法间有了抽象的父类CashSuper。”
大鸟:“不错,另外一个策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。”
小菜:“每个算法可保证它没有错误,修改其中任一个时也不会影响其他的算法,这真是的非常的好。”
大鸟:“哈,小菜今天表现的不错,我所想的你都想到了。还有,在最开始编程时,不得不在客户端的代码中为了判断用哪一个算法计算而用了if条件分支,这也是正常的。因为当不同的行为堆砌于一个类中,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。就商场收银系统的例子而言,在客户端的代码中就消除条件语句,避免了大量的判断。这是非常重要的进展。你能用一句话来概况这个优点吗?”
小菜:“策略模式封装了变化。”
大鸟:“说的非常好,策略模式就是用来封装算法的,但在初中中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。”
小菜:“但我感觉在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化地减轻了客户端的职责。”
大鸟:“是的,这已经比起初的策略模式好胜了,不过,它依然不够完美。”
小菜:“还有什么不足吗?”
大鸟:“因为在CashContext里还是用到了if或switch,也就是说,如果我们需要增加一种算法,比如满200返50,你就必须要改CashContext中的if或switch代码,这让人感觉很不happy的说!”
小菜:“啊,那你说怎么办,有需求就得改啊,任何需求的变更都是需要成本的。”
大鸟:“是的哇,但是成本的高低还是有差异的嘛。高手和菜鸟的区别就是高手可以花同样的代价获得最大的收益或者说做同样的事花最小的代价。面对同样的需求,当然是改动越小越好啦。”
小菜:“你的意思是说,还有更好的办法?”
大鸟:“当然啦,要不然我怎么会这样说哩,这个办法就是用到了反射技术,不是常有人讲:‘反射反射,程序员的快乐’,不过今天就不讲了,以后会再提它的。”
“反射真的有这么神奇?”小菜疑惑地望向了远方。
(注:在抽象工厂模式章节有对反射的讲解)
分享到:
评论

相关推荐

    策略模式结合模板方法模式

    策略模式结合模板方法模式的设计思路 策略模式结合模板方法模式是策略模式的一种变形,目的是为了解决策略模式中的一些共性问题。在策略模式中,经常会出现这样一种情况,就是发现这一系列算法的实现上存在公共功能...

    详解SpringBoot结合策略模式实战套路

    SpringBoot结合策略模式实战套路 策略模式是一种常用的设计模式,它可以使我们的代码更加灵活、可维护和可扩展。在SpringBoot项目中,策略模式可以与依赖注入机制相结合,实现更加灵活的业务逻辑处理。在本文中,...

    设计模式之策略模式 鸭子问题

    设计模式之策略模式 鸭子问题 策略模式是一种经典的设计模式,通过鸭子问题,可以让学习者更好地了解设计模式的概念和实现。策略模式的主要思想是定义一系列的算法,并将每一个算法封装起来,使它们可以相互替换。...

    桥接模式和策略模式的区别,内含可运行代码和两者详细区别

    桥接模式和策略模式是软件设计模式中的两种重要模式,它们在实现上有着相似之处,但各自的应用场景和设计理念有所不同。下面将详细阐述这两种模式的特点、区别以及它们在实际编程中的应用。 首先,桥接模式(Bridge...

    策略模式在实际项目中的应用二

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在Java中,策略模式通过定义一系列的算法,并将每一个算法封装起来,使它们可以相互替换,让算法独立于使用它的客户而变化。这种模式通常用于处理多种...

    Spring下使用策略模式

    在Spring框架中,策略模式是一种常见的设计模式,它允许我们定义一组可互换的策略,这些策略可以在运行时根据需求动态选择。这篇文章将深入探讨如何在Spring中运用策略模式,并结合源码分析其工作原理。 策略模式的...

    策略模式的实现,通过反射

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在Java中,策略模式通常用于将算法封装到不同的类中,使得可以根据需要动态选择并应用这些算法。本示例将详细介绍如何通过两种方法实现策略模式:一种...

    抽象工厂模式+工厂方法模式+策略模式+类图实现手机加工厂

    本文将探讨三个重要的设计模式:抽象工厂模式、工厂方法模式以及策略模式,并结合一个实际的场景——手机加工厂,来具体阐述它们的应用。 首先,我们来看**抽象工厂模式**。这个模式主要用于创建相关或依赖对象的...

    55-Java设计模式之策略模式与状态模式1

    Java 设计模式之策略模式与状态模式 策略模式是 Java 中的一种设计模式,它主要用于解决系统与第三方接口进行数据交互的问题。当系统需要与多种格式的数据进行交互时,使用策略模式可以很好地解决这个问题。例如,...

    策略模式封装的几个加密解密算法源码

    在"策略模式封装的几个加密解密算法源码"中,我们主要关注的是如何使用策略模式来封装常见的加密解密算法,如BASE64和MD5。 1. **BASE64编码**:BASE64是一种用于将二进制数据编码为ASCII字符的编码方式,以便在...

    策略模式的简单例子

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在软件开发中,我们经常遇到需要根据不同条件或选择执行不同算法的情况。策略模式提供了一种将算法封装到独立可互换的策略对象中,使得算法的变化独立...

    设计模式之策略模式,商场收银,封装算法

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在软件开发中,我们经常遇到需要根据不同的条件或场景来执行不同算法的情况。策略模式就是为了解决这类问题而提出的,它将每种算法封装到具有共同接口...

    js策略模式和代理模式

    策略模式和代理模式是设计模式中的两种常见模式,它们在软件开发中扮演着重要的角色,尤其是在JavaScript中,这两种模式提供了更加灵活和可维护的代码结构。 策略模式(Strategy Pattern)是一种行为设计模式,它...

    策略模式 template模式

    策略模式(Template模式) 策略模式是设计模式中的一种 객체行为型模式,它定义了一系列算法,封装每一个算法,并使它们可以互相替换。策略模式使得算法可以独立于使用它的客户而变化。 概述 在软件开发中,经常...

    Java 设计模式 策略模式

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在Java中,策略模式主要通过定义一系列的算法,并将每一个算法封装起来,使它们可以互相替换,让算法独立于使用它的客户而变化。 首先,策略模式的...

    策略模式的示例代码和思想模式

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在Java中,策略模式通常涉及接口或抽象类的实现,允许程序在运行时选择并应用不同的算法或策略。这种模式的核心在于将算法封装到独立的可互换的策略中...

    Java策略模式+案例

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在Java中,策略模式允许我们定义一组算法或策略,并将每个策略封装为一个类,使得它们可以互换,而不会影响到客户端代码。这种模式的核心在于"策略",...

    策略模式代码实现

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在策略模式中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为模式。 策略模式定义了一系列的算法,并将每一个算法封装起来,使...

    设计模式——策略模式

    策略模式的设计与实现 策略模式是一种常用的设计模式,它定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。策略模式的主要优点是它可以使得算法的变化独立于使用算法...

    软件设计模式策略模式实例

    策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在软件工程中,当一个系统需要在不同时间执行不同的算法或者行为时,策略模式就显得尤为有用。这种模式将算法封装到独立的可相互替换的策略类中,使得...

Global site tag (gtag.js) - Google Analytics