前端中的设计模式(真实场景例子)

追风少年 | 465 | 2023-11-30

本篇内容适合对设计模式有些了解,但想更进一步了解设计模式在实际场景应用的读者,对于初学者,推荐以前带我入门设计模式的《JavaScript设计模式与开发实践》这本书。

一、设计模式原则

  1. 单一职责原则:一个函数只做一件事,如果功能过于复杂就拆分开
  2. 开发/封闭原则:对扩展开放,对修改封闭;增加需求时,扩展新代码,而非修改已有代码
  3. 里氏替换原则:子类能覆盖父类
  4. 接口隔离原则:保持接口的单一独立
  5. 依赖倒转原则:面向接口编程,依赖于抽象而不依赖于具体;使用方只关注接口而不关注具体类的实现

二、创建型设计模式

1、工厂模式

实质:一个工厂对象负责创建其他对象,而具体的创建逻辑由子类决定

应用:根据参数,调用对应的插件

// 统计插件 class AnalyticsPlugin { init() { console.log("初始化统计插件"); } } // 日志插件 class LoggerPlugin { init() { console.log("初始化日志插件"); } } // 工厂对象 class PluginFactory { createPlugin(types) { types.forEach((type) => { switch (type) { case "analytics": this.init(AnalyticsPlugin); break; case "logger": this.init(LoggerPlugin); break; default: throw new Error("Invalid plugin type."); } }); } //初始化插件 init(Plugin) { const plugin = new Plugin(); plugin.init(); } } // 使用示例 const pluginFactory = new PluginFactory(); //初始化统计插件,初始化日志插件 pluginFactory.createPlugin(["analytics", "logger"]);

2、单例模式

实质:一个类只有一个实例

应用:全局状态管理器vuex和redux,单例弹窗

// 实现单体模式弹窗 var createWindow = (function () { var div; return function () { if (!div) { div = document.createElement("div"); div.innerHTML = "我是弹窗内容"; div.style.display = "none"; document.body.appendChild(div); } return div; }; })(); document.getElementById("btn").onclick = function () { var win = createWindow(); win.style.display = "block"; };

3、原型模式

实质:通过复制现有对象来创建新对象

应用:JavaScript的原型

function Shape() { this.name = "Shape"; } Shape.prototype.draw = function () { console.log(this.name + " is drawing..."); }; function Rectangle() { Shape.call(this); this.name = "Rectangle"; } Rectangle.prototype = Object.create(Shape.prototype); Rectangle.prototype.constructor = Rectangle; function Circle() { Shape.call(this); this.name = "Circle"; } Circle.prototype = Object.create(Shape.prototype); Circle.prototype.constructor = Circle; // 测试Rectangle和Circle的继承关系 let rectangle = new Rectangle(); let circle = new Circle(); rectangle.draw(); // 输出 "Rectangle is drawing..." circle.draw(); // 输出 "Circle is drawing..."

三、结构型设计模式

1、适配器模式

实质:将一个类的接口转换成客户端所期望的另一个接口。

应用:vue的computed,react的useMemo

computed:

<template> <div> {{ message }} {{ reversedMessage }} </div> </template> <script setup lang="ts"> import { computed, ref } from "vue"; const message = ref("hello vue"); const reversedMessage = computed(() => message.value.split("").reverse().join("") ); </script> useMemo: import { useMemo, useState } from "react"; function App() { const [message] = useState("hello react"); const reversedMessage = useMemo( () => message.split("").reverse().join(""), [message] ); return ( <div className="App"> {message} {reversedMessage} </div> ); } export default App;

2、装饰器模式

实质:动态地给对象添加一些额外的功能

