前端宝典十六:深入浅出8大设计模式

2024-08-24 13:20

本文主要是介绍前端宝典十六:深入浅出8大设计模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文主要探讨前端开发中的各种设计模式,主要分类有:

  • 单例模式
  • 建造者模式
  • 代理模式
  • 装饰器模式
  • 适配器模式
  • 策略模式
  • 观察者模式
  • 发布订阅模式

通过对他们实际开发中的使用场景的解析,深入浅出的一起更全面直观的进行学习:

一、单例模式

介绍

单例模式确保一个类只有一个实例,并提供一个全局访问点。

实际使用场景

实现全局唯一的状态管理,如全局配置对象、日志记录器等。

优点

  • 减少系统资源开销,因为只创建一个实例。
  • 提供全局访问点,方便在不同部分的代码中使用。

缺点

  • 违反单一职责原则,因为单例类可能承担过多的职责。
  • 可能会导致代码的紧耦合,因为其他部分的代码都依赖于这个单例。

代码实现1

class Singleton {private static instance: Singleton;private constructor() {}public static getInstance(): Singleton {if (!Singleton.instance) {Singleton.instance = new Singleton();}return Singleton.instance;}public someMethod(): void {console.log('Singleton method called.');}
}const singleton1 = Singleton.getInstance();
const singleton2 = Singleton.getInstance();
console.log(singleton1 === singleton2); // true

通过静态方法 getInstance 来获取唯一的实例。如果实例不存在则创建一个新的实例并保存起来,下次调用时直接返回已有的实例。

代码实例2

element-ui对于全局loading的处理,使用的就是单例模式进行控制,每次只能触发一个全局loading

let fullscreenLoading;
const loading = (options = {}) =>{	// options不传的话默认是fullscreenoptions = merge({}, defaults, options);if(options.fullscreen && fullscreenLoading){return fullscreenLoading; // 存在直接return}let parent = options.body? document.body: options.target;let instance = new LoadingConstrutor({el: document.createElement('div')});if (options.fullscreen) {fullscreenLoading = instance}return instance
}

这样通过Elementloading的时候,如果同时调用两次,只有一个loading的遮罩层,不会有两个

二、建造者模式

介绍

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。目的是为了生成对象,把复杂的创建过程从构造函数分离出来,然后在不改变原有构造函数的基础上,创建各种各样的对象。

实际使用场景

构建复杂的表单对象、配置对象等。

优点

  • 使得对象的创建过程更加清晰,易于理解和维护。
  • 可以方便地创建不同表示的对象,提高了代码的灵活性。

缺点

  • 增加了代码的复杂性,需要创建多个类来实现建造者模式。
  • 对于简单的对象创建,可能会显得过于繁琐。

代码实现

class Product {parts: string[] = [];addPart(part: string): void {this.parts.push(part);}
}class Builder {buildPartA(): void {// 构建部分 A 的逻辑}buildPartB(): void {// 构建部分 B 的逻辑}getResult(): Product {const product = new Product();product.addPart('Part A');product.addPart('Part B');return product;}
}const builder = new Builder();
const product = builder.getResult();
console.log(product.parts);

Builder 类负责构建复杂对象 Product,通过不同的方法逐步构建产品的各个部分,最后返回构建好的产品。

如果我们创建一个函数,参数不一定,为了方便以后的扩展,可以使用建造者模式,参数中传入默认值的方法:

function getPhone({size,screen = 'OLED',price = 100,discount = 10
}={}){console.log('size', size);console.log('screen', screen);console.log('price', price);console.log('discount', discount);
}
getPhone({size: 4, price: 200})

上面代码中通过传入参数默认值,很方便的设置参数,并且参数的顺序也不重要,未来在扩展的时候不需要担心太多地方调用传参是否会引起问题。

三、代理模式

介绍

为其他对象提供一种代理以控制对这个对象的访问。

实际使用场景

  • 图片懒加载、数据预加载。
  • 权限控制等。

优点

  • 可以在不改变目标对象的情况下,为其添加额外的功能。
  • 可以控制对目标对象的访问,提高安全性和性能。

缺点

  • 增加了代码的复杂性,需要创建代理对象。
  • 可能会影响性能,因为代理对象需要进行额外的处理。

代码实现

class RealImage {loadImage(): void {console.log('Loading real image.');}
}class ImageProxy {private realImage: RealImage | null = null;loadImage(): void {if (!this.realImage) {this.realImage = new RealImage();}this.realImage.loadImage();}
}const proxy = new ImageProxy();
proxy.loadImage();

ImageProxy 代理了 RealImage 的加载操作,在实际需要加载图像时才创建真正的图像对象,实现了延迟加载。

四、装饰器模式

介绍

动态地给一个对象添加一些额外的职责,而不改变其结构。

实际使用场景

  • 为函数或类添加日志记录、性能监控等功能。

优点

  • 可以在不改变原有代码结构的情况下,为对象添加新的功能。
  • 可以方便地组合多个装饰器,实现更复杂的功能。

缺点

  • 装饰器的使用可能会使代码变得难以理解,尤其是当装饰器嵌套过多时。
  • 可能会增加代码的复杂性,因为需要创建装饰器类。

代码实现

function logDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = function(...args: any[]) {console.log(`Calling method ${propertyKey}`);const result = originalMethod.apply(this, args);console.log(`Method ${propertyKey} finished`);return result;};return descriptor;
}class MyClass {@logDecoratormyMethod(): void {console.log('Inside myMethod.');}
}const myObj = new MyClass();
myObj.myMethod();

