title: HeadFirst 设计模式之策略模式
date: 2019-3-8 23:53:28
tags: 设计模式
preview_text: 设计模式之headfirst策略模式
1.策略模式的定义
策略模式定义了算法族,分别封装起来,让他们之前可以互相替换,此模式让算法的变化独立于使用算法的客户。
2. 策略模式的场景
- 假设你有一个模拟鸭子程序,利用标准的oo技术设计出,一个鸭子(Duck)超类,并且让各种类型的鸭子集成此超类。
/**
* 鸭子超类 ,由于每个鸭子外观都不同所以是抽象方法
*/
public abstract class Duck {
public void quack(){
System.out.println("quack quack");
}
public void swim(){
System.out.println("swimming");
}
abstract void display();
}
2.1使用继承
1.此时,产品经理决定鸭子需要有一个会飞的动作。作为一个开发者,这是最简单不过的事情,只需要在超类Duck中添加一个fly() 方法,所有继承于该超类的鸭子都会有飞行效果。
/**
* 鸭子超类 ,由于每个鸭子外观都不同所以是抽象方法
*/
public abstract class Duck {
public void quack(){
System.out.println("quack quack");
}
public void swim(){
System.out.println("swimming");
}
abstract void display();
public void fly(){
System.out.println("flying");
}
}
- 然而这样开发却忽略了一件事情,有一些不适合飞行方法的子类会仍旧具有该能力,例如橡皮鸭。
/**
* 橡皮鸭在继承中,也获取了飞行能力
*/
public class RubberDuck extends Duck {
@Override
void display() {
System.out.println("Im RubberDuck");
}
}
此时,可能会想到继承,覆盖该飞行方法,使其成为空方法。但是如果以后再要求加入新的鸭子品种,例如诱饵鸭,诱饵鸭不会叫,也不会飞。则需要覆盖他两个方法。
/**
* 模型鸭覆盖fly方法
*/
public class RubberDuck extends Duck {
@Override
public void fly() {
//空方法
}
@Override
void display() {
System.out.println("Im RubberDuck");
}
}
/**
* 诱饵鸭 不会叫也不会飞,覆盖fly以及quack方法
*/
public class DecoyDuck extends Duck {
@Override
public void quck() {
//空方法
}
@Override
public void fly() {
//空方法
}
@Override
void display() {
System.out.println("Im RubberDuck");
}
}
2.1.1 继承总结
- 中途总结使用继承的缺点,再来新的类型的鸭子都会继承所有方法,你需要针对所有鸭子,去覆盖对应方法。更为可怕的事情是鸭子增加新特性,例如进食效果,你需要对所有鸭子进行进食效果的处理。总结缺点:
- 代码在多个子类中重复。
- 运行时,行为难以改变。
- 很难知道鸭子所有的行为。
- 改变牵一发而动全身,造成其他鸭子不想要的改变。
2.2 实现接口,使用策略模式
- 我们知道quack(),fly() 方法会随着鸭子的不同而改变,此时我们需要将这两个行为独立出来。(文章只示例 独立一个fly接口)
/**
*飞行接口独立
*/
public interface FlyBehavior {
void fly();
}
/**
* 用翅膀飞行
*/
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("fly whit wings");
}
}
/**
* 用翅膀飞行
*/
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
}
}
这样的设计,可以让飞行的动作被其他的对象复用,因为飞行动作已经跟鸭子类无关了。我们也可以增加一些行为,不会影响到行为类,也不会影响到使用飞行的鸭子。
2.整合鸭子类的行为,将鸭子的飞行行为委托别的类处理。
public abstract class Duck {
//飞行行为接口
FlyBehavior flyBehavior;
public void quack(){
System.out.println("quack quack");
}
public void swim(){
System.out.println("swimming");
}
abstract void display();
//飞行方法
public void perfromFly(){
flyBehavior.fly();
}
}
在这段代码中,我们不在乎flyBehavior接口的对象到底是什么,我们只需要关心它如何呱呱叫就ok。
3.具体的鸭子集成,实现飞行方法,只需在构造器中,针对接口具体实现就可以了。
public class MallarDuck extends Duck {
//由于继承得到了flyBehavior, 使用FlyWithWings 实现该鸭子的飞行方法
public MallarDuck(){
flyBehavior = new FlyWithWings();
}
@Override
void display() {
System.out.println("Im MallarDuck" );
}
}
2.2.2 设定其动态行为
1.让鸭子在运行时,动态改变其飞行方法。在鸭子类中加入个新方法。
public void setFlyBehavior(FlyBehavior fh){
flyBehavior = fb;
}
2.创建一个新的FlyBehavior类型。
public class FlyWithRocket implements FlyBehavior{
public void fly(){
System.out.println("Im fly with rocket" );
}
}
- 测试方法中,想要改变它的飞行方法只需要调用setFlyBehavior()方法即可以改变它的飞行动作。
public class DuckTest {
public static void main(String[] args) {
Duck mallarduck = new MallarDuck();
mallarduck.perfromFly();
mallarduck.setFlyBehavior(new FlyWithRocket());
mallarduck.perfromFly();
}
}
控制台输出
Im fly with wings
Im fly with rocket
总结
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。