公号:码农充电站pro
主页:https://codeshellme.github.iohtml
本篇来介绍策略模式(Strategy Design Pattern)。java
假设咱们要为动物进行建模,好比狗,猪,兔子等,每种动物的能力是不一样的。git
首先你可能想到用继承的方式来实现,因此咱们编写了下面这个 Animal
类:github
abstract class Animal { public void run() { System.out.println("I can run."); } public void drinkWater() { System.out.println("I can drink water."); } protected abstract String type(); }
Animal
是一个抽象类,其中包括了动物的能力,每种能力用一个方法表示:算法
而后咱们编写 Dog
,Pig
和 Rabbit
:shell
class Dog extends Animal { public String type() { return "Dog"; } } class Pig extends Animal { public String type() { return "Pig"; } } class Rabbit extends Animal { public String type() { return "Rabbit"; } }
上面的三种动物都继承了 Animal
中的 run 和 drinkWater,而且都实现了本身的 type 方法。设计模式
如今咱们想给 Pig
和 Rabbit
加入吃草
的能力,最直接的办法是分别在这两个类中加入 eatGrass 方法,以下:函数
class Pig extends Animal { public void eatGrass() { System.out.println("I can eat grass."); } public String type() { return "Pig"; } } class Rabbit extends Animal { public void eatGrass() { System.out.println("I can eat grass."); } public String type() { return "Rabbit"; } }
上面代码可以达到目的,可是不够好,由于Pig
和 Rabbit
中的 eatGrass 如出一辙,是重复代码,代码没能复用。测试
为了解决代码复用,咱们能够将 eatGrass 方法放到 Animal
中,利用继承的特性,Pig
和 Rabbit
中就不须要编写 eatGrass 方法,而直接从 Animal
中继承就行。this
可是,这样仍是有问题,由于若是将 eatGrass 放在 Animal
中,Dog
中也会有 eatGrass ,而咱们并不想让 Dog
拥有吃草
的能力。
也许你会说,咱们能够在 Dog
中将 eatGrass 覆盖重写,让 eatGrass 不具备实际的能力,就像这样:
class Dog extends Animal { public void eatGrass() { // 什么都不写,就没有了吃草的能力 } public String type() { return "Rabbit"; } }
这样作虽然能到达目的,可是并不优雅。若是 Animal
的子类特别多的话,就会有不少子类都得这样覆盖 eatGrass 方法。
因此,将 eatGrass 放在 Animal
中也不是一个好的方案。
那是否能够将 eatGrass 方法提取出来,做为一个接口?
就像这样:
interface EatGrassable { void eatGrass(); }
而后,让须要有吃草
能力的动物都去实现该接口,就像这样:
class Rabbit extends Animal implements EatGrassable { public void eatGrass() { System.out.println("I can eat grass."); } public String type() { return "Rabbit"; } }
这样作能够达到目的,可是,缺点是每一个须要吃草
能力的动物之间就会有重复的代码,依然没有达到代码复用的目的。
因此,这种方式仍是不能很好的解决问题。
咱们能够将吃草
的能力看做一种“行为”,而后使用“行为类”来实现。那么须要有吃草
能力的动物,就将吃草类
的对象,做为本身的属性。
这些行为类就像一个个的组件,哪些类须要某种功能的组件,就直接拿来用。
下面咱们编写“吃草类”:
interface EatGrassable { void eatGrass(); } class EatGreenGrass implements EatGrassable { // 吃绿草 public void eatGrass() { System.out.println("I can eat green grass."); } } class EatDogtailGrass implements EatGrassable { // 吃狗尾草 public void eatGrass() { System.out.println("I can eat dogtail grass."); } } class EatNoGrass implements EatGrassable { // 不是真的吃草 public void eatGrass() { System.out.println("I can not eat grass."); } }
首先建立了一个 EatGrassable
接口,可是不用动物类来实现该接口,而是咱们建立了一些行为类 EatGreenGrass
,EatDogtailGrass
和 EatNoGrass
,这些行为类实现了 EatGrassable
接口。
这样,须要吃草的动物,不但可以吃草,并且能够吃不一样种类的草。
那么,该如何使用 EatGrassable
接口呢?须要将 EatGrassable
做为 Animal
的属性,以下:
abstract class Animal { // EatGrassable 对象做为 Animal 的属性 protected EatGrassable eg; public Animal() { eg = null; } public void run() { System.out.println("I can run."); } public void drinkWater() { System.out.println("I can drink water."); } public void eatGrass() { if (eg != null) { eg.eatGrass(); } } protected abstract String type(); }
能够看到,Animal
中增长了 eg
属性和 eatGrass
方法。
其它动物类在构造函数中,要初始化 eg
属性:
class Dog extends Animal { public Dog() { // Dog 不能吃草 eg = new EatNoGrass(); } public String type() { return "Dog"; } } class Pig extends Animal { public Pig() { eg = new EatGreenGrass(); } public String type() { return "Pig"; } } class Rabbit extends Animal { public Rabbit() { eg = new EatDogtailGrass(); } public String type() { return "Rabbit"; } }
对代码测试:
public class Strategy { public static void main(String[] args) { Animal dog = new Dog(); Animal pig = new Pig(); Animal rabbit = new Rabbit(); dog.eatGrass(); // I can not eat grass. pig.eatGrass(); // I can eat green grass. rabbit.eatGrass(); // I can eat dogtail grass. } }
实际上,上面的实现方式使用的就是策略模式。重点在于 EatGrassable
接口与三个行为类 EatGreenGrass
,EatDogtailGrass
和 EatNoGrass
。在策略模式中,这些行为类被称为算法族,所谓的“策略”,能够理解为“算法”,这些算法能够互相替换。
策略模式定义了一系列算法族,并封装在类中,它们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。
我将完整的代码放在了这里,供你们参考,类图以下:
在一开始的设计中,咱们使用的是继承(Is-a) 的方式,可是效果并非很好。
最终的方案使用了策略模式,它是一种组合(Has-a) 关系,即 Animal
与 EatGrassable
之间的关系。
这也是一种设计原则:多用组合,少用继承,组合关系比继承关系有更好的弹性。
策略模式不只重在建立一组算法(行为类),可以动态的让这些算法互相替换,也是策略模式典型应用。
所谓的“动态”是指,在程序的运行期间,根据配置,用户输入等方式,动态的设置算法。
只须要在 Animal
中加入 setter
方法便可,以下:
abstract class Animal { // 省略了其它代码 public void setEatGrassable(EatGrassable eg) { this.eg = eg; } }
使用 setter
方法:
Animal pig = new Pig(); pig.eatGrass(); // I can eat green grass. pig.setEatGrassable(new EatDogtailGrass()); // 设置新的算法 pig.eatGrass(); // I can eat dogtail grass.
原本 pig
吃的是绿草
,咱们经过 setter
方法将 绿草
换成了 狗尾草
,能够看到,算法的切换很是方便。
策略模式定义了一系列算法族,这些算法族也能够叫做行为类。策略模式使用了组合而非继承来构建类之间的关系,组合关系比继承关系更加有弹性,使用组合也比较容易动态的改变类的行为。
(本节完。)
推荐阅读:
欢迎关注做者公众号,获取更多技术干货。