应用:React的高阶组件(HOC

import { useState, useEffect } from "react"; //高阶组件 function withLoading(WrappedComponent) { return function ({ isLoading, ...props }) { if (isLoading) { return <div>Loading...</div>; } else { return <WrappedComponent {...props} />; } }; } //业务组件 function DataList({ data }) { return ( <ul> {data.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> ); } const DataListWithLoading = withLoading(DataList); function App() { const [data, setData] = useState([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { setTimeout(() => { setData([1, 2, 3]); setIsLoading(false); }, 2000); }, []); return ( <div> <h1>Data List</h1> <DataListWithLoading data={data} isLoading={isLoading} /> </div> ); } export default App;

3、代理模式

实质:创建一个代理对象来控制对另一个对象的访问

应用:使用ES6的Proxy

let obj = { a: 1, b: 2, }; let proxy = new Proxy(obj, { get(target, key, receiver) { console.log("监听get"); return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { console.log("触发set"); Reflect.set(target, key, value, receiver); }, deleteProperty(target, key) { console.log("监听删除"); Reflect.deleteProperty(target, key); //如果抛出错误或者返回false,当前属性就无法被delete命令删除 return true; }, has(target, key) { console.log("监听has"); return Reflect.has(target, key); }, }); proxy.a; //监听get proxy.a = 4; //触发set delete proxy.b; //监听删除 "a" in proxy; //监听has

四、行为型设计模式

1、观察者模式

实质:一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知

应用:比起观察者模式,更常用发布-订阅模式实现跨组件通信

class Observer { constructor() { this.all = new Map(); } on(type, handler) { const handlers = this.all.get(type); if (handlers) { handlers.push(handler); } else { this.all.set(type, [handler]); } } off(type, handler) { const handlers = this.all.get(type); if (handlers) { if (handler) { const i = handlers.indexOf(handler); if (i > -1) { handlers.splice(i, 1); } } else { this.all.set(type, []); } } } emit(type, args) { const handlers = this.all.get(type); if (handlers) { handlers.forEach((handler) => handler(args)); } } }

2、策略模式

实质:根据不同参数可以命中不同的策略

应用:在对象中定义一系列算法,随取随用

//表单校验 var strategies = { /* 不为空 */ isNonEmpty:function(value,errorMsg){ if(value === ''){ return errorMsg } }, /* 限制最小长度 */ minLength:function(value,length,errorMsg){ if(value.length < length){ return errorMsg } }, /* 手机号码格式 */ isMobile:function(value,errorMsg){ let rule = /^1[3|5|8][0-9]{9}$/ if(!rule.test(value)){ return errorMsg } }, }

3、模板方法模式

实质:父类定义公共的行为和逻辑,在子类中实现具体的细节

应用:抽象类

//抽象类 abstract class DefinePlugin { public name: string; constructor(name: string) { this.name = name; } //抽象方法 abstract monitor(): void; } //子类 class BehaviorPlugin extends DefinePlugin { constructor() { super("behavior"); } //定义具体的实现 monitor(): void { ["click"].forEach(function (eventType) { document.addEventListener( eventType, (e) => { //处理点击事件 }, true ); }); } }

4、责任链模式

实质:允许对象在链上依次处理请求

应用:数据验证

/* 验证url */ class UrlValidator { setNext(validator) { this.nextValidator = validator; } validate(data) { if (!data.url) { console.error("url不存在"); return false; } else if (this.nextValidator) { return this.nextValidator.validate(data); } else { return true; } } } /* 验证缓存数量 */ class NumberValidator { setNext(validator) { this.nextValidator = validator; } validate(data) { if (!data.count) { console.error("请输入数量"); return false; } else if (data.count && data.count < 0) { console.error("请输入非负数"); } else if (this.nextValidator) { return this.nextValidator.validate(data); } else { return true; } } } /* 责任链模式 */ const urlValidator = new UrlValidator(); const maxValidator = new NumberValidator(); /* 指定节点在责任链中的顺序 */ urlValidator.setNext(maxValidator); urlValidator.validate({ url: "1", count: -1, //请输入非负数 });

5、中介者模式

实质:对象和对象之间借助第三方中介者进行通信

应用:聊天室

/* 中介者 */ class Mediator { constructor() { this.list = []; } add(data) { this.list.push(data); } /* 广播事件 */ broadcast(source, message) { this.list.filter((o) => o !== source).forEach((o) => o.receive(message)); } } const mediator = new Mediator(); class User { constructor() { this.mediator = mediator; this.mediator.add(this); } send(message) { this.mediator.broadcast(this, message); } receive(message) { console.log(`Received message: ${message}`); } } // 使用中介者模式进行组件之间的通信 const user1 = new User(); const user2 = new User(); user1.send("Hello from User 1"); user2.send("Hi from User 2");

这里只是展示中介者示例,实际项目中,可借助WebSocket的广播事件

推荐指数:

转载声明:

原作者: 敲代码的彭于晏

转载自: 《前端中的设计模式(真实场景例子)》,如有侵权,请联系本站删除。

真诚点赞,手留余香

前端中的设计模式(真实场景例子)

关于作者 📝

追风少年

这个人很懒~

等级 LV4

粉丝 1

获赞 7

经验 606