工作常用设计模式

概述

设计模式作为软件工程中解决特定问题的经典方案,虽然设计模式很多,对于前端开发来说,实际业务工程项目中常用的就那么几个,见下。

1. 单例模式 (Singleton Pattern)

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

应用场景:全局状态管理、模态框、提示组件等

javascript 复制代码
class AuthService {
  constructor() {
    if (AuthService.instance) {
      return AuthService.instance;
    }
    this.token = null;
    this.user = null;
    AuthService.instance = this;
  }

  login(token, user) {
    this.token = token;
    this.user = user;
  }

  logout() {
    this.token = null;
    this.user = null;
  }

  isAuthenticated() {
    return !!this.token;
  }
}

// 使用示例
const auth1 = new AuthService();
const auth2 = new AuthService();
console.log(auth1 === auth2); // true

auth1.login('token123', { name: 'John' });
console.log(auth2.isAuthenticated()); // true

2. 发布-订阅模式

观察者模式的变体,通过消息通道进行通信,发布者和订阅者不需要知道彼此的存在。

javascript 复制代码
class PubSub {
  constructor() {
    this.topics = {};
    this.subId = 0;
  }

  subscribe(topic, callback) {
    if (!this.topics[topic]) {
      this.topics[topic] = {};
    }
    
    const id = ++this.subId;
    this.topics[topic][id] = callback;
    
    return {
      unsubscribe: () => {
        delete this.topics[topic][id];
        if (Object.keys(this.topics[topic]).length === 0) {
          delete this.topics[topic];
        }
      }
    };
  }

  publish(topic, data) {
    if (!this.topics[topic]) return;
    
    Object.values(this.topics[topic]).forEach(callback => {
      callback(data);
    });
  }
}

// 使用示例
const pubsub = new PubSub();

// 订阅
const subscription = pubsub.subscribe('user.login', (user) => {
  console.log('用户登录:', user);
});

// 发布
pubsub.publish('user.login', { name: 'Alice', id: 1 });

// 取消订阅
subscription.unsubscribe();

3. 工厂模式

创建对象而不暴露创建逻辑,通过工厂方法来创建对象。

应用场景:复杂对象创建、根据不同条件创建不同实例

javascript 复制代码
class Dialog {
  constructor(type, content) {
    this.type = type;
    this.content = content;
  }

  show() {
    console.log(`显示${this.type}对话框: ${this.content}`);
  }
}

class DialogFactory {
  static createDialog(type, content) {
    switch (type) {
      case 'success':
        return new Dialog('成功', content);
      case 'error':
        return new Dialog('错误', content);
      case 'warning':
        return new Dialog('警告', content);
      default:
        throw new Error('未知的对话框类型');
    }
  }
}

// 使用示例
const successDialog = DialogFactory.createDialog('success', '操作成功!');
const errorDialog = DialogFactory.createDialog('error', '操作失败!');

successDialog.show(); // 显示成功对话框: 操作成功!
errorDialog.show();   // 显示错误对话框: 操作失败!

4. 策略模式

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

应用场景:表单验证、支付方式等

javascript 复制代码
// 策略类
const validationStrategies = {
  isRequired: (value) => ({
    isValid: value !== undefined && value !== null && value !== '',
    message: '该字段为必填项'
  }),
  
  isEmail: (value) => ({
    isValid: /^[^\s@]+@[^\s@]+.[^\s@]+$/.test(value),
    message: '请输入有效的邮箱地址'
  }),
  
  minLength: (value, length) => ({
    isValid: value.length >= length,
    message: `长度不能少于${length}个字符`
  }),
  
  maxLength: (value, length) => ({
    isValid: value.length <= length,
    message: `长度不能超过${length}个字符`
  })
};

// 验证器类
class Validator {
  constructor() {
    this.rules = [];
  }

  addRule(field, strategy, ...params) {
    this.rules.push({ field, strategy, params });
    return this;
  }

  validate(data) {
    const errors = {};
    
    this.rules.forEach(rule => {
      const value = data[rule.field];
      const result = validationStrategies[rule.strategy](value, ...rule.params);
      
      if (!result.isValid) {
        if (!errors[rule.field]) {
          errors[rule.field] = [];
        }
        errors[rule.field].push(result.message);
      }
    });
    
    return {
      isValid: Object.keys(errors).length === 0,
      errors
    };
  }
}

