您现在的位置是:网站首页 > 遗留系统向设计模式演进策略文章详情
遗留系统向设计模式演进策略
陈川
【
JavaScript
】
53656人已围观
7487字
遗留系统在长期迭代中往往积累了大量的技术债务,代码结构混乱且难以维护。设计模式为解决这类问题提供了系统化的思路,通过合理的重构策略能够逐步改善代码质量。JavaScript作为动态语言,其灵活性使得模式应用具有独特优势,但也需要特别注意实现细节。
遗留系统的典型特征分析
典型的JavaScript遗留系统通常表现出以下特征:
- 全局状态泛滥:大量使用
window
对象存储状态变量
window.currentUser = { name: '匿名' };
window.appConfig = { debug: true };
- 巨型函数:单个函数超过300行代码,承担多个职责
- 回调地狱:多层嵌套的异步回调结构
fs.readFile('a.txt', (err, data1) => {
db.query('SELECT * FROM users', (err, data2) => {
api.post('/log', (err, data3) => {
// 业务逻辑混杂在这里
});
});
});
- 类型混乱:随意变更的对象结构和不一致的接口约定
重构的基本原则与准备
开始重构前必须建立安全网:
- 测试覆盖率:至少达到60%的核心业务逻辑覆盖
- 代码度量:使用ESLint配置复杂度规则
{
"rules": {
"complexity": ["error", 5],
"max-depth": ["error", 3]
}
}
- 渐进式改进:通过特性开关控制重构范围
// 特性开关示例
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, '测试消息');
});
模式的有效性度量
建立量化评估指标:
- 圈复杂度变化:使用ESLint的complexity规则
- 依赖关系数量:通过Madge等工具生成依赖图
- 变更影响范围:统计修改单个模块影响的测试用例数
- 内存使用情况:Chrome DevTools的内存分析
上一篇: 代码异味与设计模式重构机会