Head First设计模式读书笔记一 策略模式

本文示例代码材料源自Head First设计模式
以前整理自己整理的策略模式的链接:
https://blog.csdn.net/u011109881/article/details/60478840

策略模式思想

核心思想,分离变与不变。
例如原先我们设计了一个动物园系统,这个是个动物园是个不正经的动物园,只有鸭子,比如绿头鸭,塑料玩具鸭,木头鸭子等等。原先的系统已经设计好了,系统中有各种鸭子继承了Duck,并有一个方法swim(假设所有鸭子都会游泳)
类图如下:
Head First设计模式读书笔记一 策略模式_第1张图片
要知道,作为程序员,肯定知道需求的变动是很频繁的,现在发生了需求变动。要给原来的鸭子添加飞行的方法。有Java基础的会立马想到在Duck类中添加一个Fly方法。但是,请听我说完需求。现在所需要的飞行方法不是单一的,比如绿头鸭和野鸭等“真正的”鸭子是用翅膀飞的;木头鸭,塑料玩具鸭是不能飞的。马达鸭(一种玩具鸭,可以装电池,利用马达飞行)可以利用马达飞行。这样一来,继承的方式就不太好了。如果硬要使用继承,那么需要在每个子类鸭子重写父类的fly方法。
那么,使用接口呢?比如写出接口基类FlyBehavior。
然后用FlyNoWay接口表示不会飞的鸭子,FlyNoWay继承了FlyBehavior,不会飞的鸭子实现FlyNoWay接口;
用FlyWithWings接口表示会用翅膀飞的鸭子,FlyWithWings继承了FlyBehavior,会用翅膀飞的鸭子实现FlyWithWings接口;
以此类推。这样乍看很好啊,没有什么问题。但是很快我们会发现如果是木头鸭和塑料鸭,它们都实现了FlyNoWay接口,并且都在自己的类中将该方法重写一遍。因此,出现了重复代码。
那么,还有其他什么更好的方式来添加飞行方法吗?有,就是委托。委托这个词看起来很难懂,但是我们可以把委托者和被委托者想成has-a(有一个)的关系。比如上面这个例子,鸭子(被委托者)有一个飞行行为(委托者)
把飞行想成Duck的一个功能,Duck具有该功能,即Duck本身含有飞行行为的实例,可以调用飞行行为的飞行方法。
详细设计方式可以参见类图

示例思路(规划类图)

类结构:
Head First设计模式读书笔记一 策略模式_第2张图片
UML类图:
Head First设计模式读书笔记一 策略模式_第3张图片

实际代码

  • Duck基类
public abstract class Duck {
    String name;
    FlyBehavior flyBehavior;

    public void swim() {
        System.out.println("通常,所有鸭子都会游泳");
    }

    public String getName() {
        System.out.println("this is "+name);
        return name;
    }

    public void myFlyBehavior() {
        if(null == flyBehavior){
            System.out.println("你还没有给该鸭子赋予飞行方式");
            return;
        }
        flyBehavior.fly();
    }

    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
}
  • 实体类MallardDuck
public class MallardDuck extends Duck{
    public MallardDuck(String n){
        this.name =n;
    }
}
  • 实体类ToyDuck
public class ToyDuck extends Duck{
    public ToyDuck(String n){
        this.name =n;
    }
}
  • 接口FlyBehavior
public interface FlyBehavior {
    public void fly();
}
  • 飞行实现类FlyNoWay
public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("这是个假的飞行方法,我根本不会飞");
    }
}
  • 飞行实现类FlyWithWings

public class FlyWithWings implements FlyBehavior{
    public void fly() {
        System.out.println("我用翅膀飞翔");
    }
}
  • 测试类
public class DuckTest {
    public static void main(String[] args) {
        Duck toyDuck = new ToyDuck("Tim--ToyDuck");
        toyDuck.setFlyBehavior(new FlyNoWay());
        toyDuck.getName();
        toyDuck.myFlyBehavior();

        Duck mallardDuck = new MallardDuck("Jerry--mallardDuck");
        mallardDuck.setFlyBehavior(new FlyWithWings());
        mallardDuck.getName();
        mallardDuck.myFlyBehavior();
    }
}

测试结果:

this is Tim--ToyDuck
这是个假的飞行方法,我根本不会飞
this is Jerry--mallardDuck
我用翅膀飞翔

总结,再看实例

思路:分离变与不变。回忆之前的需求,可以发现,不变的是swim,变的是fly。虽然swim和fly都是鸭子的行为,但是swim不怎么变化,它可以利用继承来实现代码复用,而飞行方式却有很多种,无法继承,因此需要将飞行行为抽出来,各自实现,让Duck实现类拥有飞行行为实例,达到操作飞行行为的目的。这种思想就像零件的组装,主体是不变的,零件有各种相同功能不同性能的各种款式,这样可以做出不同的产品。
另外策略模式还有一个好处是可以动态变化行为,比如木头鸭子原来是不会飞的,它用setFlyBehavior设置不会飞行,后来设计师给他装上了引擎翅膀,它可以再次通过setFlyBehavior在运行时动态变更飞行行为。
说到这里,回顾之前写的策略模式(https://blog.csdn.net/u011109881/article/details/60478840)
在之前的例子中,看样子类图和本文的差很多,其实不然。我们对比一下:
Strategy就是本例中的FlyBehavior
Context就是本例中的Duck
之前的文章其实没有本文的类结构复杂,因为前文“只有一种鸭子”,因此也就省去“鸭子的各种实现类了”,所以看上去是比较简单的,但本质一样。另外,个人觉得之前的文章例子虽然可以解释策略模式,但是实际开发完全可以使用switch case来实现,因为那个例子没有明显的变与不变的地方。

其他策略模式的例子:

出行方式:比如一个角色character,他出门可以有不同的交通方式,如果目的地近,可以选择步行,如果距离远,可以选择开车,如果路上塞车,可以选择公交地铁。那么出行方式的变化可以抽出来形成一种策略。拿本文中的例子类比的话,角色相当于鸭子,各种交通方式相当于飞行行为。
游戏角色和武器装备:游戏中角色有不同的种类,战士法师弓箭手盗贼等等,角色可以穿着不同的武器装备,甚至还有角色限定,继续类比,则角色相当于不同种类的鸭子,武器套装则相当于不同的飞行方式,游戏角色可以随时更换武器装备。

你可能感兴趣的:(设计模式)