您的位置:首页 > 其它

Strategy Pattern 策略模式

2015-12-08 11:37 363 查看
最近在学设计模式,这些博客权当笔记。

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();
}

}
输出结果为:
我是一个真鸭子

我用翅膀飞....

呱呱 呱呱...

----------------------------

我是一个橡皮鸭子...

我不会飞...

吱吱 吱吱...

----------------------------

我是一个木头鸭子

我不会飞...

<<我不会叫>>

----------------------------

我会用火箭飞...

我用火箭筒飞...

呱呱 呱呱...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: