Strategy Pattern(策略模式)

420 阅读4分钟

引言

请不要染上“模式病”,……以后连写一个“Hello World” 都能够扯上模式,那就代表你已经病了... ... 谨做记录,以便以后浏览

需求

本文从==鸭子==这种可爱又好吃的小动物来展开,但是我们今天不说如何吃它,而且讨论如果优雅的实现鸭子的一些特定行为。

  1. 每只鸭子都会游泳、叫。
  2. 事实上有些鸭子会飞、有些鸭子并不会。
  3. 给鸭子加持火箭动力,使其成为火箭动力鸭。
  4. 针对接口编程,而不是针对实现编程

一、实现鸭子超类

public abstract class Duck {
	//叫
	public void quack(){}
	//游泳
	public void swim(){}
	//特征方法
	public abstract void display();
}

鸭子的超类决定了鸭子具有==叫==、==游泳==、==display==三个特性。

二、改变某些鸭子的行为

到这里可能大家会说,这不就写个父类继承吗? 对,你说的没错。这时我们的第一个问题来了: 假设现在已经诞生了很多只鸭子,而需要有些鸭子可以飞行,怎样实现会好一点?

给鸭子超类编写一个飞行方法?

// 飞行
public void fly() {
	System.out.print("我是会飞的鸭子");
}

这样会造成所有的鸭子子类都具有了飞行的特性,而不是部分。

三、放弃继承,使用接口

既然继承无法完美的实现功能,那么得另辟蹊径,去寻找新的方法来解决问题,首先,想到了接口。 可以将 quack()fly() 从==Duck==中分离出来。 创建 Iquack 接口和 Ifly 接口,然后让需要实现 quackfly 的鸭子去实现对应的接口。 这样比继承棒多了,再也不会看到橡皮鸭子叫着在天上飞了。

但是这样重复的代码会变多,在需要修改的子类个数较少时还能应付。 那么对成百上千个鸭子修改一下飞行的行为就会变得繁杂。 虽然使用接口解决了一部分问题,但是却造成代码无法复用。 这只能算是从一个噩梦跳进了另外一个噩梦 甚至,在会飞的鸭子中,飞行的行为也是多变的 或许就有一只鸭子是使用==火箭动力系统==来飞行

四、再次明确问题

至此还是没有找到解决问题的好方法,把问题归零梳理:

  1. 继承并不能很好的解决问题,因为鸭子的行为在子类不停的改变。
  2. 接口解决了问题,但是java接口不具有实现代码,所以继承接口无法达到代码的复用

五、策略模式

使用策略模式可以应对这种问题,策略模式的思路是: 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。 按照这个思路,要分离出来的变化之处就是 quack()fly()。 并且我们要按照针对接口编程,而不是针对实现编程原则 首先定义两个接口 QuackBehaviorFlyBehavior

六、新的实现

创建 FlyWithWings 类实现 IFlyBehavior //会飞 创建 FlyNoWay 类实现 IFlyBehavior //不会飞 创建 Quack类实现 IQuackBehavior //会叫 创建 Squeak类实现 IQuackBehavior //会说话

这样的设计,可以让==飞==和==叫==的动作被其他对象复用,因为这些行为已经与==Duck==无关了,而我们新增的一些行为,不会影响到既有的行为类。

public interface IFlyBehavior {
	void fly();
}
public interface IQuackBehavior {
	void quack();
}

然后将这两个接口整个到鸭子的超类。

public abstract class Duck {
	public IFlyBehavior iFlyBehavior;
	public IQuackBehavior iQuackBehavior;
	// 游泳
	public void swim() {}
	// 特征方法
	public abstract void display();
	// 执行 飞
	public void performFly() {
		iFlyBehavior.fly();
	}
	// 执行 叫
	public void performQuack() {
		iQuackBehavior.quack();
	}
}

使用接口实例,将fly和quack委托给行为类 这样就可以在实例化鸭子对象的时候,通过行为的不同来让鸭子实现特定的行为。 在实例化行为的时候,使用特定的行为类。

public class RedDuck extends Duck {
	public RedDuck() {
		iFlyBehavior = new FlyWithWings();
		iQuackBehavior = new Quack();
	}
	@Override
	public void display() {
		System.out.println("红色鸭子");
	}
}

到此,我们的设计总算成功了。 七、测试

更加深入一步,通过这样的设计,可以在运行时去动态的改变鸭子的行为。 上面的实现是需要在构造器去定制鸭子的行为。 现在鸭子不在是一只只会嘎嘎叫的鸭子了,它可以一飞冲天 将Duck和行为分开后,降低了耦合,提升了复用,最重要的是让代码的可扩展性得到了质的提升 如何在运行时去改变鸭子的行为。 首先我们想到Duck里面的行为接口实例。 通过改变接口的实现,来在运行时改变鸭子的行为。 添加setter方法,这样就可以动态的设置鸭子子类行为接口的实现

public static void main(String[] args) {
   	RedDuck redDuck = new RedDuck();
   	redDuck.setiFlyBehavior(new Rocket());
   	redDuck.performFly();
   }

运行结果: 火箭动力飞行

不知不觉,程序已经完善了,很神奇,这就是Strategy Pattern策略模式。

参考:《Head First Java》