装饰器函数 logDecorator 被安装到myMethod方案上,在myMethod调用前后添加了日志记录功能。

五、适配器模式

介绍

将一个类的接口转换成客户希望的另外一个接口。

实际使用场景

  • 不同库之间的接口适配。
  • 旧系统与新系统的接口整合等。

优点

  • 提高了代码的复用性,使得不同接口的类可以协同工作。
  • 可以将复杂的接口转换为简单的接口,方便使用。

缺点

  • 增加了代码的复杂性,需要创建适配器类。
  • 可能会降低系统的性能,因为适配器需要进行额外的处理。

代码实现

class OldLibrary {oldMethod(): string {return 'Old library output';}
}class Adapter {private oldLibrary: OldLibrary;constructor() {this.oldLibrary = new OldLibrary();}newMethod(): string {return `Adapted: ${this.oldLibrary.oldMethod()}`;}
}const adapter = new Adapter();
console.log(adapter.newMethod());

Adapter 类将旧库的接口转换为新的接口,使得可以在新的代码中使用旧库的功能。

六、策略模式

介绍

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

实际使用场景

  • 表单验证、排序算法选择等。
  • 当出现很多if-else或者switch时可以考虑使用策略模式

优点

  • 易于扩展和维护,当需要添加新的算法时,只需要创建一个新的策略类。
  • 可以在运行时动态地切换算法。

缺点

  • 增加了代码的复杂性,需要创建多个策略类。
  • 客户端需要了解不同的策略,增加了客户端的复杂度。

代码实现

interface Strategy {execute(): void;
}class ConcreteStrategyA implements Strategy {execute(): void {console.log('Executing strategy A.');}
}class ConcreteStrategyB implements Strategy {execute(): void {console.log('Executing strategy B.');}
}class Context {private strategy: Strategy;constructor(strategy: Strategy) {this.strategy = strategy;}setStrategy(strategy: Strategy): void {this.strategy = strategy;}executeStrategy(): void {this.strategy.execute();}
}const context = new Context(new ConcreteStrategyA());
context.executeStrategy();
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy();

Context 类根据不同的策略对象执行不同的算法,通过设置不同的策略可以在运行时动态切换算法。

观察者模式

介绍

定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知并自动更新。

实际使用场景

  • 事件处理、状态管理等。

优点

  • 实现了松耦合,观察者和被观察者之间的依赖关系很弱。
  • 可以方便地添加和删除观察者。

缺点

  • 当观察者数量较多时,通知所有观察者可能会导致性能问题。
  • 观察者模式可能会导致循环依赖的问题。

代码实现

class Subject {private observers: Observer[] = [];addObserver(observer: Observer): void {this.observers.push(observer);}removeObserver(observer: Observer): void {this.observers = this.observers.filter(obs => obs!== observer);}notifyObservers(): void {this.observers.forEach(observer => observer.update());}
}interface Observer {update(): void;
}class ConcreteObserver implements Observer {update(): void {console.log('Observer notified.');}
}const subject = new Subject();
const observer1 = new ConcreteObserver();
const observer2 = new ConcreteObserver();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifyObservers();

