引言
要分析设计模式中的“策略模式”,我们可以以《希伯来圣经》中的一个故事为例。
《希伯来圣经》中有这样一个故事,说的是人类产生不同语言的起源。在这个故事中,一群只说一种语言的人在“大洪水”之后从东方来到了示拿地区,并决定在这修建一座城市和一座“能够通天的”高塔;上帝见此情形就把他们的语言打乱,让他们再也不能明白对方的意思,并把他们分散到了世界各地。
这里我们可以将God视作一个程序员。
在他的设计中,所有的人类(Human),虽然有很多个个体,但一开始都是一样的,说的是一样的语言。
所以我们可以将Human类作为父类,然后所有的人类个体对象都继承这个Human父类。父类中有一个speak()
方法,所有继承Human类的子类都将继承这个方法,于是都拥有了用语言交流的能力。
//Human父类
public class Human {
public void speak(){
System.out.println("只要是人类的子类都speak the same language");
}
}
可以有很多的人类种类继承这个Human父类,比如来自东方的的人:
public class PeopleFromEast extends {
//PeopleFromEast继承了Human,
//所以也就继承了Human父类中的speak()方法
//所以PeopleFromEast类中甚至都不需要写任何方法
}
在demo测试中,我们可以看到,PeopleFromEast类的对象,可以调用从父类继承来的speak()
方法:
public class RunDemo {
public static void main (String[] args){
//创建一个PeopleFromEast对象
PeopleFromEast peopleFromEast = new PeopleFromEast();
//让这个对象调用从父类继承来的方法speak()
peopleFromEast.speak();
}
}
根据God最初的设想,这样的设计是没有问题的。
“土方法”的缺陷
可是后来由于语言一致,人类同心协力修建巴比塔,(程序猿)God感到受到了威胁。于是决定做出改变:让来自不同地方的人speak不同的language——比如PeopleFromEast说EastLanguage,PeopleFromWest说WestLanguage,等等。
God要实现这样的改变,最直观的方法自然是让每个继承Human父类的人类子类重写父类的speak()
方法。
//新的PeopleFromEast类
//这一次PeopleFromEast重写了speak()方法
public class PeopleFromEast extends Human {
@Override
public void speak() {
System.out.println("PeopleFromEast从今以后只能speak EastLanguage");
}
}
demo的运行结果:
如果人类的子类种类不多,那么(程序猿God)还可以每次都重写Human子类的speak()
方法。可是如果人类的子类有很多种呢?又或者不止speak()
方法有这种情况呢?比如,也许God以后希望让每种人类子类的行走方式都不一样。那是不是应该在Human父类中加入一个walk()
方法,然后让每个子类都重写呢?
也就是说,如果人类子类的“规格”常常改变,每当有新的人类子类出现,(程序猿)God都要被迫检查并可能需要覆盖speak()
和walk()
,这绝对是God所不愿意看见的。
策略模式原理及益处
这个策略依然有一个Human父类(相当于一个模板),所有的人类子类都继承这个父类(根据这个模板来创建)。
所不同的是,这次God决定在每个人类的子类中都附上自己的一份意识。让这份意识来控制这个子类应该说的语言种类。为了方便叙述,我们可以将God的这一意识称为LanguageController
。
然后,God为人类的子类可以说的Language种类提供了一个选择范围。
比如,中国人(ChineseMan--因为Chinese既可以表示“中文”又可以表示“中国人”,为了区分,我们称“中国人”为"ChineseMan")说中文(Chinese),美国人(American)就说英文(English)。
这样一来,在每个人类子类创建时,God附在其上的意识LanguageController
将发挥作用,然后按照God的意愿,从language的可选范围中选择一种语言来指定给该人类子类。
这一策略在程序设计中可以这样实现:
创建一个Human父类:
public class Human {
//LanguageController是接口类型
//languageController作为Human类的field,
//代表God将自己的意识附在人类父类之上,
//也就代表God的意识将附在所有的人类个体之上
LanguageController languageController ;
//根据God的设计,人类的模板中只需要作出规定:人会说某种语言
//而不需要作出具体的安排
public void performSpeaking (){
//人说话的时候,还得上帝的意识起作用,
//决定这个“具体的人”该说何种语言
languageController.speak();
}
}
创建LanguageController接口:
//创造God的意识,也就是LanguageController:
public interface LanguageController {
//接口中只定义了一个speak()方法
public void speak();
}
现在God要创造中国人和美国人:
ChineseMan类:
public class ChineseMan extends Human {
//中国人对象的构造器
ChineseMan() {
//构造ChineseMan对象时,God的意识发挥作用
//languageController是从Human父类继承来的
//根据God的安排,要创建ChineseMan对象,就应该告诉languageController现在要创造的这个对象说中文
languageController = new speakChinese();
//在这里,由于多态的缘故,表现为具体子类--也就是speakChinese对象
skinColorController = new ColorIsLikeAsian();
}
}
American类:
public class American extends Human {
//American的构造器
public American (){
languageController = new SpeakEnglish();
}
}
那么这个speakChinese类
和speakEnglish类
有什么玄机呢?
speakChinese类:
//speakChinese类实现了LanguageController接口
public class speakChinese implements LanguageController{
//实现LanguageController接口中规定的speak()方法
@Override
public void speak() {
System.out.println("我说中文!");
}
}
speakChinese类可能不太好理解,因为speak Chinese(说中文)更多算是一种行为(action),这里却定义为类。其实只需要换一种思维,把speakChinese类视作一种标签,把这个标签赋给God的意识(languageContronller),也就实现了根据不同的human子类来确定不同的语言。
speakEnglish类:
//SpeakEnglish类实现了LanguageController接口
public class SpeakEnglish implements LanguageController {
@Override
public void speak() {
System.out.println("I speak English!");
}
}
下面运行一个demo--StrategyModeDemo.java
:
public class StrategyModeDemo {
public static void main(String[] args){
//新建一个ChineseMan对象
Human chineseMan = new ChineseMan();
//ChineseMan说的语言:
chineseMan.performSpeaking();
//American对象:
Human american = new American();
//American说的语言:
american.performSpeaking();
}
}
StrategyModeDemo.java运行结果:
执行逻辑图示
而整个执行流程的逻辑则如图所示:
更上一层楼
当然,以上所有的Human子类在被创建时,他们说什么话就已经注定了。
这是因为他们的God的意识起作用的环节在他们的构造器中。
那么God有没有办法让已经被造出来的ChineseMan对象改说English呢?
答案是肯定的!
我们只需要在Human.java
中加入一个修改语言的方法即可:
public class Human {
LanguageController languageController ;
SkinColorController skinColorController;
//控制Human的子类说的语言,比如中国人说中文
public void performSpeaking (){
languageController.speak();
}
//随时改变能说的语言
public void setLanguage (LanguageController languageController){
languageController.speak();
}
}
再次运行StrategyModeDemo.java
:
public class StrategyModeDemo {
public static void main(String[] args){
//新建一个ChineseMan对象
Human chineseMan = new ChineseMan();
//ChineseMan说的语言:
chineseMan.performSpeaking();
System.out.println("God决定让ChineseMan也说英文");
chineseMan.setLanguage(new SpeakEnglish());
}
}
个人浅见,如有不足,欢迎批评指正:)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。