初学设计模式:策略模式

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,加油!

原文地址:https://www.cnblogs.com/bugmaster/p/4353519.html