篇来介绍策略模式(Strategy Design Pattern)。
假如咱们要为动物进行建模,比方狗,猪,兔子等,每种动物的能力是不同的。
1,应用继承
首先你可能想到用继承的形式来实现,所以咱们编写了上面这个 Animal
类:
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
是一个抽象类,其中包含了动物的能力,每种能力用一个办法示意:
- _run_:奔跑能力。
- _drinkWater_:喝水能力。
- _type_:返回动物的品种,比方“狗”,“兔子”。这是一个形象办法,子类要去实现。
而后咱们编写 Dog
,Pig
和 Rabbit
:
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
中继承就行。
然而,这样还是有问题,因为如果将 eatGrass 放在 Animal
中,Dog
中也会有 eatGrass,而咱们并不想让 Dog
领有 吃草
的能力。
兴许你会说,咱们能够在 Dog
中将 eatGrass 笼罩重写,让 eatGrass 不具备理论的能力,就像这样:
class Dog extends Animal {public void eatGrass() {// 什么都不写,就没有了吃草的能力}
public String type() {return "Rabbit";}
}
这样做尽管能达到目标,然而并不优雅。如果 Animal
的子类特地多的话,就会有很多子类都得这样笼罩 eatGrass 办法。
所以,将 eatGrass 放在 Animal
中也不是一个好的计划。
2,应用接口
那是否能够将 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";}
}
这样做能够达到目标,然而,毛病是每个须要 吃草
能力的动物之间就会有反复的代码,仍然没有达到代码复用的目标。
所以,这种形式还是不能很好的解决问题。
3,应用行为类
咱们能够将 吃草
的能力看作一种“行为”,而后应用“行为类”来实现。那么须要有 吃草
能力的动物,就将 吃草类
的对象,作为本人的属性。
这些行为类就像一个个的组件,哪些类须要某种性能的组件,就间接拿来用。
上面咱们编写“吃草类”:
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.}
}
4,策略模式
实际上,下面的实现形式应用的就是 策略模式 。重点在于 EatGrassable
接口与三个行为类 EatGreenGrass
,EatDogtailGrass
和 EatNoGrass
。在策略模式中,这些行为类被称为 算法族,所谓的“策略”,能够了解为“算法”,这些算法能够相互替换。
策略模式定义了一系列算法族,并封装在类中,它们之间能够相互替换,此模式让算法的变动独立于应用算法的客户。
我将残缺的代码放在了这里,供大家参考,类图如下:
5,继承与组合
在一开始的设计中,咱们应用的是 继承(Is-a) 的形式,然而成果并不是很好。
最终的计划应用了策略模式,它是一种 组合(Has-a) 关系,即 Animal
与 EatGrassable
之间的关系。
这也是一种设计准则:多用组合,少用继承,组合关系比继承关系有更好的弹性。
6,动静设定行为
策略模式不仅重在创立一组算法(行为类),可能动静的让这些算法相互替换,也是策略模式典型利用。
所谓的“动静”是指,在程序的运行期间,依据配置,用户输出等形式,动静的设置算法。
只须要在 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
办法将 绿草
换成了 狗尾草
,能够看到,算法的切换十分不便。
7,总结
策略模式定义了一系列 算法族 ,这些算法组也能够叫作 行为类 。策略模式应用了 组合而非继承 来构建类之间的关系,组合关系比继承关系更加有弹性,应用组合也比拟容易动静的扭转类的行为。