您现在的位置是:网站首页 > 遗留系统向设计模式演进策略文章详情

遗留系统向设计模式演进策略

遗留系统在长期迭代中往往积累了大量的技术债务,代码结构混乱且难以维护。设计模式为解决这类问题提供了系统化的思路,通过合理的重构策略能够逐步改善代码质量。JavaScript作为动态语言,其灵活性使得模式应用具有独特优势,但也需要特别注意实现细节。

遗留系统的典型特征分析

典型的JavaScript遗留系统通常表现出以下特征:

  1. 全局状态泛滥:大量使用window对象存储状态变量
window.currentUser = { name: '匿名' };
window.appConfig = { debug: true };
  1. 巨型函数:单个函数超过300行代码,承担多个职责
  2. 回调地狱:多层嵌套的异步回调结构
fs.readFile('a.txt', (err, data1) => {
  db.query('SELECT * FROM users', (err, data2) => {
    api.post('/log', (err, data3) => {
      // 业务逻辑混杂在这里
    });
  });
});
  1. 类型混乱:随意变更的对象结构和不一致的接口约定

重构的基本原则与准备

开始重构前必须建立安全网:

  1. 测试覆盖率:至少达到60%的核心业务逻辑覆盖
  2. 代码度量:使用ESLint配置复杂度规则
{
  "rules": {
    "complexity": ["error", 5],
    "max-depth": ["error", 3]
  }
}
  1. 渐进式改进:通过特性开关控制重构范围
// 特性开关示例
const useNewPaymentSystem = false;

function processPayment() {
  return useNewPaymentSystem 
    ? new PaymentProcessor().execute()
    : legacyPaymentHandler();
}

创建型模式的应用策略

工厂方法改造构造函数

将直接的对象创建改为工厂方法:

// 改造前
function createDialog(type) {
  if (type === 'alert') {
    return new AlertDialog();
  } else {
    return new ConfirmDialog();
  }
}

// 改造后
class DialogFactory {
  static create(type) {
    const creators = {
      alert: () => new AlertDialog(),
      confirm: () => new ConfirmDialog()
    };
    return creators[type]?.() ?? new DefaultDialog();
  }
}

单例模式管理全局状态

逐步替换window全局变量:

// 状态管理单例
class AppState {
  static #instance;
  #config = {};
  
  constructor() {
    if (!AppState.#instance) {
      AppState.#instance = this;
    }
    return AppState.#instance;
  }
  
  setConfig(config) {
    this.#config = Object.freeze({...this.#config, ...config});
  }
  
  get config() {
    return {...this.#config};
  }
}

// 使用方式
const state = new AppState();
state.setConfig({ theme: 'dark' });

结构型模式的重构实践

适配器模式整合老旧接口

包装遗留代码提供统一接口:

// 旧版API
class OldService {
  fetchData(callback) {
    setTimeout(() => callback([1,2,3]), 500);
  }
}

// 适配器
class ServiceAdapter {
  constructor(oldService) {
    this.service = oldService;
  }
  
  async getData() {
    return new Promise(resolve => {
      this.service.fetchData(resolve);
    });
  }
}

// 使用示例
const adapter = new ServiceAdapter(new OldService());
const data = await adapter.getData();

装饰器模式增强功能

不修改原函数的情况下添加功能:

// 原始函数
function saveToDB(data) {
  console.log(`保存数据: ${JSON.stringify(data)}`);
}

// 装饰器工厂
function withLogging(fn) {
  return function(...args) {
    console.log(`调用 ${fn.name} 参数:`, args);
    const result = fn.apply(this, args);
    console.log(`调用完成`);
    return result;
  };
}

// 使用装饰器
const enhancedSave = withLogging(saveToDB);
enhancedSave({ user: 'admin' });

行为型模式的系统改造

观察者模式解耦事件处理

替换事件回调的硬编码:

// 事件总线实现
class EventBus {
  constructor() {
    this.subscribers = {};
  }
  
  subscribe(event, callback) {
    if (!this.subscribers[event]) {
      this.subscribers[event] = [];
    }
    this.subscribers[event].push(callback);
  }
  
