结合ES6 编写 JavaScript 设计模式中的结构型模式

目录
  • 前言
  • 什么是设计模式?
  • 结构型设计模式
  • 适配器模式
    • 实例
  • 桥接模式
    • 实例
  • 组合模式
    • 实例
  • 装饰者模式
    • 实例
  • 门面模式
    • 实例
  • 享元模式
    • 实例
  • 代理模式
    • 实例

前言

本文将对 20 多种 JavaScript 设计模式进行简单概述,然后结合 ES6 类的方式来编写实例代码展示其使用方式。

JavaScript 在现代前端中扮演重要的角色,相比过去能够做的事情已经不在一个级别上了。JavaScript 最大的特征是其灵活性,一般只要敢想敢写,可以把程序写得很简单,有可以写得很复杂。其灵活性导致编写 JavaScript 的时候能够不断的优化,能够不断质疑写的质量。而设计模式,是不分编程语言的,是软件工程的内容,JavaScript 强大的表现力赋予了开发者运用设计模式编写代码时创造性。

设计模式系列文章:

  • ES6 类聊 JavaScript 设计模式之创建型模式

什么是设计模式?

设计模式是软件设计中常见问题的解决方案,这些模式很容易重复使用并且富有表现力。

在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。它并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案。面向对象设计模式通常以类别或对象来描述其中的关系和相互作用,但不涉及用来完成应用程序的特定类别或对象。—— 维基百科

在 JavaScript 中使用设计模式主要有以下原因:

  • 可维护性:设计模式有助于降低模块间的耦合程度。
  • 沟通:设计模式为处理不同类型的对象提供了一套通用的术语。
  • 性能:对代码起到优化作用,提高程序的运行速度。当然不是所有的设计模式都能够改善性能。

有三种模式:创建型模式,结构型模式、行为型模式。

  • 创建型模式:解决与创建对象相关的问题。
  • 结构型模式:处理实体之间的关系,以及它们如何共同组成一个更大的结构。
  • 行为型模式:处理对象如何相互通信和交互。

结构型设计模式

结构型设计模式涉及类和对象组合,使用继承来组合接口。

在软件工程中,结构设计模式是通过识别实现实体之间关系的简单方法来简化设计的设计模式。

  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰者模式
  • 门面模式
  • 享元模式
  • 代理模式

适配器模式

适配器模式允许具有不兼容接口的类通过将它们自己的接口封装在现有类周围来一起工作。适配器也称为包装器(wrapper),用来把不匹配的接口替换为一个可用于现有系统中的接口。

在软件工程中,适配器模式是一种软件设计模式,它允许将现有类的接口用作另一个接口。它通常用于使现有类在不修改其源代码的情况下与其他类一起使用。

实例

实例将通过一个计算器的例子进行改造。 Calculator1 是旧接口,Calculator2 是新接口。将构建一个适配器,它将包装新接口并使用其新方法为其提供结果。

class Calculator1 {
    constructor() {
        this.operations = function (value1, value2, operation) {
            const operationHandler = {
                add: () => value1 + value2,
                sub: () => value1 - value2,
            };
            return (
                typeof (operationHandler[operation] === "function") &&
                operationHandler[operation]()
            );
        };
    }
}
class Calculator2 {
    constructor() {
        this.add = function (value1, value2) {
            return value1 + value2;
        };
        this.sub = function (value1, value2) {
            return value1 - value2;
        };
    }
}
// 使用适配器模式构建类
class CalcAdapter {
    constructor() {
        const cal2 = new Calculator2();
        this.operations = function (value1, value2, operation) {
            return (
                typeof (cal2[operation] === "function") &&
                cal2[operation](value1, value2)
            );
        };
    }
}
const adaptedCalc = new CalcAdapter();
console.log(adaptedCalc.operations(30, 25, "sub")); // 5

桥接模式

桥接模式将抽象与实现分开,以便两者可以独立变化。是一种既能把两个对象连接在一起,又能避免二者间的强耦合的方法。

