标签:
污污公司开发了个应用叫没事玩个鸡,这是一款娱乐类应用,可以一键玩鸡,有不同品类的鸡给你玩,如三黄鸡、乌鸡、白鸡等。这个应用的内部设计是标准面向对象技术,设计了一个鸡超类(Superclass),让各种鸡继承此超类。我们来看一下代码:
</pre><pre name="code" class="java"> /** * 超类鸡 */ abstract class Chicken { /** * 打招呼 */ public void sayHi() { System.out.println("咯咯咯~"); } /** * 鸡的样子,每种品类的鸡样子都不一样,所以该方法是抽象的 * 由具体的鸡来实现自己在屏幕上显示的样子 */ public abstract void show(); } /** * 白鸡,继承Chicken类 */ class WhiteChicken extends Chicken { @Override public void show() { // 样子是白色的 } } /** * 乌鸡,继承Chicken类 */ class BlackChicken extends Chicken { @Override public void show() { // 样子是乌黑的 } } /** * 尖叫鸡,继承Chicken类 */ class ScreamChicken extends Chicken { /** * 尖叫鸡不会咯咯叫,所以重写sayHi()方法 */ @Override public void sayHi() { System.out.println("惨叫声~"); } @Override public void show() { // 样子是无毛的 } }
代码很简单,具体品类的鸡继承超类(父类)鸡,所有的鸡都会叫,所以由父类处理。不同品类的鸡样子不同,所以由具体品类的鸡来实现。
没事玩个鸡应用很快火了,出现了很多竞争对手了,这时产品经理想要改变一些玩法来抛开竞争对手,想出了让鸡可以跑动,这样用户可就以玩跑动中的鸡了,程序员一想,这简单嘛,只需要在父类中加一个run()
方法就可以了,这样所有品类的鸡都会跑了:
/** * 跑步 */
public void run() {
System.out.println("拼命跑");
}
改完后就交给测试人员去测试去了。测试人员一测试,天了撸~出了个明显的bug
啊,尖叫鸡也会跑!于是将bug
提交到了系统中。
程序员一看,这确实不应该让尖叫鸡也能跑,先修复再说,于是他这样改了尖叫鸡类:
/** * 尖叫鸡,继承Chicken类 */ class ScreamChicken extends Chicken { /** * 尖叫鸡不会咯咯叫,所以重写sayHi()方法 */ @Override public void sayHi() { System.out.println("惨叫声~"); } @Override public void run() { // 覆盖,什么也不做 } @Override public void show() { // 样子是无毛的 } } }
程序员重写了run()
方法,然后什么也不实现,这样修复了bug
。通过这个bug
程序员也体会到了一件事:当涉及“维护”时,为了”复用(reuse)”目的而使用继承,并不太完美。
没事玩个鸡应用更新后更火了,于是产品经理决定每个月更新一次产品(至于更新的方法,他们还没有想到)。
程序员接到产品经理的更新计划就想,以后万一又要增加一些品类的鸡会怎样?万一有的功能部分品类的鸡是不具备的呢?那不是又得改父类又得改子类,牵一发而动全身,看来需要一个更好的方式才行。于是他开始翻看编程指南,终于找到一个设计原则。
设计原则
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
换句话说,如果每次新的需求一来,都会使某方面的代码发生变化,那你就可以确定,这部分代码需要被抽出来,和其他稳定的代码有所区分,把变化的部分封装
起来,以便以后可以轻易的改动或扩展此部分,而不影响不需要变化的部分。
从哪里开始呢?我们知道Chicken
类的sayHi()
方法和run()
方法会随着鸡的不同而改变,比如尖叫鸡sayHi()
的方式就和别的鸡不一样,它也不会跑。根据上面说的设计原则
,需要将它们独立出来,为了把这两个行为从Chicken
类中分开,程序员将它们从Chicken
类中取出来,建立一组新类来代表每个行为。示意图如下:
现在已经把变化和不会变化的部分分开了,需要考虑如何设计鸡的行为类了。继续翻看编程指南,发现了另一个设计原则。
设计原则
针对接口编程,而不是针对实现编程。
现在程序员利用接口代表每个行为,比如,SayHiBehavior
和RunBehavior
,而行为的每个实现都将实现其中的一个接口。我们来看一下这两个接口的代码:
// SayHiBehavior接口:
interface SayHiBehavior { void sayHi(); } // RunBehavior接口: interface RunBehavior { void run(); }
接口定义好了,我们就可以弄一些具体实现了。
普通的叫声:
public class NormalSayHi implements SayHiBehavior{ @Override public void sayHi() { System.out.println("我是正常 的叫声"); } }
尖叫声:
public class ScreamSayHi implements SayHiBehavior{ @Override public void sayHi() { System.out.println("我是尖叫哈哈哈"); } }
正常跑的行为:
public class NormalRun implements RunBehavior{ @Override public void run() { System.out.println("我是正常的跑"); } }
public class FaseRun implements RunBehavior{ @Override public void run() { System.out.println("我是飞快的跑"); } }
然后就重构下Chilcken这个父类:
public abstract class Chicken { private SayHiBehavior behavior; private RunBehavior behavior2; public abstract void show();//显示他的类别 public void setSayHiBehavior(SayHiBehavior behavior) { this.behavior = behavior; } public void setRunBehavior(RunBehavior behavior) { this.behavior2 = behavior; } public void performSayHi(){//执行叫声 behavior.sayHi(); } public void performRun(){//执行跑 behavior2.run(); } }程序员将
Chicken
类的sayHi()
方法和run()
方法给注释掉了,增加了打招呼行为SayHiBehavior
和跑步行为RunBehavior
,通过setter
来设置。然后用performSayHi()
和performRun()
方法将具体的打招呼行为和跑步行为委托给SayHiBehavior
和RunBehavior
去做。
public class Strategy { public static void main(String[] args){ WhiteChicken chicken = new WhiteChicken(); chicken.setRunBehavior(new NormalRun());//白鸡使用的跑的策略是正常跑 chicken.setSayHiBehavior(new NormalSayHi());//叫声是使用的正常的叫声 chicken.show(); chicken.performRun(); chicken.performSayHi(); BlackChicken chicken2 = new BlackChicken(); chicken2.show(); chicken2.setRunBehavior(new FaseRun()); chicken2.setSayHiBehavior(new ScreamSayHi()); chicken2.performRun(); chicken2.performSayHi(); } }
这样就完美的实现了策略模式,可以根据你的需要,设置不同的策略,修改起来耦合性也比较低,不会对以前的代码有太大的影响。
以上就是策略模式了,定义了算法族,分别封装起来,让他们之间可以互相替换。比如鸡的打招呼行为就有普通打招呼和惨叫打招呼,想用哪个就用哪个。在这里也使用了像多态
和组合
来辅助实现策略模式。多用组合,少用继承也是一个设计原则。
标签:
原文地址:http://blog.csdn.net/u012808234/article/details/51149796