《Head First 设计模式》读书笔记——策略模式

因为疫情在家无聊,把设计模式复习一遍。强烈推荐《Head First 设计模式》这本书,看了好几遍了,每次都受益匪浅。

策略模式概念

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

涉及到的设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起

通俗的讲这个原则就是把新的需求所需的代码变化封装,和以前稳定的代码区分。

这个原则的另一种思考方式:把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分。

几乎每个设计模式都提供一套方法令系统中的「某部分改变」不会影响其他部分。

举个例子来具体说明策略模式,就用书中鸭子的例子,我们需要让不同的鸭子实现不同的行为,比如,真鸭子会飞会叫,玩具鸭不会飞,不会叫,并且不产生代码冗余,且符合我们刚提到的设计原则。

示例代码

先来看一个糟糕的实现方式,首先是鸭子抽象类,所有的鸭子都继承这个类。

public abstract class Duck {
    public void swim() {
        System.out.println("鸭子游泳");
    }

    public abstract void display();
}

接着说飞行和叫的接口,有这些行为的鸭子需要实现这两个接口。

public interface Flyable {
    void fly();
}

public interface Quackable {
    void quack();
}

再来就是几种鸭子的类。

public class MallardDuck extends Duck implements Flyable, Quackable {
    @Override
    public void display() {
        System.out.println("Mallard");
    }

    @Override
    public void fly() {
        System.out.println("fly");
    }

    @Override
    public void quack() {
        System.out.println("quack");
    }
}

public class RedheadDuck extends Duck implements Flyable, Quackable {
    @Override
    public void display() {
        System.out.println("Red head");
    }

    @Override
    public void fly() {
        System.out.println("read head fly");
    }

    @Override
    public void quack() {
        System.out.println("quack");
    }
}

public class RubberDuck extends Duck implements Quackable {
    @Override
    public void display() {
        System.out.println("Rubber");
    }

    @Override
    public void quack() {
        System.out.println("quack");
    }
}

感受一下吧,这个种实现方式哪里不好?代码冗余,重复代码太多,如果很多个鸭子要修改它的行为,这将是噩梦啊。

回到我们之前的设计原则,找出应用中的变化并独立除了,不要把可变和不可变的代码混合。

分开变化和不变的部分

为了打到这个目的,我们将会建立两组类,和Duck类无关的类,为了实现他们的行为,那么从我们的上述例子来看,那就是「fly」和「quack」两个行为了。比如叫的行为,我们可以实现「呱呱叫」、「嘎嘎叫」和「不会叫」,毕竟鸭子叫法是不一样的。

既然行为已经分出来了,接下来该设计它的行为,这又涉及到一个设计原则:针对接口编程,而不是针对实现编程。

划重点,这是什么意思?针对接口编程,就拿咱们这个例子来讲,是在「Duck」类中,行为不是通过具体的实现去表现,而使用接口代替,比如,「FlyBehavior」和「QuackBehavior」去替代鸭子的行为,他们只负责调用方法,具体的实现方式由这两个接口的行为类去实现。

来比较一下两种两种方式的差别,第一种方式,我们通过实现接口来搞定这两个行为,专注于在鸭子类中实现,没办法更改,当我们需要更改的时候只能通过修改鸭子代码去改。而后面一种,鸭子的子类使用接口表现的行为,实际的实现是通过行为类实现的,而不是绑死在鸭子的子类中。这样就为变化提供了方便。

我们来看看代码实现。

策略模式代码实现

鸭子抽象类,

public abstract class Duck {
    QuackBehavior quackBehavior;
    FlyBehavior flyBehavior;

    public abstract void display();

    public void performQuack() {
        quackBehavior.quack();
    }

    public void performFly() {
        flyBehavior.fly();
    }

    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}

这里面,我们包括了两个接口,一个「QuackBehavior」一个「FlyBehavior」,用以在 performQuack 和 performFly 两个方法中执行动作,下面两个 set 方法,是我们可以更改鸭子行为方法。

再来看飞行行为接口,及它的实现类。

public interface FlyBehavior {
    void fly();
}

public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("can't fly");
    }
}

public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("Fly with Wings");
    }
}

这里很简单就是一个接口,两个实现类。同理,叫的接口结构类似。

public interface QuackBehavior {
    void quack();
}

public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Quack");
    }
}

public class Squeak implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Squeak");
    }
}

public class MuteQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("<< Silence >>");
    }
}

两个鸭子类。

public class MalardDuck extends Duck {
    @Override
    public void display() {
        System.out.println("Malard");
    }

    public MalardDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        this.flyBehavior = flyBehavior;
        this.quackBehavior = quackBehavior;
    }
}

public class RedHeadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("red head duck");
    }

    public RedHeadDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        this.flyBehavior = flyBehavior;
        this.quackBehavior = quackBehavior;
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        MalardDuck malardDuck = new MalardDuck(new FlyWithWings(), new Quack());
        malardDuck.display();
        malardDuck.performFly();
        malardDuck.performQuack();

        System.out.println("-----------");

        RedHeadDuck redHeadDuck = new RedHeadDuck(new FlyNoWay(), new MuteQuack());
        redHeadDuck.display();
        redHeadDuck.performQuack();
        redHeadDuck.performFly();
        System.out.println("change ---------");
        redHeadDuck.display();
        redHeadDuck.setFlyBehavior(new FlyWithWings());
        redHeadDuck.performFly();
        redHeadDuck.setQuackBehavior(new Quack());
        redHeadDuck.performQuack();
    }
}

我们通过构造方法,指定鸭子的行为,通过 set 方法修改鸭子的行为,这样当我们需要增加行为的时候,只需要增加新的行为类,重新指定鸭子的行为就可以了,而无需修改鸭子类的实现。

这里也使用了一个设计原则,多用组合,少用继承

使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以「在运行时动态地改变行为」,只要组合的行为对象符合正确的接口标准即可。

到这里整个策略模式就完事了。以后再有新想法,再补充吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值