桥接模式是一种结构化设计模式,它允许将一个大类或一组密切相关的类拆分为两个独立的层次结构:抽象和实现,它们可以相互独立地开发。—— 维基百科

实例

将创建渲染器类来渲染多个形状。

class VectorRenderer {
    renderCircle(radius) {
        console.log(`渲染一个半径为 ${radius} 的圆`);
    }
}
class RasterRenderer {
    renderCircle(radius) {
        console.log(`绘制一个半径为 ${radius} 的像素圆`);
    }
}
class Shape {
    constructor(renderer) {
        this.renderer = renderer;
    }
}
class Circle extends Shape {
    constructor(renderer, radius) {
        super(renderer);
        this.radius = radius;
    }

    draw() {
        this.renderer.renderCircle(this.radius);
    }
    resize(factor) {
        this.radius *= factor;
    }
}
const raster = new RasterRenderer();
const vector = new VectorRenderer();
const circle = new Circle(vector, 5);
const rasterCircle = new Circle(raster, 5);
circle.draw();
circle.resize(2);
circle.draw();
rasterCircle.draw();
rasterCircle.resize(2);
rasterCircle.draw();

组合模式

组合模式组合对象,以便可以将它们作为单个对象进行操作,非常适合用于创建WEB上的动态用户界面。

组合模式描述了一组对象,这些对象的处理方式与相同类型对象的单个实例相同。—— 维基百科

实例

将使用工作示例:

class Employer {
    constructor(name, role) {
        this.name = name;
        this.role = role;
    }
    print() {
        const { name } = this;
        console.log(`姓名:${name}`);
    }
}
class EmplyerGroup {
    constructor(name, composite = []) {
        console.log(name);
        this.name = name;
        this.composite = composite;
    }
    print() {
        console.log(this.name);
        this.composite.forEach((emp) => {
            emp.print();
        });
    }
}
const ravi = new Employer("ravi", "developer");
const bhavy = new Employer("bhavy", "developer");
const groupDevelopers = new EmplyerGroup("Developer", [ravi, bhavy]);
groupDevelopers.print();

装饰者模式

装饰者模式动态地添加或覆盖对象的行为,用于把对象透明的包装到另一个种具有相同接口的对象中。

装饰器模式是一种设计模式,它允许将行为动态地添加到单个对象,而不会影响同一类中其他对象的行为。

实例

将以颜色和形状为例,如果必须画一个圆,将创建方法并画一个圆。如果要画一个红色的圆圈,将行为被添加到一个对象中,装饰器模式将帮助实现。

class Shape {
    constructor(color = "") {
        this.color = color;
    }
}
class Circle extends Shape {
    constructor(radius = 0) {
        super();
        this.radius = radius;
    }
    resize(factor) {
        this.radius *= factor;
    }
    toString() {
        return `一个直径为 ${this.radius} 的园`;
    }
}
class ColoredShape extends Shape {
    constructor(shape, color) {
        super();
        this.shape = shape;
        this.color = color;
    }
    toString() {
        return `${this.shape.toString()},颜色为${this.color}`;
    }
}
const circle = new Circle(2);
console.log(circle); // Circle { color: '', radius: 2 }
const redCircle = new ColoredShape(circle, "红色");
console.log(redCircle.toString()); // 一个直径为 2 的园,颜色为红色

门面模式

门面模式为复杂代码提供了一个简化的接口,可以用来把现有接口转换为一个更便于使用的接口。

门面模式是面向对象编程中常用的一种软件设计模式。与架构中的外观类似,外观是一个对象,它充当前端接口,掩盖更复杂的底层或结构代码。 —— 维基百科

实例

举一个客户端与计算机交互的例子:

class CPU {
    freeze() {
        console.log("冻结…");
    }

    jump() {
        console.log("跳过…");
    }

