Strategy Pattern 策略模式
2015-12-08 11:37
363 查看
最近在学设计模式,这些博客权当笔记。
这种需求是很简单的需求,只需要有一个Duck父类,在父类里设计一个fly和一个quack方法,让子类来继承它就哦了。如果Duck父类实现了fly(用翅膀飞)和quack(呱呱叫)方法,那么所有的鸭子子类都会继承这两个方法,代码简介了很多,而且子类可以通过覆盖这两个方法去实现不同的功能。
但是如果有橡皮鸭子和木头鸭子的出现,橡皮鸭子会吱吱叫而不是父类的呱呱叫,但是橡皮鸭子不会飞;木头鸭子既不会叫也不会飞。
针对这种情况可以将父类的fly和quack方法设置成抽象方法,让子类具体实现,这种方式有弊端,那就是如果有大量不同的子类就会造成在子类中fly和quack方法里代码的重复。
还有一种方法就是父类的fly和quack方法实现大多数鸭子的fly和quack功能,少数的鸭子子类只需要覆盖这两个方法即可,但是鬼知道大多数鸭子的fly和quack功能是什么样的、少数鸭子的fly和quack功能又是什么样的,并且在这个需求一直改变的现况下,用这两种方式会经常去改原有的代码。
我们可以使用策略模式来实现该功能,具体的操作就是把需求中可能会改变的东西给拿出来,让它和不变的东西分离开来,这样需求再改变的时候只需要修改可变的部分,而不用去修改原有的不变的代码。代码变化引起的不经意后果变少,系统变得更有弹性。这就是我们的设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。意思就是,如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分代码需要被抽出来,和其他稳定的代码有所区分。这样的概念很简单,几乎是每个设计模式背后的精神所在。所有的模式都提供一种方法让"系统中的某部分改变不影响其他部分"。
我们知道Duck类的fly和quack方法会随着鸭子的不同而有所改变,所以我们把这两个方法从Duck类中抽出来,建立一组新类来代表每个行为。我们希望一切能有弹性,因为每个鸭子的实现类不确定,所以fly和quack方法的具体实现也不确定,我们就可以设计一个FlyBehavior和Quackehavior接口,然后每增加一种fly方式,我们就增加一个新类来实现FlyBehavior接口;同样每增加一个quack方式,我们就增加一个新类类实现QuackBehavior接口。这样就可以动态的去实现fly和quack方法,而不需要在编译期写死。
![](http://img.blog.csdn.net/20151208115922689?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这里如果有一个真实的鸭子,那么就增加一个RealFly类来实现FlyBehavior接口同时实现fly方法,因为父类Duck已经有了FlyBehavior的引用,所以可以在父类Duck的fly方法里直接调用FlyBehavior的接口,而在子类RealDuck中的构造方法中来根据实际情况去实例化一个FlyBehavior的子类,这里实例化的是RealFly。这样再调用RealDuck的fly方法的时候就会调用RealFly的fly方法。这样就做到了动态实现fly方法。这就是另外一个设计原则:针对接口编程,而不是针对实现编程。
这样的设计,可以让fly和quack方法被其他鸭子的实现类复用,因为这两个行为已经不再和鸭子类有关了。 另外我们还可以再增加些其他的行为,例如eat(吃,不同鸭子吃的行为也不一样),这样不会影响到即有的行为类,也不会影响"使用"到fly行为的鸭子类。这么一来,有了继承的"复用"好处,却没有继承带来的包袱。这些实现了FlyBehavior和QuackBehavior接口的"行为类"(因为这些类中只有行为,没有具体的属性)不但可以用在鸭子上,也可以用在其他类上,如鸡、鸟、天鹅...
用火箭飞行的唐老鸭:
真是的鸭子:会呱呱叫,会用翅膀飞:
木头鸭子,不会飞,也不会叫:
这样我们就可以让所有的鸭子子类任意组合fly和quack行为,因为fly和quack行为是独立的,谁都可以任意拿来用,只要遵循委托的规则。
测试:
我是一个真鸭子
我用翅膀飞....
呱呱 呱呱...
----------------------------
我是一个橡皮鸭子...
我不会飞...
吱吱 吱吱...
----------------------------
我是一个木头鸭子
我不会飞...
<<我不会叫>>
----------------------------
我会用火箭飞...
我用火箭筒飞...
呱呱 呱呱...
Strategy Pattern 策略模式
策略模式简介:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
现在有一个需求,有一个Duck鸭子的父类,需要用父类来生出很多不同种类的鸭子子类,大家都知道真实的鸭子会叫,有的鸭子会飞,(我也不知道会不会飞),但是还有玩具鸭子,例如橡皮鸭子(一捏就会吱吱叫,但是不会飞)、木头鸭子(怎么捏也不会叫,也不会飞),还有唐老鸭(唐老鸭背着喷气筒用火箭飞,还会呱呱叫)。这只是暂时的需求,后期还会根据新生的鸭子产生对应的需求。这种需求是很简单的需求,只需要有一个Duck父类,在父类里设计一个fly和一个quack方法,让子类来继承它就哦了。如果Duck父类实现了fly(用翅膀飞)和quack(呱呱叫)方法,那么所有的鸭子子类都会继承这两个方法,代码简介了很多,而且子类可以通过覆盖这两个方法去实现不同的功能。
但是如果有橡皮鸭子和木头鸭子的出现,橡皮鸭子会吱吱叫而不是父类的呱呱叫,但是橡皮鸭子不会飞;木头鸭子既不会叫也不会飞。
针对这种情况可以将父类的fly和quack方法设置成抽象方法,让子类具体实现,这种方式有弊端,那就是如果有大量不同的子类就会造成在子类中fly和quack方法里代码的重复。
还有一种方法就是父类的fly和quack方法实现大多数鸭子的fly和quack功能,少数的鸭子子类只需要覆盖这两个方法即可,但是鬼知道大多数鸭子的fly和quack功能是什么样的、少数鸭子的fly和quack功能又是什么样的,并且在这个需求一直改变的现况下,用这两种方式会经常去改原有的代码。
我们可以使用策略模式来实现该功能,具体的操作就是把需求中可能会改变的东西给拿出来,让它和不变的东西分离开来,这样需求再改变的时候只需要修改可变的部分,而不用去修改原有的不变的代码。代码变化引起的不经意后果变少,系统变得更有弹性。这就是我们的设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。意思就是,如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分代码需要被抽出来,和其他稳定的代码有所区分。这样的概念很简单,几乎是每个设计模式背后的精神所在。所有的模式都提供一种方法让"系统中的某部分改变不影响其他部分"。
我们知道Duck类的fly和quack方法会随着鸭子的不同而有所改变,所以我们把这两个方法从Duck类中抽出来,建立一组新类来代表每个行为。我们希望一切能有弹性,因为每个鸭子的实现类不确定,所以fly和quack方法的具体实现也不确定,我们就可以设计一个FlyBehavior和Quackehavior接口,然后每增加一种fly方式,我们就增加一个新类来实现FlyBehavior接口;同样每增加一个quack方式,我们就增加一个新类类实现QuackBehavior接口。这样就可以动态的去实现fly和quack方法,而不需要在编译期写死。
<span style="font-size:18px;">package wang.behavior; public interface FlyBehavior { void fly(); }</span>
<span style="font-size:18px;">package wang.behavior; public interface QuackBehavior { <span style="white-space:pre"> </span>void quack(); } </span>
这里如果有一个真实的鸭子,那么就增加一个RealFly类来实现FlyBehavior接口同时实现fly方法,因为父类Duck已经有了FlyBehavior的引用,所以可以在父类Duck的fly方法里直接调用FlyBehavior的接口,而在子类RealDuck中的构造方法中来根据实际情况去实例化一个FlyBehavior的子类,这里实例化的是RealFly。这样再调用RealDuck的fly方法的时候就会调用RealFly的fly方法。这样就做到了动态实现fly方法。这就是另外一个设计原则:针对接口编程,而不是针对实现编程。
package wang.oldservice; /** * @author Wang */ public abstract class Duck { //抽象方法,让子类去实现自己要展示的是什么 public abstract void display(); /** * 所有的鸭子子类都会继承该方法,都有飞的功能 */ public void fly(){ System.out.println("I am Flying~"); } /** * 所有的鸭子子类都会继承该方法,都有呱呱叫的功能 */ public void quack(){ System.out.println("呱呱呱~~"); } }
这样的设计,可以让fly和quack方法被其他鸭子的实现类复用,因为这两个行为已经不再和鸭子类有关了。 另外我们还可以再增加些其他的行为,例如eat(吃,不同鸭子吃的行为也不一样),这样不会影响到即有的行为类,也不会影响"使用"到fly行为的鸭子类。这么一来,有了继承的"复用"好处,却没有继承带来的包袱。这些实现了FlyBehavior和QuackBehavior接口的"行为类"(因为这些类中只有行为,没有具体的属性)不但可以用在鸭子上,也可以用在其他类上,如鸡、鸟、天鹅...
package wang.behavior.impl; import wang.behavior.FlyBehavior; public class FlyWithWings implements FlyBehavior { @Override public void fly() { System.out.println("我用翅膀飞...."); } }
<pre name="code" class="java">不会飞的行为: package wang.behavior.impl; import wang.behavior.FlyBehavior; public class CannotFly implements FlyBehavior { @Override public void fly() { System.out.println("我不会飞..."); } }
用火箭飞行的唐老鸭:
package wang.behavior.impl; import wang.behavior.FlyBehavior; public class FlyWithRocket implements FlyBehavior { @Override public void fly() { System.out.println("我用火箭筒飞..."); } }呱呱叫的行为:
package wang.behavior.impl; import wang.behavior.QuackBehavior; public class GuaGuaQuack implements QuackBehavior { @Override public void quack() { System.out.println("呱呱 呱呱..."); } }吱吱叫的行为:
package wang.behavior.impl; import wang.behavior.QuackBehavior; public class ZhiZhiQuack implements QuackBehavior { @Override public void quack() { System.out.println("吱吱 吱吱..."); } }不会叫的行为:
package wang.behavior.impl; import wang.behavior.QuackBehavior; public class CannotQuack implements QuackBehavior { @Override public void quack() { System.out.println("<<我不会叫>>"); } }接下来就是要实现具体的鸭子子类了
真是的鸭子:会呱呱叫,会用翅膀飞:
package wang.service; import wang.behavior.impl.FlyWithWings; import wang.behavior.impl.GuaGuaQuack; public class RealDuck extends Duck { public RealDuck() { flyBehavior = new FlyWithWings(); quackBehavior = new GuaGuaQuack(); } @Override public void display() { System.out.println("我是一个真鸭子"); } }橡皮鸭子,会吱吱叫,但是不会飞:
package wang.service; import wang.behavior.impl.CannotFly; import wang.behavior.impl.ZhiZhiQuack; public class XiangpiDuck extends Duck { public XiangpiDuck() { flyBehavior = new CannotFly(); quackBehavior = new ZhiZhiQuack(); } @Override public void display() { System.out.println("我是一个橡皮鸭子..."); } }
木头鸭子,不会飞,也不会叫:
package wang.service; import wang.behavior.impl.CannotFly; import wang.behavior.impl.CannotQuack; public class WoodenDuck extends Duck { public WoodenDuck() { flyBehavior = new CannotFly(); quackBehavior = new CannotQuack(); } @Override public void display() { System.out.println("我是一个木头鸭子"); } }会用火箭飞的唐老鸭,也会呱呱叫:
package wang.service; import wang.behavior.impl.FlyWithRocket; import wang.behavior.impl.GuaGuaQuack; public class DuckFlyWithRocket extends Duck { public DuckFlyWithRocket() { flyBehavior = new FlyWithRocket(); quackBehavior = new GuaGuaQuack(); } @Override public void display() { System.out.println("我会用火箭飞..."); } }
这样我们就可以让所有的鸭子子类任意组合fly和quack行为,因为fly和quack行为是独立的,谁都可以任意拿来用,只要遵循委托的规则。
测试:
package wang.behavior.test; import wang.service.Duck; import wang.service.DuckFlyWithRocket; import wang.service.RealDuck; import wang.service.WoodenDuck; import wang.service.XiangpiDuck; public class Test { /** * @param args */ public static void main(String[] args) { Duck duck1 = new RealDuck(); duck1.display(); duck1.fly(); duck1.quack(); System.out.println("----------------------------"); Duck duck2 = new XiangpiDuck(); duck2.display(); duck2.fly(); duck2.quack(); System.out.println("----------------------------"); Duck duck3 = new WoodenDuck(); duck3.display(); duck3.fly(); duck3.quack(); System.out.println("----------------------------"); Duck duck4 = new DuckFlyWithRocket(); duck4.display(); duck4.fly(); duck4.quack(); } }输出结果为:
我是一个真鸭子
我用翅膀飞....
呱呱 呱呱...
----------------------------
我是一个橡皮鸭子...
我不会飞...
吱吱 吱吱...
----------------------------
我是一个木头鸭子
我不会飞...
<<我不会叫>>
----------------------------
我会用火箭飞...
我用火箭筒飞...
呱呱 呱呱...
相关文章推荐
- Unity3D-DoTween
- Spring AOP 事务管理
- Xcode7 修改bundle identifier
- javascript中window.event事件用法详解
- android(cm11)状态栏源码分析(一)
- 队列的模拟
- leetcode Intersection of Two Linked Lists
- C++标准库——时间相关
- VS下LUA编程环境搭建
- Esper系列(十)NamedWindow语法delete、Select+Delete、Update
- 分享Android开发自学笔记之AndroidStudio常用功能
- 欢迎使用CSDN-markdown编辑器
- js+ajax实现获取文件大小的方法
- SQL数据库对某一字段下的值进行求和,然后倒序排序
- 位域初识
- 隔离框架
- quartz启动两次(tomcat)
- 链表中倒数第k个节点
- MySQL之Xtrabackup原理及实施
- 图片文字提取之路-01预处理