趣解设计模式之《会飞的橡皮鸭》

〇、小故事

小王公司要开发一个养鸭子的模拟养育游戏。游戏中会出现各种类型的鸭子。小王设计了一个基础的鸭子类------Duck类,这个类有3个方法:

quack 】用来描述鸭子叫声的方法;

swim 】用来描述鸭子游泳姿态的方法;

display】用来描述鸭子外貌的方法;

然后通过继承Duck类,根据鸭子的不同种类/类型创建了很多的鸭子子类,比如:绿头鸭红头鸭橡皮鸭等等。程序运行很棒,过了几天,领导说,小王啊,咱们游戏中小鸭子行为举止太单一了,能不能让这些鸭子能飞起来,这样看着效果更好。小王说:"没问题,这个好办!"。

这时,小王就在父类Duck里增加了一个fly()方法,然后就自信的去部署程序然后睡觉了。第二天,给客户演示的游戏展示大会上,自信满满的领导在演示时,发生了可怕的事情,那就是,一大堆橡皮鸭也在游戏中飞来飞去。游戏发布会失败了!

小王被领导狠狠的批评了一顿,回去后,就开始改bug,把橡皮鸭的fly方法重写为不会飞,并且一个个的检查了游戏中的40多个类型的鸭子的所有行为(因为都是从父类继承的)。小李当天加班到了凌晨4点

他突然发现了自己设计中非常严重的问题,那就是,按照现在的设计方式,每当有新的鸭子子类出现的时候,他就要被迫检查所有父类继承过来的方法是否需要覆盖重写,比如fly(),quark()等。这简直是个巨大的大坑

那我们有什么办法可以解决上面的问题吗?当然是可以的,那就是我们今天要介绍的设计模式------策略模式

一、模式定义

策略模式Strategy Pattern

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

这里面的核心思想就是,将变化的行为抽取出来,然后采用组合的方式而非继承的方式来为目标类增加所需能力 。以上面的故事为例,我们可以将fly行为、quack行为等抽取出来,分别定义为接口。然后,通过实现FlyBehavior接口QuackBehavior接口来丰富具体飞行与叫声行为(即:算法族),然后在具体的鸭子实现类中,引入不同的飞行和叫声的子类实现,从而实现行为的多样性。并且,可以通过更换子类来变更鸭子的具体行为。

二、模式类图

根据上面小王的故事,我们来用策略模式对其第一版的设计进行改造一下。首先,将飞行行为和叫声行为从Duck父类中抽取出来,分别定义为FlyBehavior接口和QuackBehavior接口。

然后针对FlyBehavior飞行行为接口定义如下实现类:

FlyWithWings 】用翅膀飞行的行为;

FlyNoWay】不会用翅膀进行飞行的行为;

然后针对QuackBehavior叫声行为接口定义如下实现类:

Quack 】呱呱的叫;

Squeak 】吱吱的叫;

MuteQuack】不会叫;

好了,上面的这5个实现类就是所谓的算法族 ,为什么被称为算法族呢?我们日常编写代码,其实就是由数据结构+算法行为组成的,所有的行为都可以称之为算法,所以,针对飞行行为和叫声行为,都被称为算法族。

除了算法族之外,我们创建Duck父类,以及各种不同种类的鸭子,例如:绿头鸭红头鸭橡皮鸭......

具体的类图实现请见下图所示:

三、代码实现

创建抽象鸭子父类Duck.java

java 复制代码
public abstract class Duck {
    protected FlyBehavior flyBehavior;

    protected QuackBehaviorBehavior quackBehaviorBehavior;

    public abstract void display();

    public void swin() {
        System.out.println("鸭子游泳");
    }

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

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

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

    public void setQuackBehaviorBehavior(QuackBehaviorBehavior quackBehaviorBehavior) {
        this.quackBehaviorBehavior = quackBehaviorBehavior;
    }
}

创建鸭子的具体实现子类DecoyDuck�.javaMallardDuck�.javaRedheadDuck.javaRubberDuck.java

java 复制代码
/**
 * @description 诱饵鸭
 * @author: muse
 **/
public class DecoyDuck extends Duck {
    public DecoyDuck() {
        flyBehavior = new FlyNoWay(); // 不会飞
        quackBehaviorBehavior = new MuteQuack(); // 哑巴鸭,不会叫
    }
    public void display() {
        System.out.println("诱饵鸭的外貌特征");
    }
}