    execute() {
        console.log("执行…");
    }
}
class Memory {
    load(position, data) {
        console.log("加载中…");
    }
}
class HardDrive {
    read(lba, size) {
        console.log("读取中…");
    }
}
class ComputerFacade {
    constructor() {
        this.processor = new CPU();
        this.ram = new Memory();
        this.hd = new HardDrive();
    }
    start() {
        this.processor.freeze();
        this.ram.load("", this.hd.read("lba", "size"));
        this.processor.jump("");
        this.processor.execute();
    }
}
const computer = new ComputerFacade();
computer.start();

享元模式

享元模式降低了创建类似对象的内存成本,是一种可以用于优化目的的设计模式。最适合用于解决因创建大量类似对象而累及性能的问题。

享元是一种通过与其他类似对象共享尽可能多的数据来最小化内存使用的对象。—— 维基百科

实例

以用户为例,让有多个同名的用户,可以通过存储一个名称来节省内存,并将其引用给具有相同名称的用户。

class User {
    constructor(fullname) {
        this.fullname = fullname;
    }
}
class User2 {
    constructor(fullname) {
        const getOrAdd = (s) => {
            const idx = User2.strings.indexOf(s);
            if (idx !== -1) {
                return idx;
            } else {
                User2.strings.push(s);
                return User2.strings.length - 1;
            }
        };
        this.names = fullname.split(" ").map(getOrAdd);
    }
}
User2.strings = [];
const getRadmon = (max) => Math.floor(Math.random() * Math.floor(max));
const randomString = () => {
    const result = [];
    for (let x = 0; x < 10; ++x) {
        result.push(String.fromCharCode(65 + getRadmon(26)));
    }
    return result.join("");
};

现在将通过创建 10k 个用户来进行不使用享元模式和使用享元模式的占用字符大小来类比内存大小。

const users = [];
const users2 = [];
const firstnames = [];
const lastnames = [];
for (let i = 0; i < 100; ++i) {
    firstnames.push(randomString());
    lastnames.push(randomString());
}
for (const first of firstnames) {
    for (const last of lastnames) {
        users.push(new User(`${first} ${last}`));
        users2.push(new User2(`${first} ${last}`));
    }
}
console.log(`10k用户占用了大约 ${JSON.stringify(users).length} 字符`); // 10k用户占用了大约 370001 字符
const user2length = [users2, User2.strings]
    .map((x) => JSON.stringify(x).length)
    .reduce((x, y) => x + y);
console.log(`10k用户在享元模式占用约 ${user2length} 字符`); // 10k用户在享元模式占用约 191602 字符

代理模式

通过使用代理,一个类可以代表另一个类的功能。更加详细的介绍,可以参阅《JavaScript 设计模式之代理模式

代理模式是一种软件设计模式。代理,在其最一般的形式中,是一个作为与其他事物的接口的类。—— 维基百科

实例

以价值代理作为实例。

class Percentage {
    constructor(percent) {
        this.percent = percent;
    }
    toString() {
        return `${this.percent}%`;
    }
    valueOf() {
        return this.percent / 100;
    }
}
const fivePercent = new Percentage(5);
console.log(fivePercent.toString()); // 5%
console.log(`5% 的50倍是${50 * fivePercent}`); // 5% 的50倍是2.5

