一周一种设计模式--策略模式

1,382 阅读3分钟

本文的例子来源于HeadFirst,这是一本很好的设计模式的书,推荐大家阅读。

一、策略模式的定义

  策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

二、案例说明

  比如有一个鸭子的游戏,应用中有许多种鸭子(黄鸭子、绿鸭子),玩家(Client)可以操作游戏中的任何一个鸭子。因此我们可以定义一个鸭子的超类(Duck),所有的鸭子继承这个超类,玩家只需要引用Duck这个超类即可操作所有的鸭子,这似乎是个不错的设计,其类图如下:   通过这样的设计,Client可以适配所有Duck的子类,并且想往游戏中新增一个蓝色的鸭子易如反掌,Duck已经实现了游泳和呱呱叫的方法,我们的蓝色鸭子只需要继承Duck并实现蓝色的外观(dispaly抽象方法)。这是不是完美的设计呢?
  当然不是最完美的设计,假如玩家希望往游戏增加一只会飞的鸭子来增加游戏体验感,并且Client的代码任然只会持有Duck一种引用。于是我们就往Duck中增加一个fly()方法让鸭子具备了飞行的能力,继承看似完美的解决了这些问题。我们只想加入一种会飞的鸭子,但是这么一来所有的鸭子都会飞了,想解决这个问题必须在所有不会飞的鸭子中重写fly()方法,这简直太糟糕了!这仅仅是增加一个需求,就让之前的代码都需要更新,如果需求改变多了,这样的一个设计肯定不能支撑下去。

我们使用策略模式重新设计下类图如下:

  所有的鸭子同样是继承Duck,但是我们把鸭子呱呱叫的行为和飞行的行为从鸭子中抽离出来了,并且将飞行和呱呱叫抽象为接口FlyBehavior和QuackBehavior。这时只需要让鸭子有一个飞行的行为和呱呱叫的行为。比如我们想要一个不会叫只会飞的红鸭子,只需要将MuteQuack、FlyWithWings设置到RedDuck中。这么设计有下面几个优点:
1. 这种组合的方式让代码更加的灵活。
2. 将变化的行为抽离出来,如果行为发生了改变只需要修改这个行为即可,不会影响其他代码。
3. 鸭子只是依赖飞行和呱呱叫的接口,这样任何一个行为的实现都可以适用于鸭子。

这刚好对应了下面这三点设计原则:
  1. 少用继承,多用组合。
  2. 不要把可能发生变化的代码和不需要发生变化的代码混在一起。
  3. 针对接口编程,而不是针对实现编程。

三、从鸭子到策略模式

策略模式定义了算法族(飞行和咕咕叫的行为接口),分别封装起来(根据行为接口实现各种各样的行为),让它们之间可以相互替换(让鸭子有一个飞行和咕咕叫的行为,这样就可以替换任何一种行为实例),此模式让算法的变化独立于使用算法的客户。我们将鸭子去掉把类图抽象一下就是策略模式的类图了: Strategy就是变化的算法簇,这里可以称为为策略,StrategyA、StrategyB就是可以相互替换的算法实现。Context就是拥有这个策略的上下文,策略可以是任何Strategy的实现。