(Strategy Pattern(策略模式)
引言
请不要染上“模式病”,……以后连写一个“Hello World” 都能够扯上模式,那就代表你已经病了… …
谨做记录,以便以后浏览
需求
本文从鸭子这种可爱又好吃的小动物来展开,但是我们今天不说如何吃它,而且讨论如果优雅的实现鸭子的一些特定行为。
- 每只鸭子都会游泳、叫。
- 事实上有些鸭子会飞、有些鸭子并不会。
- 给鸭子加持火箭动力,使其成为火箭动力鸭。
- 针对接口编程,而不是针对实现编程
一、实现鸭子超类
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 接口,然后让需要实现 quack 和 fly 的鸭子去实现对应的接口。
这样比继承棒多了,再也不会看到橡皮鸭子叫着在天上飞了。
但是这样重复的代码会变多,在需要修改的子类个数较少时还能应付。
那么对成百上千个鸭子修改一下飞行的行为就会变得繁杂。
虽然使用接口解决了一部分问题,但是却造成代码无法复用。
这只能算是从一个噩梦跳进了另外一个噩梦
甚至,在会飞的鸭子中,飞行的行为也是多变的
或许就有一只鸭子是使用火箭动力系统来飞行
四、再次明确问题
至此还是没有找到解决问题的好方法,把问题归零梳理:
- 继承并不能很好的解决问题,因为鸭子的行为在子类不停的改变。
- 接口解决了问题,但是java接口不具有实现代码,所以继承接口无法达到代码的复用
五、策略模式
使用策略模式可以应对这种问题,策略模式的思路是:
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
按照这个思路,要分离出来的变化之处就是 quack() 和 fly()。
并且我们要按照针对接口编程,而不是针对实现编程原则
首先定义两个接口 QuackBehavior 和 FlyBehavior
六、新的实现
创建 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》