  publish(event, data) {
    (this.subscribers[event] || []).forEach(cb => cb(data));
  }
}

// 应用场景
const bus = new EventBus();
bus.subscribe('user.login', user => {
  console.log(`用户登录: ${user.name}`);
});

// 触发事件
bus.publish('user.login', { name: 'Alice' });

策略模式替换条件分支

消除复杂的switch-case结构:

// 改造前
function calculateBonus(level, salary) {
  switch(level) {
    case 'S': return salary * 0.2;
    case 'A': return salary * 0.1;
    default: return salary * 0.05;
  }
}

// 改造后
const bonusStrategies = {
  S: salary => salary * 0.2,
  A: salary => salary * 0.1,
  default: salary => salary * 0.05
};

function calculateBonus(level, salary) {
  const strategy = bonusStrategies[level] || bonusStrategies.default;
  return strategy(salary);
}

异步流程的现代化改造

Promise包装回调函数

将回调式API转为Promise:

// 旧式回调API
function legacyFetch(url, callback) {
  // 模拟异步操作
  setTimeout(() => {
    callback(null, { data: '响应数据' });
  }, 100);
}

// Promise包装器
function promisify(fn) {
  return function(...args) {
    return new Promise((resolve, reject) => {
      fn(...args, (err, result) => {
        err ? reject(err) : resolve(result);
      });
    });
  };
}

// 使用示例
const modernFetch = promisify(legacyFetch);
modernFetch('/api/data').then(console.log);

async/await重构流程控制

优化异步代码结构:

// 改造前
function fetchUserData(userId, callback) {
  getUser(userId, (err, user) => {
    if (err) return callback(err);
    getOrders(user.id, (err, orders) => {
      if (err) return callback(err);
      getPayments(orders[0].id, (err, payments) => {
        callback(null, { user, orders, payments });
      });
    });
  });
}

// 改造后
async function fetchUserData(userId) {
  const user = await getUser(userId);
  const orders = await getOrders(user.id);
  const payments = await getPayments(orders[0].id);
  return { user, orders, payments };
}

模块化与依赖管理

逐步替换全局依赖

使用模块系统隔离依赖:

// 旧式全局jQuery
$('#btn').click(() => {
  $.ajax({ /* ... */ });
});

// 改造为模块化引入
import $ from 'jquery';

function initButton() {
  $('#btn').click(/* ... */);
}

// 进一步替换为现代方案
document.getElementById('btn').addEventListener('click', async () => {
  const res = await fetch('/api');
});

依赖注入改善测试

解耦模块间的硬依赖:

// 硬编码依赖
class UserService {
  constructor() {
    this.api = new AuthAPI();
  }
}

// 依赖注入改造
class UserService {
  constructor(api) {
    this.api = api;
  }
}

// 测试用例
const mockAPI = { login: jest.fn() };
const service = new UserService(mockAPI);

性能优化与模式选择

享元模式减少内存消耗

优化大量相似对象的创建:

// 对象池实现
class TexturePool {
  static #pool = new Map();
  
  static getTexture(name) {
    if (!this.#pool.has(name)) {
      this.#pool.set(name, this.loadTexture(name));
    }
    return this.#pool.get(name);
  }
  
  static loadTexture(name) {
    console.log(`加载纹理: ${name}`);
    return { name, data: new ArrayBuffer(1024) };
  }
}

// 使用方式
const texture1 = TexturePool.getTexture('grass');
const texture2 = TexturePool.getTexture('grass');  // 复用实例

代理模式控制资源访问

添加访问控制和缓存层:

// 原始服务
class DataService {
  fetchData(id) {
    console.log(`请求数据: ${id}`);
    return { id, value: Math.random() };
  }
}

// 代理实现
class DataProxy {
  constructor(service) {
    this.service = service;
    this.cache = new Map();
  }
  
  fetchData(id) {
    if (this.cache.has(id)) {
      return this.cache.get(id);
    }
    const data = this.service.fetchData(id);
    this.cache.set(id, data);
    return data;
  }
}

// 使用代理
const proxy = new DataProxy(new DataService());
proxy.fetchData(1);  // 实际请求
proxy.fetchData(1);  // 返回缓存

测试策略与模式验证

模拟对象测试复杂交互

使用测试替身验证模式实现:

// 待测试的服务
class NotificationService {
  constructor(messenger) {
    this.messenger = messenger;
  }
  
  notifyAll(users, message) {
    users.forEach(user => {
      this.messenger.send(user.id, message);
    });
  }
}

// 测试用例
test('应该给所有用户发送通知', () => {
  const mockMessenger = {
    send: jest.fn()
  };
  const service = new NotificationService(mockMessenger);
  const users = [{id: 1}, {id: 2}];
  
  service.notifyAll(users, '测试消息');
  
  expect(mockMessenger.send).toHaveBeenCalledTimes(2);
  expect(mockMessenger.send).toHaveBeenCalledWith(1, '测试消息');
});

模式的有效性度量

建立量化评估指标:

  1. 圈复杂度变化:使用ESLint的complexity规则
  2. 依赖关系数量:通过Madge等工具生成依赖图
  3. 变更影响范围:统计修改单个模块影响的测试用例数
  4. 内存使用情况:Chrome DevTools的内存分析

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步