到此这篇关于结合ES6 编写 JavaScript 设计模式中的结构型模式的文章就介绍到这了,更多相关JS 结构型模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 解析Javascript设计模式Revealing Module 揭示模式单例模式

    目录 1. Revealing Module 揭示模式 2. Singleton 单例模式 1. Revealing Module 揭示模式 该模式能够在私有范围内简单定义所有的函数和变量,并返回一个匿名对象, 它拥有指向私有函数的指针,该函数是他希望展示为公有的方法. 示例: <script> var myRevealingModule = function () { var privateVar = "Ren Cherry", publicVar = "Hey

  • JavaScript设计模式之中介者模式详解

    目录 中介者模式 现实中的中介者 中介者模式的例子 泡泡堂游戏 为游戏增加队伍 玩家增多带来的困扰 用中介者模式改造泡泡堂游戏 小结 中介者模式 在我们生活的世界中,每个人每个物体之间都会产生一些错综复杂的联系.在应用程序里也是一样,程序由大大小小的单一对象组成,所有这些对象都按照某种关系和规则来通信. 平时我们大概能记住 10 个朋友的电话.30 家餐馆的位置.在程序里,也许一个对象会和其他 10 个对象打交道,所以它会保持 10 个对象的引用.当程序的规模增大,对象会越来越多,它们之间的关系

  • JavaScript设计模式之职责链模式详解

    目录 职责链模式 1. 现实中的职责链模式 2. 实际开发中的职责链模式 3. 用职责链模式重构代码 4. 灵活可拆分的职责链节点 5. 异步的职责链 6. 职责链模式的优缺点 7. 用 AOP 实现职责链 8. 小结 职责链模式 职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 职责链模式的名字非常形象,一系列可能会处理请求的对象被连接成一条链,请求在这些对象之间依次传递,直到遇

  • JS前端中的设计模式和使用场景示例详解

    目录 引言 策略模式 1.绩效考核 2.表单验证 策略模式的优缺点: 代理模式 1.图片懒加载: 2.缓存代理 总结 引言 相信大家在日常学习和工作中都多多少少听说/了解/使用过 设计模式,我们都知道,使用恰当的设计模式可以优化我们的代码,那你是否知道对于前端开发哪些 设计模式 是日常工作经常用到或者必须掌握的呢?本文我将带大家一起学习下前端常见的设计模式以及它们的 使用场景!!! 本文主讲: 策略模式 代理模式 适合人群: 前端人员 设计模式小白/想知道如何在项目中使用设计模式 策略模式 策略

  • JS前端设计模式之发布订阅模式详解

    目录 引言 例子1: version1: version2: 总结 引言 昨天我发布了一篇关于策略模式和代理模式的文章,收到的反响还不错,于是今天我们继续来学习前端中常用的设计模式之一:发布-订阅模式. 说到发布订阅模式大家应该都不陌生,它在我们的日常学习和工作中出现的频率简直不要太高,常见的有EventBus.框架里的组件间通信.鉴权业务等等......话不多说,让我们一起进入今天的学习把!!! 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系 当一个对象的状态发生改变时,所有

  • JavaScript设计模式之单例模式

    目录 单例模式 实现单例模式 透明的单例模式 用代理实现单例模式 惰性单例 通用的惰性单例 小结 单例模式 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池.全局缓存.浏览器中的 window 对象等.在 JavaScript 开发中,单例模式的用途同样非常广泛.试想一下,当我 们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少 次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建. 实现单例模式 要实现一个标准的单例模

  • 结合ES6 编写 JavaScript 设计模式中的结构型模式

    目录 前言 什么是设计模式? 结构型设计模式 适配器模式 实例 桥接模式 实例 组合模式 实例 装饰者模式 实例 门面模式 实例 享元模式 实例 代理模式 实例 前言 本文将对 20 多种 JavaScript 设计模式进行简单概述,然后结合 ES6 类的方式来编写实例代码展示其使用方式. JavaScript 在现代前端中扮演重要的角色,相比过去能够做的事情已经不在一个级别上了.JavaScript 最大的特征是其灵活性,一般只要敢想敢写,可以把程序写得很简单,有可以写得很复杂.其灵活性导致编

  • Python设计模式中的结构型桥接模式

    目录 一.桥接模式 二.应用场景 三.代码示例 一.桥接模式 桥接模式,希望能够将一个事物的两个维度分离(解耦),使其都可以独立地变化,并通过桥梁连接起来. (类)抽象部分(Abstraction):存在于多个实体中的共同的概念性联系,就是抽象化.作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待. (对象)实体部分(Implementation):抽象化给出的具体实现,就是实现化. 简而言之,桥接模式就是指在一个软件系统的抽象化和实现化之间,使用组合/聚合关系而不是继承关

  • Python设计模式中的结构型适配器模式

    目录 一.适配器模式 二.应用场景 三.代码示例 方式一 方式二 一.适配器模式 适配器,顾名思义是一种万能的接口,达到万能转换的效果. 适配器模式,定义一个适配器类,并且在该类中定义了适配器接口,这些适配接口能够将原来由于接口不兼容而不能在一起工作的多种类型进行适配,使得它们能够一同工作. 二.应用场景 三.代码示例 实体角色: 目标接口(Target):定义提供给 Client 访问的接口,可以是一个抽象类或接口,也可以是具体类.待适配的类 / 适配者类(Adaptee):被适配的角色,它们

  • JavaScript设计模式中的桥接和中介者模式

    目录 一.桥接设计模式 桥接设计模式在工作中的使用 二.JavaScript中介者模式 生活中的中介者 中介者设计模式案例 业务中的中介者 一.桥接设计模式 桥接设计模式是一种偏向于组合的设计模式,而非继承的设计模式,实现的细节从一个模块推送给另一个具有单独模块的对象 桥接设计模式在开发中常用于事件监控,还有数组的一些方法都能够体现出来桥接设计模式的思想,例如数组方法的forEach方法 桥接设计模式在工作中的使用 有一个很多页面的网站,我们预期是让用户可以选择修改网站的主体,如果给每个页面都创

  • JavaScript设计模式中的观察者模式

    目录 观察者设计模式 初始数据 被观察者 观察者 观察者设计模式 观察者设计模式中分为被观察者和观察者,当被观察者触发了某个边界条件,观察者就会触发事件,这里需要俩个构造函数进行观察者设计模式一个是被观察者一个是观察者 我们来利用观察者监听对象某个属性的修改操作,对象某个属性的修改会触发观察者的某些方法 初始数据 let obj = { name: "若水" } 被观察者 创建被观察者,我们给被观察者定义一个初始化状态,用于记录观察属性的初始值,还需定义一个观察者方法队列,用于对观察者

  • JavaScript 设计模式中的代理模式详解

    前言: 代理模式,代理(proxy)是一个对象,它可以用来控制对另一个对象的访问. 现在页面上有一个香港回归最想听的金典曲目列表: <ul id="container"> <li>我的中国心</li> <li>东方之珠</li> <li>香港别来无恙</li> <li>偏偏喜欢你</li> <li>相亲相爱</li> </ul> 需要给页面添加

  • 理解JavaScript设计模式中的单例模式

    单例模式(Singleton Pattern)是最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建.这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象. 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 这样做的缺点就是:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关

  • JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法示例

    本文实例讲述了JavaScript设计模式之观察者模式(发布订阅模式)原理与实现方法.分享给大家供大家参考,具体如下: 观察者模式,又称为发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己的状态. 在观察者模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的特定活动并在状态改变后获得通知.订阅者也称为观察者,而被观察的对象称为发布者或主题.当发生了一个重要的事件时,发布

  • Go语言设计模式之结构型模式

    目录 一.组合模式(Composite Pattern) 1.1.简述 1.2.Go实现 二.适配器模式(Adapter Pattern) 2.1.简述 2.2.Go实现 三.桥接模式(Bridge Pattern) 3.1.简述 3.2.Go实现 四.总结 一.组合模式(Composite Pattern) 1.1.简述 在面向对象编程中,有两个常见的对象设计方法,组合和继承,两者都可以解决代码复用的问题,但是使用后者时容易出现继承层次过深,对象关系过于复杂的副作用,从而导致代码的可维护性变差

  • Java结构性设计模式中的装饰器模式介绍使用

    目录 装饰器模式 概述 实现原理 主要角色 应用场景 优缺点 装饰器模式的基本使用 创建抽象组件 具体组件 抽象装饰器 具体装饰器 客户端调用 装饰器模式 概述 装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),属于结构型模式. 它是指在不改变原有对象的基础之上,允许向一个现有的对象添加新的功能,同时又不改变其结构,作为现有的类的一个包装. 这种模式创建一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能. 提供了比继承

随机推荐