Subject 维护一组观察者,当状态变化时通知所有观察者进行更新。

八、发布订阅模式

介绍

发布者和订阅者之间通过事件进行通信,发布者发布事件,订阅者订阅感兴趣的事件并在事件发生时做出响应。

实际使用场景

  • 消息通知、事件总线等。

优点

  • 实现了松耦合,发布者和订阅者之间不需要直接知道对方的存在。
  • 可以方便地添加和删除订阅者。

缺点

  • 当事件过多时,管理事件可能会变得复杂。
  • 可能会导致内存泄漏,如果订阅者没有正确地取消订阅。

代码实现

class EventEmitter {private events: { [eventName: string]: ((...args: any[]) => void)[] } = {};on(eventName: string, callback: (...args: any[]) => void): void {if (!this.events[eventName]) {this.events[eventName] = [];}this.events[eventName].push(callback);}emit(eventName: string,...args: any[]): void {if (this.events[eventName]) {this.events[eventName].forEach(callback => callback(...args));}}
}const eventEmitter = new EventEmitter();
eventEmitter.on('eventName', (data) => {console.log(`Received event with data: ${data}`);
});
eventEmitter.emit('eventName', 'Some data');

EventEmitter 类提供了订阅和发布事件的方法,订阅者通过 on 方法订阅事件,发布者通过 emit 方法发布事件,触发订阅者的回调函数。

这篇关于前端宝典十六:深入浅出8大设计模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102593

相关文章

从入门到精通详解LangChain加载HTML内容的全攻略

《从入门到精通详解LangChain加载HTML内容的全攻略》这篇文章主要为大家详细介绍了如何用LangChain优雅地处理HTML内容,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录引言:当大语言模型遇见html一、HTML加载器为什么需要专门的HTML加载器核心加载器对比表二

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

HTML中meta标签的常见使用案例(示例详解)

《HTML中meta标签的常见使用案例(示例详解)》HTMLmeta标签用于提供文档元数据,涵盖字符编码、SEO优化、社交媒体集成、移动设备适配、浏览器控制及安全隐私设置,优化页面显示与搜索引擎索引... 目录html中meta标签的常见使用案例一、基础功能二、搜索引擎优化(seo)三、社交媒体集成四、移动

HTML input 标签示例详解

《HTMLinput标签示例详解》input标签主要用于接收用户的输入,随type属性值的不同,变换其具体功能,本文通过实例图文并茂的形式给大家介绍HTMLinput标签,感兴趣的朋友一... 目录通用属性输入框单行文本输入框 text密码输入框 password数字输入框 number电子邮件输入编程框

HTML img标签和超链接标签详细介绍

《HTMLimg标签和超链接标签详细介绍》:本文主要介绍了HTML中img标签的使用,包括src属性(指定图片路径)、相对/绝对路径区别、alt替代文本、title提示、宽高控制及边框设置等,详细内容请阅读本文,希望能对你有所帮助... 目录img 标签src 属性alt 属性title 属性width/h

CSS3打造的现代交互式登录界面详细实现过程

《CSS3打造的现代交互式登录界面详细实现过程》本文介绍CSS3和jQuery在登录界面设计中的应用,涵盖动画、选择器、自定义字体及盒模型技术,提升界面美观与交互性,同时优化性能和可访问性,感兴趣的朋... 目录1. css3用户登录界面设计概述1.1 用户界面设计的重要性1.2 CSS3的新特性与优势1.

HTML5 中的<button>标签用法和特征

《HTML5中的<button>标签用法和特征》在HTML5中,button标签用于定义一个可点击的按钮,它是创建交互式网页的重要元素之一,本文将深入解析HTML5中的button标签,详细介绍其属... 目录引言<button> 标签的基本用法<button> 标签的属性typevaluedisabled

HTML5实现的移动端购物车自动结算功能示例代码

《HTML5实现的移动端购物车自动结算功能示例代码》本文介绍HTML5实现移动端购物车自动结算,通过WebStorage、事件监听、DOM操作等技术,确保实时更新与数据同步,优化性能及无障碍性,提升用... 目录1. 移动端购物车自动结算概述2. 数据存储与状态保存机制2.1 浏览器端的数据存储方式2.1.

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,