初学设计模式:策略模式
tags:blog 设计模式 2015-03
初学设计模式: 策略模式
引入: 策略模式用来做什么? 为什么要用策略模式?
需求:
现在我是一名开发者, 我需要开发一个关于鸭子的模拟系统.这个系统可能会有 MallardDuck, RedHeadDuck 鸭子是能够飞的,他们需要有 Fly 方法, 而且都是能发出叫声和游泳的,即有 swim 和 quack 方法.
最简单的代码设计方式:用Duck 作为超类,用MarllardDuck & RedHeadDuck 分别继承Duck类:
public abstract class Duck{
public void Fly(){
System.out.println("FLy...");
}
public void Quack(){
System.out.println("Quack");
}
public void swim(){
System.out.println("swim");
}
public void display(){
System.out.println("duck");
}
}
//继承上面的DUCK,从而有了 Fly & display 方法
public class MallardDuck extends Duck{
public void display(){
System.out.println("marllardDuck");
}
}
public class RedHeadDuck extends Duck{
public void display(){
System.out.println("RedHeadDuck");
}
}
上面的设计目前是很合理的,但是需求总是不固定的. 比方说我们现在增加了两种鸭子:RubberDuck(橡皮鸭) & DecoyDuck(诱饵鸭). 前者不会飞,后者既不会飞也不会叫.这个时候用上面的设计就显得不合理了, 当继承 Duck的时候会将 quack & fly 方法同时继承过来, 你当然可以通过复写 fly & quack 方法来使得继承过来的方法无效, 但是这里继承还是暴露出来了它的问题:
- 代码在多个子类中重复
- 运行时不能改变
- 牵一发而动全身
- 很难知道鸭子的所有行为
既然从Duck继承 fly & quack 会有这么多的问题,那么将 fly & quack 写成接口,然后用 Duck 的子类去实现这个接口如何? 答案当然是不好,接口是无法实现复用目的的! 这样会造成大量的冗余代码,每个具有上述方法的 Duck 的子类都会去重写 fly & quack 方法, 而且最最可怕的是, 一旦fly 方法需要一点变更, 必须要修改每一个 Duck 子类!
既然传统的方法无法解决问题,那就需要一些改变.
OO的设计原则说:
应当找出应用中可能需要变化之处, 把他独立出来.
应当把会变化的部分取出并封装起来, 以后便可以容易的扩充或改动此部分, 而不影响其他的功能.
上述概念很简单, 但却是每个设计模式的精神所在: 让系统中某个部分的改变不会影响到其他的部分.
所以, 既然 fly & quack 是变化的地方, 我们就应该把它 取出&封装 :
//Duck超类
public abstract class Duck {
protected FlyBehavior flyBehavior;
protected QuackBehavior quackBehavior;
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
...
}
//fly接口
public interface FlyBehavior {
void fly();
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!");
}
}
//quack接口
public interface QuackBehavior {
void quack();
}
public class Quack implements QuackBehavior{
public void quack(){
System.out.println("quack");
}
}
//Duck 的继承类
public class ADuck extends Duck {
public ADuck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
}
}
按照以上方法定义出来的Duck 类既实现了代码的最大复用,同时又避免了陷入扩展的困境.
若果需要,我们还可以在给ADuck 的实例不同的fly 或是 quack 方法,以达到在 运行时改变的目的.
本例中, fly & quack 实际上是一个个具体的方法, 通过面向接口编程的思想, 我们只看重fly & quack 的接口,而将实现委托给 fly & quack 的具体实现类来执行.这也就是策略模式.
以下是策略模式的正式解释:
定义了算法簇,分别封装起来,让他们之间可以互相替换.此模式让算法的变化独立于使用算法的客户.
通过这个例子,我才算是真正明白了策略模式,good,加油!