Strategy(策略)模式

本文探讨了策略模式在代码组织中的应用,通过Oozinoz公司推销焰火制品的案例,详细介绍了如何使用策略模式来封装不同的算法或策略,使程序更加灵活且易于维护。

  算法是定义好的过程,它能够根据一组输入产生一个输出.而策略是一个计划,它也涉及如何从一组输入获得一组输出.在通常情况下,与算法相比,策略是能够提供更大范围的可选方案.因而,策略通常代表一组或者一簇可以相互替换的方案.

   当计算机程序存在多种策略时,程序代码会比较复杂.当有多个策略可用时,与策略相关的程序逻辑必须选择其中一个策略来执行,这时策略选择的程序代码可能变得很复杂,同时策略使用的逻辑通常也比较复杂.我们可以使用Strategy模式来整理这些代码.

   策略操作定义了策略的输入和输出,而把策略的实现工作留给各个类。这些类以不同的方案来实现同一个操作,它们为用户提供统一的接口,因而这些类可以相互替换。Strategy模式允许多种策略共存,而代码不会混乱。策略模式也可以实现模式选择逻辑和策略本身相分离。

   Strategy模式的意图在于把可选的策略或方案封装在不同的类中,并在这些类中实现一个共同的操作。

 

1.策略建模

   Strategy模式把问题的不同解决方法封装在不同的类中,有助于组织和简化代码。先来看看不使用Strategy模式情况下策略的建模方式:

   现在思考一下Oozinoz公司的焰火制品推销广告策略。当客户访问Oozinoz公司的网站或给公司呼叫中心打电话时,客户会获得一些有关购买焰火制品的建议。Oozinoz公司使用了两个现有的推荐引擎来向客户推荐合适的焰火制品。Customer类选择并应用其中的一个引擎来决定应该向客户推荐哪种焰火制品。

  Rel8引擎是其中的一个推荐引擎,它能够根据客户之间的相似性来向客户推荐焰火制品.在使用这种引擎之前,客户必须进行注册,并且给出一些诸如他对焰火和其他娱乐活动的喜好或厌恶等信息.

  如果客户没有注册,Oozinoz公司则使用另一个名为LikeMyStuff的推荐引擎,这个引擎能够根据客户最近的购买信息向其进行推荐.如果对这两种引擎都没有提供足够的信息,该推销软件将随机选择一种焰火向客户推荐.不过,如果Oozinoz公司希望推销某种焰火制品,那么这种焰火制品将会俦被推销,而不考虑前面的因素.图1所示的各个类相互协作,从而实现向客户推荐焰火制品的行为.

 

  LikeMyStuff和Rel8引擎分别接收一个Customer对象,并将某种焰火制品推荐给这个客户.在利用两个引擎推荐焰火制品之前,我们必须对它们进行配置.不过,LikeMyStuff需要一个数据库,而Rel8则完全依赖于对象模型.Customer类的getRecommend()方法实现了Oozinoz公司的焰火制品推销广告策略:

    public Firework getRecommended() {
        // if we're promoting a particular firework, return it
        try {
            Properties p = new Properties();
            p.load(ClassLoader.getSystemResourceAsStream("config/strategy.dat"));
            String promotedName = p.getProperty("promote");

            if (promotedName != null) {
                Firework f = Firework.lookup(promotedName);
                if (f != null) return f;
            }
        } catch (Exception ignored) {
            // If resource missing or it failed to load,
            // fall through to the next approach.
        }

        // if registered, compare to other customers
        if (isRegistered()) {
            return (Firework) Rel8.advise(this);
        }

        // check spending over the last year
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.YEAR, -1);
        if (spendingSince(cal.getTime()) > 1000) return (Firework) LikeMyStuff.suggest(this);

        // oh well!
        return Firework.getRandom();
    }

 getRecommended()方法希望如果在促销焰火制品时,将焰火制品的名称保存在config目录下的strategy.dat文件中.

 protmote = JSquirrel

 如果没有类似这样的文件,并且客户已经注册,则getRecommended()方法会使用Rel8引擎,如果没有促销焰火制品,客户没有注册,并且客户在本系统中留有以前的购买记录,则getRecommended()方法会使用LIkeMyStuff引擎.如果客户既没有注册,也不是大客户,上面的代码则会随机选择一种焰火制品推荐给他.getRecommended()方法的确可以起作用,并且这段代码写得并不是很糟糕.不过,我们还可以让它变得更好.


Customer类依赖于其他的类来获取将被推荐的焰火制品,其中包括两个现有的推荐引擎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值