• JavaScript设计模式(五)——发布订阅模式、桥接模式、组合模式


    个人简介

    👀个人主页: 前端杂货铺
    🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
    📃个人状态: 研发工程师,现效力于中国工业软件事业
    🚀人生格言: 积跬步至千里,积小流成江海
    🥇推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒Three.js 🍖JS版算法
    🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

    设计模式

    内容参考链接
    JavaScript设计模式(一)构造器模式、原型模式、类模式
    JavaScript设计模式(二)简单工厂模式、抽象工厂模式、建造者模式
    JavaScript设计模式(三)单例模式、装饰器模式、适配器模式


    ✨✨前言

    大家好,这里是前端杂货铺。

    上一节,我们学习了策略模式、代理模式、观察者模式。这一节,我们学习发布订阅模式、桥接模式和组合模式…

    一、发布订阅模式

    观察者和目标要互相知道,发布者和订阅者 不用相互知道,通过第三方实现调度,属于 经过解耦合的 观察者模式。

    我们定义 PubSub 类(发布订阅),里面的 message 对象用于存放要订阅的信息。

    subscribe 方法期待两个参数(type 和 cb),type 表示要订阅的类型,cb 表示订阅的详细信息,直接把 cb 赋值给 type 即可。

    publish 方法也期待两个参数(type 和 data),如果发布的时候没有该类型,则直接 return,否则发布出该类型。

    unsubscribe 同 subscribe 一样,期待两个参数,用于取消订阅。

    class PubSub {
        message = {};
        // 发布
        publish(type, data) {
            // 发布的时候没有该类型,直接 return
            if (!this.message[type]) return;
            this.message[type].forEach(item => item(data));
        }
        // 订阅
        subscribe(type, cb) {
            this.message[type] = [cb];
        }
        // 取消订阅
        unsubscribe(type, cb) {
            // 取消订阅的时候如果没有该类型,直接 return
            if (!this.message[type]) return;
    
            if (!cb) {
                this.message[type] && (this.message[type].length = 0);
            } else {
                this.message[type] = this.message[type].filter(item => item !== cb);
            }
        }
    }
    
    // 创建 pubsub 实例
    const pubsub = new PubSub();
    
    function milk(data) {
        console.log('milk', data);
    }
    
    function apple(data) {
        console.log('apple', data);
    }
    
    function chicken(data) {
        console.log('chicken', data);
    }
    
    // 订阅 牛奶、苹果、大盘鸡
    pubsub.subscribe('milk', milk);
    pubsub.subscribe('apple', apple);
    pubsub.subscribe('chicken', chicken);
    
    // 发布 牛奶、苹果、大盘鸡
    pubsub.publish('milk', '牛奶');
    pubsub.publish('apple', '苹果');
    pubsub.publish('chicken', '大盘鸡');
    
    console.log('分-----割-----线')
    
    // 取消对大盘鸡的订阅
    pubsub.unsubscribe('chicken');
    
    // 发布 牛奶、苹果、大盘鸡(大盘鸡不会被发布,因为被取消订阅了)
    pubsub.publish('milk', '牛奶');
    pubsub.publish('apple', '苹果');
    pubsub.publish('chicken', '大盘鸡');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    在这里插入图片描述


    二、桥接模式

    将抽象部分与它的实现部分分离,使它们都可以独立地变化。

    使用场景:一个类存在两个或多个独立变化的维度,且这两个维度都需要进行扩展。

    优点:把抽象与实现隔开,有助于独立地管理各组成部分。

    缺点:每使用一个桥接元素都要增加一次函数调用,这对程序的性能会有一些负面影响,还提高了系统的复杂程度。

    举个栗子:汽车厂商会把同一型号的发动机放到多款车型上使用。拿奥迪为例,假如它有 V6、V8 两款发动机,那么这两款发动机会装载到奥迪轿车或奥迪SUV两种车型中。

    在这里插入图片描述

    // 奥迪轿车(车型一)
    class Aodi1 {
        constructor(engine) {
            this.engine = engine;
        }
    }
    
    // 轿车平台
    Aodi1.prototype.platform = function() {
        console.log('aodi1 轿车平台');
    }
    
    // 加载轿车发动机
    Aodi1.prototype.loadEngine = function() {
        this.engine.run();
    }
    
    // 奥迪SUV(车型二)
    class Aodi2 {
        constructor(engine) {
            this.engine = engine;
        }
    }
    
    // SUV平台
    Aodi2.prototype.platform = function() {
        console.log('aodi2 SUV平台');
    }
    
    // 加载SUV发动机
    Aodi2.prototype.loadEngine = function() {
        this.engine.run();
    }
    
    // V6 发动机
    function V6() {
        this.run = () => {
            console.log('v6发动机');
        }
    }
    
    // V8 发动机
    function V8() {
        this.run = () => {
            console.log('v8发动机');
        }
    }
    
    // 搭载 V6 发动机的轿车
    let car1 = new Aodi1(new V6());
    // 搭载 V8 发动机的轿车
    let car2 = new Aodi1(new V8());
    // 搭载 V8 发动机的SUV
    let suv1 = new Aodi2(new V8());
    
    car1.platform();
    car1.loadEngine();
    
    car2.platform();
    car2.loadEngine();
    
    suv1.platform();
    suv1.loadEngine();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    在这里插入图片描述


    三、组合模式

    组合模式在对象间形成 树形结构
    组合模式中基本对象和组合对象被一致对待;
    无须关系对象有多少层,调用时只需在根部进行调用;

    比如我们扫描文件夹和文件夹里面的文件,它是一种树形结构,我们从根文件夹出发,扫描二级文件夹,再扫描二级文件夹里的文件…,类似于如下结构:

    在这里插入图片描述

    // 文件夹
    const Folder = function(folder) {
        this.folder = folder;
        this.list = [];
    }
    
    // 添加文件夹
    Folder.prototype.add = function(res) {
        this.list.push(res);
    }
    
    // 扫描文件夹
    Folder.prototype.scan = function() {
        console.log('扫描文件夹', this.folder);
        for (let i = 0; i < this.list.length; i++) {
            this.list[i].scan();
        }
    }
    
    // 文件
    const File = function(file) {
        this.file = file;
    }
    
    // 扫描文件
    File.prototype.scan = function() {
        console.log('开始扫描文件', this.file);
    }
    
    // 根文件夹
    let rootFolder = new Folder("root");
    
    // 子文件夹
    let htmlFolder = new Folder("html");
    let cssFolder = new Folder("css");
    let jsFolder = new Folder("js");
    
    // 文件
    let html4 = new File("html4");
    let html5 = new File("html5");
    let css2 = new File("css2");
    let css3 = new File("css3");
    let es5 = new File("es5");
    let es6 = new File("es6");
    
    // 添加文件夹
    rootFolder.add(htmlFolder);
    rootFolder.add(cssFolder);
    rootFolder.add(jsFolder);
    
    // 添加文件
    htmlFolder.add(html4);
    htmlFolder.add(html5);
    cssFolder.add(css2);
    cssFolder.add(css3);
    jsFolder.add(es5);
    jsFolder.add(es6);
    
    // 从根扫描
    rootFolder.scan();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    在这里插入图片描述


    🎉🎉本篇小结

    本文我们了解了发布订阅模式、桥接模式、组合模式。

    发布订阅模式 的发布者和订阅者不用相互知道,通过第三方实现调度,属于经过解耦合的观察者模式。

    桥接模式是一种 结构型设计模式,用于把抽象化与实现化解耦,使得二者可以独立变化。

    组合模式 又叫部分整体模式,是一种 结构型设计模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。

    好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


    参考资料:

    1. 百度百科 · 软件设计模式(设计模式)
    2. 菜鸟教程 · 设计模式
    3. JavaScript设计模式 【作者:千锋教育】

    在这里插入图片描述


  • 相关阅读:
    信息学奥赛一本通 1361:产生数(Produce) | 洛谷 P1037 [NOIP2002 普及组] 产生数
    哇喔~~~
    React Hooks
    pandas cut 的方法
    c primer plus学习笔记
    (附源码)计算机毕业设计SSM开放实验室管理系统
    Kafka集群搭建
    【深度学习】深度学习实验四——循环神经网络(RNN)、dataloader、长短期记忆网络(LSTM)、门控循环单元(GRU)、超参数对比
    业务工人业务实体元模型-软件方法(下)第9章分析类图案例篇Part09
    如何使用visual studio 2010构建SQLite3.lib文件
  • 原文地址:https://blog.csdn.net/qq_45902692/article/details/132781469