策略模式(Strategy Pattern)

来源:互联网 发布:网络线路切换器 编辑:程序博客网 时间:2024/06/05 00:46

策略模式

策略模式:在我们的生活中其实就有很多这样的例子,比如我们我们要做一件事情,就说看书吧,A喜欢先看简介,然后看目录,然后在从第一章开始看,这就是看书的一种策略,B喜欢直接从第一章开始看,C喜欢直接看简介,然后看结尾。这是三种看书的方式,也就是所说的策略,对于策略,我的理解就是同一件事,对于不同的对象所匹配的一种执行事件的方式。策略模式是为了实现弱耦合的很好的方式。映射到程序员的世界里,策略就是算法了,策略模式就是处理同一件事,我可以有好几个不同的策略(算法)。
没事玩个鸡应用
污污公司开发了个应用叫没事玩个鸡,这是一款娱乐类应用,可以一键玩鸡,有不同品类的鸡给你玩,如三黄鸡、乌鸡、白鸡等。这个应用的内部设计是标准面向对象技术,设计了一个鸡超类(Superclass),让各种鸡继承此超类。我们来看一下代码:

/** * 超类鸡 */    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();    }}

这样就完美的实现了策略模式,可以根据你的需要,设置不同的策略,修改起来耦合性也比较低,不会对以前的代码有太大的影响。
总结
以上就是策略模式了,定义了算法族,分别封装起来,让他们之间可以互相替换。比如鸡的打招呼行为就有普通打招呼和惨叫打招呼,想用哪个就用哪个。在这里也使用了像多态和组合来辅助实现策略模式。多用组合,少用继承也是一个设计原则。

原创粉丝点击