/**
 * @description 绿头鸭
 * @author: muse
 **/
public class MallardDuck extends Duck {
    public MallardDuck() {
        flyBehavior = new FlyWithWings(); // 鸭子飞行
        quackBehaviorBehavior = new Quack(); // 红头鸭的呱呱叫
    }
    public void display() {
        System.out.println("绿头鸭的外貌特征");
    }
}

/**
 * @description 红头鸭
 * @author: muse
 **/
public class RedheadDuck extends Duck {
    public RedheadDuck() {
        flyBehavior = new FlyWithWings(); // 鸭子飞行
        quackBehaviorBehavior = new Quack(); // 红头鸭的呱呱叫
    }
    public void display() {
        System.out.println("红头鸭的外貌特征");
    }
}

/**
 * @description 橡皮鸭
 * @author: muse
 **/
public class RubberDuck extends Duck {
    public RubberDuck() {
        flyBehavior = new FlyNoWay(); // 不会飞
        quackBehaviorBehavior = new Squeak(); // 橡皮鸭的吱吱叫
    }
    public void display() {
        System.out.println("橡皮鸭的外貌特征");
    }
}

创建飞行行为接口FlyBehavior.java

java 复制代码
/**
 * @description 飞行行为
 * @author: muse
 **/
public interface FlyBehavior {
    void fly();
}

创建具体的飞行行为子类FlyNoWay.javaFlyWithWings.java

java 复制代码
/**
 * @description 不会飞行
 * @author: muse
 **/
public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("不会飞行");
    }
}

/**
 * @description 实现鸭子的飞行
 * @author: muse
 **/
public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("实现鸭子的飞行");
    }
}

创建叫声行为接口QuackBehaviorBehavior.java

java 复制代码
/**
 * @description 叫声行为
 * @author: muse
 **/
public interface QuackBehaviorBehavior {
    void quack();
}

创建具体的叫声行为子类MuteQuack.javaQuack.javaSqueak.java

java 复制代码
/**
 * @description 不会叫
 * @author: muse
 **/
public class MuteQuack implements QuackBehaviorBehavior {
    public void quack() {
        System.out.println("哑巴鸭,不会叫");
    }
}

/**
 * @description 呱呱叫
 * @author: muse
 **/
public class Quack implements QuackBehaviorBehavior {
    public void quack() {
        System.out.println("普通鸭子的呱呱叫");
    }
}

/**
 * @description 吱吱叫
 * @author: muse
 **/
public class Squeak implements QuackBehaviorBehavior {
    public void quack() {
        System.out.println("橡皮鸭的吱吱叫");
    }
}

测试类StrategyTest.java

java 复制代码
/**
 * @description 测试类
 * @author: muse
 **/
public class StrategyTest {
    public static void main(String[] args) {
        Duck rubberDuck = new RubberDuck(); // 生成橡皮鸭
        rubberDuck.display();
        rubberDuck.performFly();
        
        System.out.println("-----------对橡皮鸭进行改造,让它能飞起来------------");
        
        rubberDuck.setFlyBehavior(new FlyWithWings()); // 更换会飞的算法族
        rubberDuck.performFly();
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号"爪哇缪斯" ~ \(^o^)/ ~ 「干货分享,每天更新」

相关推荐
开心就好20252 小时前
不同阶段的 iOS 应用混淆工具怎么组合使用,源码混淆、IPA混淆
后端·ios
架构师沉默2 小时前
程序员如何避免猝死?
java·后端·架构
椰奶燕麦2 小时前
Windows PackageManager (winget) 核心故障排错与通用修复指南
后端
zjjsctcdl3 小时前
springBoot发布https服务及调用
spring boot·后端·https
zdl6863 小时前
Spring Boot文件上传
java·spring boot·后端
世界哪有真情3 小时前
哇!绝了!原来这么简单!我的 Java 项目代码终于被 “拯救” 了!
java·后端
RMB Player3 小时前
Spring Boot 集成飞书推送超详细教程:文本消息、签名校验、封装工具类一篇搞定
java·网络·spring boot·后端·spring·飞书
重庆小透明3 小时前
【搞定面试之mysql】第三篇 mysql的锁
java·后端·mysql·面试·职场和发展
武超杰4 小时前
Spring Boot入门教程
java·spring boot·后端
IT 行者4 小时前
Spring Boot 集成 JavaMail 163邮箱配置详解
java·spring boot·后端