上一篇,我说我觉得《三十六计》就是一种设计模式,那么在这一篇【策略模式】中,我想我就索性也以行军打仗作为例子吧。
行军作战是军队的事,不管是哪个国家的军队,他们都有一个共同的名字:军队。
用java开发的概念来说,军队就应该是一个超级父类,然后具有军队的特征,比如名字这个属性,发起战争、结束战争、行军布阵等行为。
package warTest;
/**
* 军队的超级父类
* @author tuzongxun
*/
public class Army {
/*
* 军队名称
*/
protected String armyName;
/**
* 开始战争 @author:tuzongxun
*/
public void armyStart() {
System.out.println(armyName + "开始战争");
}
/**
* 结束战争 @author:tuzongxun
*/
public void armyStop() {
System.out.println(armyName + "结束战争");
}
/**
* 行军布阵 @author:tuzongxun
*/
public void march() {
System.out.println(armyName + "使用【一字长蛇阵】");
}
}
超级父类只是一个泛指,是什么军队、具体拥有哪些行为,这就需要具体的军队去做,比如秦军、楚军等,而这里的秦军、楚军便是军队的子类,拥有军队应该有的一些特征,那么初始的想法自然就是继承。
/**
*秦军
*/
public class ArmyQin extends Army {
public ArmyQin(String armyName) {
this.armyName = armyName;
}
}
/**
* 楚军
*/
public class ArmyChu extends Army {
public ArmyChu(String armyName) {
this.armyName = armyName;
}
}
有人的地方就有江湖,有军队的地方就有战争,既然有了两支具体的军队,那么摩擦在所难免,总有人会抑制不住一统天下的欲望。
于是,下战帖,擂战鼓,行军布阵,攻城拔寨。正如我们的测试类一样:
public class ArmyTest {
public static void main(String[] args) {
Army army1 = new ArmyChu("楚军");
Army army2 = new ArmyQin("秦军");
army1.armyStart();
army1.march();
army2.armyStart();
army2.march();
}
}
然后战争的结果便如我们测试运行的结果,看起来不分胜负:
战争开始进入了僵持局面,两军将士心中也无比的疑惑,为什么我用的是【一字长蛇阵】,他也用的是【一字长蛇阵】?不行,我要用【八门金锁阵】!
说起来容易,做起来难,要怎么改呢?在我们的代码中我立即便想到了这样几种实现方式:
第一种:
在父类的行军布阵方法里进行判断
public void march() {
if ("秦军".equals(armyName)) {
System.out.println(armyName + "使用【一字长蛇阵】");
} else {
System.out.println(armyName + "使用【八门金锁阵】");
}
}
好吧,这样一来,似乎两军不用僵持了,然而,两军将士却反而更加的迷惑了,我们用什么阵法难道不该是自己控制的吗?为什么是父类控制的?这就如同我继承了祖先的一些特征,祖先现在却控制我的行为一样,这根本就不合常理!
而且,现在是两支军队,如果再来一个魏军,再来一个齐军呢,是否要在父类不断地增加if和else?
设计来源于生活,便需要符合基本的规律和逻辑,因此这种方式虽然看似可行,却似乎有些反人类。
正如父类的行为和特征是不可变的、那已经是历史因素一样,我们不能也不该要求父类改变。也正如我们长大以后都不会再完全听从父母的安排一样,子类的行为也不该完全受父类的控制。
父类的特征可以存在,但是可以基因变异,父类的方法可以存在,但是可以覆盖。
那么,就有了第二种实现方式:用子类重写的方式改变父类的方法
@Override
public void march() {
System.out.println(armyName +"使用【八门金锁阵】");
}
@Override
public void march() {
System.out.println(armyName +"使用【一字长蛇阵】");
}
如此一来,第一种方式的问题我已经彻底解决了,我们做自己的事,不需要再打扰到父类了。
两军将士也是乐呵呵的,使用什么阵法的控制权也终于回到了自己的手中,我的阵法我做主,这酸爽。。。
可是,问题又来了,这两支军队是爽了,可是新来的第三支军队,魏军,心里就很不爽了。
为什么呢?因为他们根本就不懂阵法,他们只知道冲啊杀啊。可是他们也继承了军队这个父类,使得他们也必须要布阵,怎么办?
那他们只能布阵啊,只不过布了一通之后什么也没有布出来,然后就这样白白的耽误了时间和功夫。就好比下边这段代码一样,也重写了march方法,然后里边一片空白。
@Override
public void march() {
}
虽然什么效果都没有,但是却不得不做点什么,魏军不爽,后来的所有不会布阵的军队同样的不爽,于是将士之间便慢慢的怨声载道:为什么我们明明不会布阵,却还要在打仗的时候做这个动作呢?
上苍有耳,这种怨声载道很快就传到了他的耳中,于是他决定消除这种声音!一声“急急如意令”,时空倒转,军队又回到了最初只有军队概念的时候,没有子类,只有父类。
既然在行军布阵上出现了争议和问题,那么就需要改动。怎么改呢?很显然上边我们看到秦军代表了使用一字长蛇阵这一类的军队,楚军代表使用了八门金锁阵这一类的军队,还有魏军,代表了不会布阵的军队。
那么两种阵法便是两个行军布阵的子类,很容易就会想到他们会有一个行军布阵的父类。
而在之前的继承中实际上我们还发现了一个问题,那就是之前父类的march方法中的内容实际上是没有用的,因为子类一继承就覆盖了,这样的话父类的具体行为实际上是很多余的。
那么正如这里的两个行军布阵的子类会拥有一个行军布阵的父类一样,他们的父类实际上也做不了什么,具体的行军布阵都在子类实现,因此这个父类便不需要具体的实现,只需要抽象的行为就足够了,因此这里需要的不再是类,而是一个接口。
/**
* 行军布阵超级父类的接口
* @author tuzongxun
*/
public interface March {
public void march();
}
然后我们有两种实现:
public class MarchYiZi implements March {
public void march() {
System.out.println("使用【一字长蛇阵】");
}
}
public class MarchBaMen implements March {
public void march() {
System.out.println("使用【八门金锁阵】");
}
}
这样一来,我们便可以把行军布阵变成军队的一种属性,至于具体的属性内容就可以交由具体的子类去解决,军队父类增加行军布阵的属性,同时变更行军布阵的行为方式:
/**
* 布阵
*/
protected March march;
/**
* 行军布阵 @author:tuzongxun
*/
public void march() {
if (march != null) {
march.march();
}
}
军队父类只知道能够行军布阵,至于到底谁来实施,那就具体的子类自己控制,如果子类不知道行军布阵,那么就不用管它,march为null,什么都不用做了,也解决了魏军那一类子类的怨声载道。
而子类也不需要再一个个的覆盖父类的方法,只需要初始化的时候选择合适的阵法即可,同时如果战场需要再加入其他阵法,如【天地三才阵】、【七星北斗阵】,军队父类也不需要有什么改动,只需要在增加一个march的子类就可以轻松解决了。
public class ArmyChu extends Army {
public ArmyChu(String armyName) {
this.armyName = armyName;
this.march = new MarchBaMen();
}
}
public class ArmyQin extends Army {
public ArmyQin(String armyName) {
this.armyName = armyName;
this.march = new MarchYiZi();
}
}
public class ArmyWei extends Army {
public ArmyWei(String armyName) {
this.armyName = armyName;
}
}
上边便是我学习策略模式过程中自己写的一个例子,不知道看的人懂了没有,我自己应该勉强算是懂了。
这是一个业务的实现和优化的过程,最终的结构便是使用了所谓的【策略模式】,策略模式是什么,先看一下比较权威的定义:
《headfirst设计模式》:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
刘伟:策略模式定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
那么定义里边涉及了几个名词:算法族或算法类、封装、互相替换、算法独立于使用算法的客户。
在我的例子中,最终提取出来的行军布阵抽象父类以及两个行军布阵的子类,便是所谓的算法族和算法类。如果结合《headfirst设计模式》,那就是抽出来的鸭子的飞行和叫声;结合刘伟老师的例子,就是不同的打折对象的打折方式。
至于封装,那就是每一种方式归拢在一起,形成独立自有的类,而替换,很显然,每个军队都可以随意调用任何一种行军布阵的阵法,只需要在开始作战前,也就是初始化的时候进行不同的初始化选择。
至于算法独立于使用的客户,这里的客户就是具体的军队,很显然,如果要改动阵法只需要改动具体阵法类,而不需要改动具体军队类,他们也是相对独立的。
设计模式可以改善代码的结构,让后期开发和维护更容易,但是万物都是过犹不及。
我的例子也只不过就是为了学习和理解而进行的生搬硬套,但是当我套成功以后,我发现确实让自己大大的增加了对《策略模式》的理解。
所以我想,学习这个模式的同学,在看别人例子的同时,不妨也这样做一下,想一个自己的例子看能否成功套用,如果能,我相信也一定能让自己对这个模式有一个更好的理解!
熟能生巧,套得多了,用的多了,自然就能得心应手,再进一步的理解。