Design Principle:
Identify the aspects of your application that vary and separate them from what stays the same - 将频繁改变的部分从系统中抽离出来
Program to an interface, not an implementation - 面向接口编程而非实现
Favor composition over inheritance - 组合优于继承
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
完成共一个任务往往有多种不同的方法,每一种方法我们称之为策略,我们可以通过不同的条件选择不同的算法完成任务
UML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +-----------------+ | Context | |---------------- | +-------------------+ | - strategy |<>------------ | <<Interface>> | |---------------- | | Strategy | | + setStrategy(s)| |------------------ | | + perfStratecy()| | algorithm() | | | +-------------------+ | | ^ | | |---------------------------------- +-----------------+ | | | | | | +-------------------+ +-------------------+ | |ConcreateStrategyA | |ConcreateStrategyB | | |------------------ | |------------------ | ... | algorithm() | | algorithm() | +-------------------+ +-------------------+
案例 已有一段鸭子模拟器代码,所有鸭子都有一个基类,基类中定义了一些常用方法,并给了实现。只有 display() 是需要每个类定制的,声明成了 abstract 并在子类中各自实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 +--------------------+ | Duck | |--------------------| | swim() | | display() | | quack() | | //other methods | +--------------------+ ^ | |--------------- | | | | +----------------+ +----------------+ | MallardDuck | | RedHeadDuck | |--------------- | |--------------- | |display() | |display() | | | | | | | | | +----------------+ +----------------+
某个 release,客户突然想要看到鸭子能飞。。。小码农一拍脑袋:嗨,这还不简单。就直接在基类里添加了 fly() 的实现。然后转头就去写其他功能了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 +--------------------+ | Duck | |--------------------| | swim() | | display() | | quack() | | fly() | | //other methods | +--------------------+ ^ | |--------------- | | | | +----------------+ +----------------+ | MallardDuck | | RedHeadDuck | |--------------- | |--------------- | |display() | |display() | | | | | | | | | +----------------+ +----------------+
三天后,Sales 一个夺命连环 call 打到小码农这儿,说自己给客户演示功能的时候,突然发现,程序里的橡皮鸭子起飞了!!!这还了得,小码农打开了 IDE 看了橡皮鸭子的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 +--------------------+ | Duck | |--------------------| | swim() | | display() | | quack() | | fly() | | //other methods | +--------------------+ ^ | |---------------------------------------- | | | | | | +----------------+ +----------------+ +----------------+ | MallardDuck | | RedHeadDuck | | RubberDuck | |--------------- | |--------------- | |--------------- | |display() | |display() | |display() | | | | | |quack(){ | | | | | |override quack | +----------------+ +----------------+ |} | | | +----------------+
好嘛,原来基类加完 fly() 方法之后忘了重写橡皮鸭子这个子类了,还能咋整,要不重写一下 fly() 呗
1 2 3 4 5 6 7 8 9 +-------------------------+ | RubberDuck | | ---------------------- | | | | display() | | quack(){// rubber quck }| | fly(){ // do nothing } | | | +-------------------------+
不过转念一下,这也不是办法啊,不然哪天要新加一个什么木头鸭子,不是还得和橡皮鸭子一样再来一遍?于是小码农就寻思着,把 quack 和 fly 整成接口?
把这个想法和同事小美一说,小美立马就不乐意了,这个产品有几十个类,把这两个玩意儿整成接口,你不得在这几十个类里面都改一遍,你改啊?!
小码农想也是,但是没什么法子,就去问自己的师傅大能耐了。小码农把情况和大能耐说了一遍。
‘耐哥,就上面的情况,听说你精通设计模式,这种情况,有法儿吗’
‘咱先不说设计模式的事儿,写代码最起码得掌握一个原则:先把频繁变动的代码和不变的拆分开了后我们再说事儿’
按照耐哥的思路,小码农把 quack 和 fly 从鸭子类里面单独拿出来写成接口,并为他们做了各种实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +-------------------------+ | <<Interface>> | | FlyBehavior | | ---------------------- | | fly() | | | +-------------------------+ ^ |--------------------- | | | | +-------------------------+ +-------------------------+ | FlyWithWings | | FlyNoWay | | ---------------------- | | ---------------------- | | fly(){ // duck flying } | | fly(){ // can't fly } | | | | | +-------------------------+ +-------------------------+
既然将这两个属性从基类中抽出来了,那原来的基类中我们再用这两个新的接口代替,并新建方法调用接口实现 fly 和 quack 的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 +--------------------+ | Duck | |--------------------| | FlyBehavior fb | | QuackBehavior qb | | | |--------------------| | swim() | | display() | | perormQuack() <----------- qb.quack(); | performFly() <----------- fb.fly(); | //other methods | | | +--------------------+
按照这个逻辑,小码农重构了以后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public interface FlyBehavior { void fly () ; } public class FlyNoWay implements FlyBehavior { public void fly () { System.out.println("I can't fly..." ); } } public class FlyWithWings implements FlyBehavior { public void fly () { System.out.println("I can fly..." ); } } public interface QuackBehavior { void quack () ; } public class Quack implements QuackBehavior { public void quack () { System.out.println("Quack...Quack..." ); } } public class Squack implements QuackBehavior { public void quack () { System.out.println("Squack..." ); } } public class MuteQuack implements QuackBehavior { public void quack () { System.out.println("<< Silence >>" ); } } public abstract class Duck { QuackBehavior quackBehavior; FlyBehavior flyBehavior; public void performQuack () { quackBehavior.quack(); } public void performFly () { flyBehavior.fly(); } public void swim () { System.out.println("All ducks float, even decoys!" ); } public abstract void display () ; } public class MallardDuck extends Duck { public MallardDuck () { this .flyBehavior = new FlyWithWings(); this .quackBehavior = new Quack(); } @Override public void display () { System.out.println("I'm a Mallard duck..." ); } } public class MiniDuckSimulator { public static void main (String[] args) { MallardDuck mallardDuck = new MallardDuck(); mallardDuck.performQuack(); mallardDuck.performFly(); } }
大功告成,小码农拿着自己的成果找了大能耐 review 代码
‘不错不错,不过你有没有想过你的代码还可以更灵活,只要在 Duck 里面添加一个 set 方法,你就可以动态的改变鸭子的行为了哟’
小码农一想,立马知道了其中的关键,修改了代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +--------------------+ | Duck | |--------------------| | FlyBehavior fb | | QuackBehavior qb | | | |--------------------| | swim() | | display() | | perormQuack() <----------- qb.quack(); | performFly() <----------- fb.fly(); | //other methods | | setFlyBehavior() | | setQuackBehavior() | | | +--------------------+
1 2 3 4 5 6 7 public abstract class Duck { QuackBehavior quackBehavior; public void setQuackBehavior (QuackBehavior quackBehavior) { this .quackBehavior = quackBehavior; } public void setFlyBehavior (FlyBehavior flyBehavior) { this .flyBehavior = flyBehavior; } }
正巧项目组想要一个新的鸭子模型,能够动态的改变飞行模式,在氪金以前是不能飞的,但是氪金以后能弹射起步。。。正好赶巧了,小码农三下五除二就实现了功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class RocketFly implements FlyBehavior { public void fly () { System.out.println("Fly in rocket speed..." ); } } public class MiniDuckSimulator { public static void main (String[] args) { ModelDuck modelDuck = new ModelDuck(); modelDuck.performFly(); modelDuck.setFlyBehavior(new RocketFly()); modelDuck.performFly(); } }
小码农哼着小曲儿锁了屏,转头跑到走廊上掏出手机开启了王者农药。。。