一文读懂策略模式

设计二十三式之策略模式


插播

01 意图

策略是一种行为设计模式,它允许您定义一系列算法,将它们中的每一个放入一个单独的类中,并使它们的对象可互换。

策略

02 问题

有一天,您决定为休闲旅行者创建一个导航应用程序。该应用程序以精美的地图为中心,可帮助用户在任何城市快速定位自己。

该应用程序最需要的功能之一是自动路线规划。用户应该能够输入地址并在地图上查看到该目的地的最快路线。

该应用程序的第一个版本只能在道路上构建路线。驾车出行的人都喜出望外。但显然,并不是每个人都喜欢在假期开车。因此,在下一次更新中,您添加了一个构建步行路线的选项。在那之后,您添加了另一个选项,让人们在他们的路线中使用公共交通工具。

然而,这仅仅是个开始。后来您计划为骑自行车的人添加路线建设。甚至在以后,另一种选择是建立穿越城市所有旅游景点的路线。

虽然从业务角度来看,该应用程序是成功的,但技术部分却让您头疼不已。每次添加新的路由算法时,导航器的主类都会增加一倍。在某些时候,野兽变得难以维持。

对其中一种算法的任何更改,无论是简单的错误修复还是街道分数的轻微调整,都会影响整个类,增加在已经工作的代码中创建错误的机会。

此外,团队合作变得低效。您的团队成员在成功发布后立即被雇用,他们抱怨他们花费太多时间来解决合并冲突。实现一个新特性需要你更改同一个巨大的类,与其他人生成的代码冲突。

03 解决方案

策略模式建议您采用一个以多种不同方式执行特定操作的类,并将所有这些算法提取到称为策略的单独类中。

称为context的原始类必须有一个字段用于存储对其中一种策略的引用。上下文将工作委托给链接的策略对象,而不是自行执行。

上下文不负责为工作选择合适的算法。相反,客户端将所需的策略传递给上下文。实际上,上下文对策略了解不多。它通过相同的通用接口与所有策略一起工作,该接口仅公开一个用于触发封装在所选策略中的算法的方法。

这样,上下文变得独立于具体策略,因此您可以添加新算法或修改现有算法,而无需更改上下文代码或其他策略。

在我们的导航应用程序中,每个路由算法都可以通过一个buildRoute方法提取到自己的类中。该方法接受起点和终点,并返回路线检查点的集合。

即使给定相同的参数,每个路由类也可能构建不同的路由,主导航器类并不真正关心选择哪种算法,因为它的主要工作是在地图上呈现一组检查点。该类具有切换活动路由策略的方法,因此其客户端(例如用户界面中的按钮)可以将当前选择的路由行为替换为另一个。

04 举个栗子

想象一下,你必须去机场。您可以搭乘公共汽车、叫出租车或骑自行车。

这些是您的运输策略。您可以根据预算或时间限制等因素选择其中一种策略。

05 结构实现

类图

在此示例中,上下文使用多种策略来执行各种算术运算。

// The strategy interface declares operations common to all
// supported versions of some algorithm. The context uses this
// interface to call the algorithm defined by the concrete
// strategies.
interface Strategy is
    method execute(a, b)

// Concrete strategies implement the algorithm while following
// the base strategy interface. The interface makes them
// interchangeable in the context.
class ConcreteStrategyAdd implements Strategy is
    method execute(a, b) is
        return a + b

class ConcreteStrategySubtract implements Strategy is
    method execute(a, b) is
        return a - b

class ConcreteStrategyMultiply implements Strategy is
    method execute(a, b) is
        return a * b

// The context defines the interface of interest to clients.
class Context is
    // The context maintains a reference to one of the strategy
    // objects. The context doesn't know the concrete class of a
    // strategy. It should work with all strategies via the
    // strategy interface.
    private strategy: Strategy

    // Usually the context accepts a strategy through the
    // constructor, and also provides a setter so that the
    // strategy can be switched at runtime.
    method setStrategy(Strategy strategy) is
        this.strategy = strategy

    // The context delegates some work to the strategy object
    // instead of implementing multiple versions of the
    // algorithm on its own.
    method executeStrategy(int a, int b) is
        return strategy.execute(a, b)


// The client code picks a concrete strategy and passes it to
// the context. The client should be aware of the differences
// between strategies in order to make the right choice.
class ExampleApplication is
    method main() is
        Create context object.

        Read first number.
        Read last number.
        Read the desired action from user input.

        if (action == addition) then
            context.setStrategy(new ConcreteStrategyAdd())

        if (action == subtraction) then
            context.setStrategy(new ConcreteStrategySubtract())

        if (action == multiplication) then
            context.setStrategy(new ConcreteStrategyMultiply())

        result = context.executeStrategy(First number, Second number)

        Print result.

06 适用场景

  • 当您想在对象中使用算法的不同变体并能够在运行时从一种算法切换到另一种算法时,请使用策略模式。

  • 策略模式允许您通过将对象与可以以不同方式执行特定子任务的不同子对象相关联来间接改变对象在运行时的行为。

  • 当您有许多相似的类,它们只是在执行某些行为的方式上有所不同时,请使用该策略。

  • 策略模式让您可以将不同的行为提取到单独的类层次结构中,并将原始类组合成一个类,从而减少重复代码。

  • 使用该模式将类的业务逻辑与在该逻辑的上下文中可能不那么重要的算法的实现细节隔离开来。

  • 策略模式允许您将各种算法的代码、内部数据和依赖项与其余代码隔离开来。各种客户端获得一个简单的接口来执行算法并在运行时切换它们。

  • 当您的类有一个在同一算法的不同变体之间切换的大量条件运算符时,请使用该模式。

  • 策略模式允许您通过将所有算法提取到单独的类中来消除这种条件,所有这些都实现相同的接口。原始对象将执行委托给这些对象之一,而不是实现算法的所有变体。

07 如何实施

  1. 在上下文类中,确定一种易于频繁更改的算法。它也可能是在运行时选择并执行同一算法的变体的大量条件。

  2. 声明算法所有变体通用的策略接口。

  3. 一个一个地,将所有算法提取到自己的类中。他们都应该实现策略接口。

  4. 在上下文类中,添加一个用于存储对策略对象的引用的字段。提供用于替换该字段值的设置器。上下文应仅通过策略接口与策略对象一起使用。上下文可以定义让策略访问其数据的接口。

  5. 上下文的客户必须将其与合适的策略相关联,该策略与他们期望上下文执行其主要工作的方式相匹配。

08 优缺点

欢迎查看公众号内容:
https://mp.weixin.qq.com/s?__biz=Mzk0NjI5NzE1Ng==&mid=2247483723&idx=1&sn=505edd63f1bde0bc86b34d3c683848da&chksm=c3090160f47e887685c5034066198feddcaaa136dfaf1b8e31d1dca502fb7a5bd033b0bf8d73&token=787431505&lang=zh_CN#rd

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容