假如现在有一个需求:针对来自不同国家的人,分别展示他们擅长的竞技项目以及一些相同的特点。
先定义一个Person.java
class Person {
sex() {男 / 女};
smile() {// 爱笑}
speak() {// 说话}
sport() {// 擅长的运动 比如足球}
}
人有共同的特点,比如都爱笑,性别分为男人和女人,所以从OO的角度,你首先可能会想到继承,这样代码也可以复用。
现在产品经理说,我现在要看看德国人,于是你创建了一个German类,它继承了Person.
展示效果:德国人有男人和女人,爱笑,说话,足球是他们的特长运动。
产品经理看后点了点头,他又说再给我看看我们中国人的,于是程序猿A又信誓旦旦创建了Chinese.java,同样继承了Person。
展示效果依然是爱笑、说话、足球是擅长的运动,产品经理这回表情有点严肃,说了一句:你这个回去先改改再来。
从上面的例子可以看出,代码虽然复用了,但是如果靠继承是有点不切实际的,怎么办呢。
这时候策略模式就可以帮助我们解决这个问题。先看定义
策略模式:定义了算法族,封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
读起来有点抽象,别担心,下面用具体的例子来展开说明。
还是上面的这个例子,我们知道Person里面有一些行为是共有的,有一些行为是不同的。那我们把Person中不同的行为抽离出来,但是这里还有个问题就是还是没有体现出不同的行为。所以要引入这类行为的接口,不同的实现体现出不同行为即可。
比如sport这个行为,定义一个接口叫SportAbility.java 再定义它的实现:GermanSport和ChineseSport
class GermanSport implements SportAbility() {
sport () {
// 足球
}
}
class ChineseSport implements SportAbility() {
sport () {
// 乒乓球
}
}
上面的Person类做如下改造:
class Person {
private SportAbility sportAbility;
sex() {男 / 女}
smile() {// 爱笑}
speak() {// 说话}
doSport() { sportAbility.sport()}
}
增加了接口SportAbility的引用,GermanSport和ChineseSport就是定义中提到的算法,它们组合就成了算法族。
算法族有了,现在还需要将这些算法new出来的角色,就是说的客户,我们定义两个类:German、Chinese
Chinese extends Person {
Chiness () {
sportAbility = new ChineseSport();
}
}
German extends Person {
German () {
sportAbility = new GermanSport();
}
}
分别创建了ChineseSport和GermanSport两个实例。
测试类Main.java
class Main {
public static void main(String[] arg) {
// 这里想看中国人Chinese
// 如果是德国人就换成 German
Person person = new Chinese();
person.sex();
person.speak();
person.smile();
person.doSport();
}
}
前3个方法行为输出都一致,只有doSport行为不一致。
策略模式UML:
从图中可以看到,右边是行为,不同类型有不同的行为展示,它们是一些接口和它的实现,也就是定义中的算法族,左边是客户,客户根据自己的需求组合算法族来实现自己的需求。
策略模式也体现了设计模式的一种设计原则:多用组合,少用继承。