“如果我成了诸葛亮” —— 策略模式

268 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。


大家好,我是方圆,这篇博客儿主要参考《Head First 设计模式》


1. 我遇到了一个问题

我有一只小鸭子,每天就知道嘎嘎叫,我觉得很无聊,我想让它学学飞,以下是对我的鸭子的代码

public class RealDuck extends Duck {
    @Override
    public void quack() {
        super.quack();
    }
}

我们也顺便看以下,它继承的基类Duck

public abstract class Duck {

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

}

1.1 我要让它怎么实现飞起来呢?

  • 首先,我想到了!我直接在基类Duck里写一个飞的方法不就好了吗?简单!
public abstract class Duck {

    public void quack(){
        System.out.println("嘎嘎嘎");
    }
    
    public void fly(){
        System.out.println("我学会飞啦!");
    }
}

简单测试一下

public class Test {
    public static void main(String[] args) {
        Duck myDuck = new RealDuck();

        myDuck.quack();
        myDuck.fly();
    }
}

在这里插入图片描述

很好,问题貌似解决了,但是,如果是玩具小黄鸭实现了这个基类,那岂不是玩具鸭子也飞了起来

public class Test {
    public static void main(String[] args) {
        Duck myDuck = new RealDuck();

        myDuck.quack();
        myDuck.fly();

        System.out.println("玩具鸭 ↓");
        Duck toyDuck = new ToyDuck();
        toyDuck.fly();
    }
}

在这里插入图片描述

这下可咋好?玩具鸭也飞起来了,那我在玩具鸭里把这个fly方法重写一下吧,这就解决了

public class ToyDuck extends Duck {
    @Override
    public void fly() {
        System.out.println("我是玩具鸭,我不会飞");
    }
}

但是回过头来,想想,我不光只有玩具鸭啊,我还有宠物鸭,唐老鸭,小黄鸭等等,很多鸭子都不会飞,每个类都要重写一下,岂不是很麻烦吗?而且代码很多都是重复的,这个问题怎么解决呀?

1.2 我想到了解决办法

  • 设计原则:将应用中可能需要变化的部分,独立出来,不要和那些不变的代码混在一起。

那我们,把fiy()这个方法拿出来吧,但是要怎么往外拿呢?我们看一下下一个设计原则

  • 设计原则:针对接口编程,而不是针对实现编程

怎么理解呢,拿鸭子飞这个例子来说,我们把飞定义为FlyBehavior接口,这样用它的实现类实现这个接口,以达到不同的飞的方式(会飞与不会飞),理解了吗?没有理解正常,我说的不清楚,看下面代码就明白了

public interface FlyBehavior {
    void fyl();
}

public class CanFly implements FlyBehavior {
    @Override
    public void fyl() {
        System.out.println("我能飞");
    }
}
public class CantFly implements FlyBehavior {
    @Override
    public void fyl() {
        System.out.println("我不会飞哇!");
    }
}

两个实现类实现了FlyBehavior接口,成就了两种会飞与不会飞的方法,这样,这个飞行的动作就能够让其他的对象复用,因为这些行为已经跟鸭子没有关系了,实现了解耦,就算我们新增别的行为,也不需要修改鸭子的代码。

1.3 将这种方法进行实现

我们在Duck中引入FlyBehavior接口这个实例变量,如下

public abstract class Duck {
    
    private FlyBehavior flyBehavior;

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

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

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

这样我们就可以,根据不同的鸭子注入不同的行为,实现不同的动作啦,我们试试!

public class Test {
    public static void main(String[] args) {
        Duck myDuck = new RealDuck();
        myDuck.setFlyBehavior(new CanFly());
        myDuck.fly();

        System.out.println("玩具鸭 ↓");
        Duck toyDuck = new ToyDuck();
        toyDuck.setFlyBehavior(new CantFly());
        toyDuck.fly();
    }
}

在这里插入图片描述

我们完成了鸭子业务的设计,我们回过头来,俯瞰一下这个大局

在这里插入图片描述

这也体现了一种设计原则

  • 设计原则:多用组装,少用继承

等等,听你讲了这么半天?策略模式呢?啊?听了半天鸭子没有设计模式我不干!

在这里插入图片描述

等等!放下刀,我这就说,这就说!

2. 策略模式

其实策略模式,已经出现过了,只不过小的没提而已。我们看一眼它的描述,说的是啥?

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

明白了吧,好了,收!

在这里插入图片描述

好吧,把刀放下,我接着讲,接着讲昂!

在这里插入图片描述

认真看看红框里的东西,比对一下概念,是不是有点儿明白了? 咱说白了,它不就是,不就是能够在行为间不同的切换嘛? 我写写代码你就一下懂了!

写俩新接口实现类

public class FlyStyleOne implements FlyBehavior {
    @Override
    public void fyl() {
        System.out.println("我先慢点儿飞!");
    }
}
public class FlyStyleTwo implements FlyBehavior {
    @Override
    public void fyl() {
        System.out.println("我再快点儿飞!");
    }
}

测试一下

public class Test {
    public static void main(String[] args) {
        Duck myDuck = new RealDuck();
        myDuck.setFlyBehavior(new CanFly());
        myDuck.fly();

        myDuck.setFlyBehavior(new FlyStyleOne());
        myDuck.fly();

        myDuck.setFlyBehavior(new FlyStyleTwo());
        myDuck.fly();
    }
}

显示结果

在这里插入图片描述

飞的不同的方式,对应的就是不同的算法,我能够把这些算法之间随便换,也就是我的鸭子,随便飞,想咋飞咋飞,换着法儿的飞!这就实现了策略模式。前边的都是铺垫,策略模式就这儿点儿。

Ok,这下行了吧?

在这里插入图片描述


能看到这里,也奖励一把小fafa吧~