// 使用示例
const validator = new Validator()
  .addRule('username', 'isRequired')
  .addRule('username', 'minLength', 3)
  .addRule('email', 'isRequired')
  .addRule('email', 'isEmail');

const data = {
  username: 'ab',
  email: 'invalid-email'
};

const result = validator.validate(data);
console.log(result);

5. 装饰器模式

动态地给对象添加额外的职责,而不改变对象本身。

应用场景:功能扩展、高阶组件、vue2类组件。

javascript 复制代码
// 基础组件
class Component {
  render() {
    return '基础组件';
  }
}

// 装饰器基类
class Decorator {
  constructor(component) {
    this.component = component;
  }

  render() {
    return this.component.render();
  }
}

// 具体装饰器
class BorderDecorator extends Decorator {
  render() {
    return `带有边框的(${super.render()})`;
  }
}

class ColorDecorator extends Decorator {
  constructor(component, color) {
    super(component);
    this.color = color;
  }

  render() {
    return `<span style="color: ${this.color}">${super.render()}</span>`;
  }
}

// 使用示例
let component = new Component();
component = new BorderDecorator(component);
component = new ColorDecorator(component, 'red');

console.log(component.render());
// <span style="color: red">带有边框的(基础组件)</span>

6. 适配器模式

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

应用场景:API兼容、第三方库适配、数据格式转换

javascript 复制代码
// 老版本API
class OldAPI {
  request(data) {
    return `老API响应: ${JSON.stringify(data)}`;
  }
}

// 新版本API(期望的接口)
class NewAPI {
  fetch(options) {
    return `新API响应: ${options.url} - ${JSON.stringify(options.body)}`;
  }
}

// 适配器
class APIAdapter {
  constructor(oldAPI) {
    this.oldAPI = oldAPI;
  }

  fetch(options) {
    // 将新API的调用方式适配到老API
    const oldStyleData = {
      method: options.method || 'GET',
      path: options.url,
      body: options.body
    };
    
    return this.oldAPI.request(oldStyleData);
  }
}

// 使用示例
const oldAPI = new OldAPI();
const adapter = new APIAdapter(oldAPI);

// 使用新API的接口调用老API的功能
const result = adapter.fetch({
  url: '/api/users',
  method: 'POST',
  body: { name: 'John' }
});

console.log(result);

7. 代理模式

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

应用场景:缓存代理、权限控制、虚拟代理、数据验证

javascript 复制代码
// 真实服务
class ImageService {
  constructor(filename) {
    this.filename = filename;
    this.loadImage(); // 模拟耗时的图片加载
  }

  loadImage() {
    console.log(`加载图片: ${this.filename}`);
  }

  display() {
    console.log(`显示图片: ${this.filename}`);
  }
}

// 代理
class ImageProxy {
  constructor(filename) {
    this.filename = filename;
    this.realService = null;
  }

  display() {
    if (!this.realService) {
      this.realService = new ImageService(this.filename);
    }
    this.realService.display();
  }
}

// 使用示例
const image1 = new ImageProxy('photo1.jpg');
const image2 = new ImageProxy('photo2.jpg');

// 只有在真正需要显示时才加载图片
image1.display(); // 第一次调用会加载图片
image1.display(); // 第二次直接使用已加载的图片
相关推荐
雨季66617 分钟前
Flutter 三端应用实战:OpenHarmony 简易“动态内边距调节器”交互模式深度解析
javascript·flutter·ui·交互·dart
天人合一peng31 分钟前
Unity中button 和toggle监听事件函数有无参数
前端·unity·游戏引擎
会飞的战斗鸡1 小时前
JS中的链表(含leetcode例题)
javascript·leetcode·链表
方也_arkling1 小时前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐1 小时前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
qq_177767372 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_177767372 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体
web打印社区2 小时前
web-print-pdf:突破浏览器限制,实现专业级Web静默打印
前端·javascript·vue.js·electron·html
RFCEO2 小时前
前端编程 课程十三、:CSS核心基础1:CSS选择器
前端·css·css基础选择器详细教程·css类选择器使用方法·css类选择器命名规范·css后代选择器·精准选中嵌套元素
烬头88212 小时前
React Native鸿蒙跨平台采用了函数式组件的形式,通过 props 接收分类数据,使用 TouchableOpacity实现了点击交互效果
javascript·react native·react.js·ecmascript·交互·harmonyos