您现在的位置是:网站首页 > 重构工具与设计模式转换文章详情

重构工具与设计模式转换

重构工具与设计模式转换

重构工具与设计模式转换是提升代码质量的重要手段。当项目规模扩大或需求变更时,原有的代码结构可能变得难以维护,此时通过重构工具辅助设计模式转换能显著提高开发效率。JavaScript生态中有多种工具支持这一过程,结合设计模式的使用可以创建更灵活、可维护的代码结构。

重构工具的基本使用

现代IDE如VS Code、WebStorm等内置了强大的重构功能。以VS Code为例,其JavaScript/TypeScript支持提供了重命名符号、提取函数、提取变量等基础重构操作:

// 重构前
function calculateTotal(items) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    total += items[i].price * items[i].quantity;
  }
  return total;
}

// 使用提取函数重构后
function calculateItemSubtotal(item) {
  return item.price * item.quantity;
}

function calculateTotal(items) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    total += calculateItemSubtotal(items[i]);
  }
  return total;
}

ESLint等静态分析工具也能识别代码中的"坏味道",建议可能的重构方向。例如配置eslint-plugin-unicorn可以检测出适合使用策略模式的条件分支:

// 重构前
function getSound(animal) {
  if (animal === 'dog') return 'woof';
  if (animal === 'cat') return 'meow';
  if (animal === 'bird') return 'tweet';
  throw new Error('Unknown animal');
}

// 策略模式重构后
const animalSounds = {
  dog: () => 'woof',
  cat: () => 'meow',
  bird: () => 'tweet'
};

function getSound(animal) {
  const soundMaker = animalSounds[animal];
  if (!soundMaker) throw new Error('Unknown animal');
  return soundMaker();
}

从过程式到面向对象的设计转换

当代码中存在大量全局函数和共享状态时,可以考虑转换为基于类的设计模式。重构工具可以帮助完成这种转换:

// 重构前
let counter = 0;

function increment() {
  counter++;
}

function decrement() {
  counter--;
}

// 使用重构工具转换为单例模式
class Counter {
  static instance;
  #count = 0;

  constructor() {
    if (Counter.instance) return Counter.instance;
    Counter.instance = this;
  }

  increment() {
    this.#count++;
  }

  decrement() {
    this.#count--;
  }

  get count() {
    return this.#count;
  }
}

工厂模式的自动化重构

当遇到复杂的对象创建逻辑时,可以使用重构工具辅助转换为工厂模式。以下示例展示了如何将分散的对象创建统一管理:

// 重构前
function createAdminUser(name) {
  const user = new User(name);
  user.role = 'admin';
  user.permissions = ['read', 'write', 'delete'];
  return user;
}

function createGuestUser(name) {
  const user = new User(name);
  user.role = 'guest';
  user.permissions = ['read'];
  return user;
}

// 工厂模式重构后
class UserFactory {
  static create(type, name) {
    switch (type) {
      case 'admin':
        return new AdminUser(name);
      case 'guest':
        return new GuestUser(name);
      default:
        throw new Error('Invalid user type');
    }
  }
}

class AdminUser extends User {
  constructor(name) {
    super(name);
    this.role = 'admin';
    this.permissions = ['read', 'write', 'delete'];
  }
}

观察者模式的重构实现

事件驱动的代码常常可以通过观察者模式进行重构。现代前端框架中的响应式系统大多基于此模式:

// 重构前
let data = { value: 0 };
const dependentFunctions = [];

function updateData(newValue) {
  data.value = newValue;
  dependentFunctions.forEach(fn => fn());
}

function watchData(fn) {
  dependentFunctions.push(fn);
}

// 重构为通用观察者模式
class Observable {
  constructor() {
    this.observers = [];
  }

  subscribe(fn) {
    this.observers.push(fn);
  }

  unsubscribe(fn) {
    this.observers = this.observers.filter(observer => observer !== fn);
  }

  notify(data) {
    this.observers.forEach(observer => observer(data));
  }
}

class DataModel extends Observable {
  #value = 0;

  get value() {
    return this.#value;
  }

  set value(newValue) {
    this.#value = newValue;
    this.notify(this.#value);
  }
}

装饰器模式的应用转换

装饰器模式可以在不修改原有对象的情况下扩展功能。TypeScript的装饰器语法使这种模式更易实现:

// 重构前
class BasicCalculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

class LoggingCalculator {
  calculator: BasicCalculator;

  constructor(calculator: BasicCalculator) {
    this.calculator = calculator;
  }

  add(a: number, b: number): number {
    console.log(`Adding ${a} and ${b}`);
    return this.calculator.add(a, b);
  }
}

// 使用装饰器语法重构
function logOperation(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    console.log(`Calling ${propertyKey} with args: ${JSON.stringify(args)}`);
    const result = originalMethod.apply(this, args);
    console.log(`Result: ${result}`);
    return result;
  };

  return descriptor;
}

class Calculator {
  @logOperation
  add(a: number, b: number): number {
    return a + b;
  }
}

状态模式的重构过程

当对象行为随状态改变而改变时,状态模式可以消除大量的条件语句:

// 重构前
class Order {
  state = 'pending';

  next() {
    if (this.state === 'pending') {
      this.state = 'processing';
    } else if (this.state === 'processing') {
      this.state = 'shipped';
    } else if (this.state === 'shipped') {
      this.state = 'delivered';
    }
  }

  cancel() {
    if (this.state === 'pending' || this.state === 'processing') {
      this.state = 'cancelled';
    } else {
      throw new Error('Cannot cancel order in current state');
    }
  }
}

// 状态模式重构后
class OrderState {
  constructor(order) {
    this.order = order;
  }

  next() {
    throw new Error('Must be implemented by subclass');
  }

  cancel() {
    throw new Error('Must be implemented by subclass');
  }
}

class PendingState extends OrderState {
  next() {
    this.order.setState(new ProcessingState(this.order));
  }

  cancel() {
    this.order.setState(new CancelledState(this.order));
  }
}

class Order {
  constructor() {
    this.setState(new PendingState(this));
  }

  setState(state) {
    this.state = state;
  }

  next() {
    this.state.next();
  }

  cancel() {
    this.state.cancel();
  }
}

组合模式的重构示例

处理树形结构数据时,组合模式能提供一致的操作接口:

// 重构前
function renderMenu(menu) {
  if (menu.items) {
    let html = `<ul>`;
    menu.items.forEach(item => {
      html += renderMenu(item);
    });
    html += `</ul>`;
    return html;
  } else {
    return `<li>${menu.name}</li>`;
  }
}

// 组合模式重构后
class MenuComponent {
  constructor(name) {
    this.name = name;
  }

  add() {
    throw new Error('Unsupported operation');
  }

  render() {
    throw new Error('Must be implemented');
  }
}

class MenuItem extends MenuComponent {
  render() {
    return `<li>${this.name}</li>`;
  }
}

class Menu extends MenuComponent {
  constructor(name) {
    super(name);
    this.children = [];
  }

  add(component) {
    this.children.push(component);
  }

  render() {
    return `
      <ul>
        ${this.children.map(child => child.render()).join('')}
      </ul>
    `;
  }
}

重构为模块模式

模块模式是JavaScript中常用的封装方式,重构工具可以帮助将分散的代码组织为模块:

// 重构前
const apiUrl = 'https://api.example.com';
let cache = {};

async function fetchUser(id) {
  if (cache[id]) return cache[id];
  const response = await fetch(`${apiUrl}/users/${id}`);
  const user = await response.json();
  cache[id] = user;
  return user;
}

// 模块模式重构后
const UserService = (() => {
  const apiUrl = 'https://api.example.com';
  const cache = {};

  async function fetchUser(id) {
    if (cache[id]) return cache[id];
    const response = await fetch(`${apiUrl}/users/${id}`);
    const user = await response.json();
    cache[id] = user;
    return user;
  }

  return { fetchUser };
})();

命令模式的转换实现

将操作封装为对象可以实现撤销、重做等功能,重构工具可以帮助识别适合转换为命令模式的代码:

// 重构前
class TextEditor {
  constructor() {
    this.content = '';
  }

  insertText(text, position) {
    this.content = this.content.slice(0, position) + text + this.content.slice(position);
  }

  deleteText(start, end) {
    this.content = this.content.slice(0, start) + this.content.slice(end);
  }
}

// 命令模式重构后
class Command {
  execute() {
    throw new Error('Must be implemented');
  }

  undo() {
    throw new Error('Must be implemented');
  }
}

class InsertCommand extends Command {
  constructor(editor, text, position) {
    super();
    this.editor = editor;
    this.text = text;
    this.position = position;
    this.previousContent = editor.content;
  }

  execute() {
    this.editor.content = this.editor.content.slice(0, this.position) + 
                         this.text + 
                         this.editor.content.slice(this.position);
  }

  undo() {
    this.editor.content = this.previousContent;
  }
}

class TextEditor {
  constructor() {
    this.content = '';
    this.commandHistory = [];
  }

  executeCommand(command) {
    command.execute();
    this.commandHistory.push(command);
  }

  undo() {
    const command = this.commandHistory.pop();
    if (command) command.undo();
  }
}

适配器模式的重构应用

当接口不兼容时,适配器模式可以充当中间件。重构工具能帮助识别需要适配的场景:

// 重构前
class OldAPI {
  request(data, callback) {
    setTimeout(() => {
      callback(null, { result: data });
    }, 1000);
  }
}

// 新代码期望使用Promise接口
async function newCodeNeedsPromiseAPI() {
  // 直接使用OldAPI会导致代码不一致
}

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

  async request(data) {
    return new Promise((resolve, reject) => {
      this.oldAPI.request(data, (err, result) => {
        if (err) reject(err);
        else resolve(result);
      });
    });
  }
}

重构为代理模式

代理模式可以控制对原始对象的访问,重构工具能帮助识别需要添加控制逻辑的场景:

// 重构前
class ImageLoader {
  constructor(url) {
    this.url = url;
  }

  load() {
    console.log(`Loading image from ${this.url}`);
    return `Image data from ${this.url}`;
  }
}

// 直接使用ImageLoader无法实现延迟加载
const images = [
  new ImageLoader('image1.jpg'),
  new ImageLoader('image2.jpg')
];

// 代理模式重构
class ImageProxy {
  constructor(url) {
    this.url = url;
    this.realImage = null;
  }

  load() {
    if (!this.realImage) {
      this.realImage = new ImageLoader(this.url);
    }
    return this.realImage.load();
  }
}

const images = [
  new ImageProxy('image1.jpg'),
  new ImageProxy('image2.jpg')
];

模板方法模式的重构

当多个算法有相似结构时,模板方法模式可以提取公共部分:

// 重构前
class ReportGenerator {
  generatePDF(data) {
    this.validateData(data);
    const processed = this.processData(data);
    const formatted = this.formatPDF(processed);
    this.saveToFile(formatted, 'report.pdf');
  }

  generateHTML(data) {
    this.validateData(data);
    const processed = this.processData(data);
    const formatted = this.formatHTML(processed);
    this.saveToFile(formatted, 'report.html');
  }
}

// 模板方法模式重构
class ReportGenerator {
  generate(data, filename) {
    this.validateData(data);
    const processed = this.processData(data);
    const formatted = this.format(processed);
    this.saveToFile(formatted, filename);
  }

  format(data) {
    throw new Error('Must be implemented by subclass');
  }
}

class PDFReportGenerator extends ReportGenerator {
  format(data) {
    return `PDF formatted: ${JSON.stringify(data)}`;
  }
}

class HTMLReportGenerator extends ReportGenerator {
  format(data) {
    return `<html><body>${JSON.stringify(data)}</body></html>`;
  }
}

重构为享元模式

当系统存在大量相似对象时,享元模式可以共享相同部分以减少内存使用:

// 重构前
class Tree {
  constructor(type, x, y) {
    this.type = type;
    this.x = x;
    this.y = y;
    this.texture = this.loadTexture(type); // 昂贵的操作
  }

  loadTexture(type) {
    console.log(`Loading texture for ${type}`);
    return `${type}_texture`;
  }

  render() {
    console.log(`Rendering ${this.type} at (${this.x}, ${this.y})`);
  }
}

// 创建1000棵树会重复加载纹理
const forest = [];
for (let i = 0; i < 1000; i++) {
  forest.push(new Tree('Oak', Math.random() * 100, Math.random() * 100));
}

// 享元模式重构
class TreeType {
  constructor(type) {
    this.type = type;
    this.texture = this.loadTexture(type);
  }

  loadTexture(type) {
    console.log(`Loading texture for ${type}`);
    return `${type}_texture`;
  }
}

class TreeFactory {
  static treeTypes = {};

  static getTreeType(type) {
    if (!this.treeTypes[type]) {
      this.treeTypes[type] = new TreeType(type);
    }
    return this.treeTypes[type];
  }
}

class Tree {
  constructor(type, x, y) {
    this.type = TreeFactory.getTreeType(type);
    this.x = x;
    this.y = y;
  }

  render() {
    console.log(`Rendering ${this.type.type} at (${this.x}, ${this.y})`);
  }
}

职责链模式的重构

当请求需要经过多个处理者时,职责链模式可以提供更灵活的解决方案:

// 重构前
function handleRequest(request) {
  if (request.type === 'type1') {
    return handleType1(request);
  } else if (request.type === 'type2') {
    return handleType2(request);
  } else if (request.type === 'type3') {
    return handleType3(request);
  } else {
    throw new Error('Unknown request type');
  }
}

// 职责链模式重构
class Handler {
  constructor() {
    this.nextHandler = null;
  }

  setNext(handler) {
    this.nextHandler = handler;
    return handler;
  }

  handle(request) {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    throw new Error('No handler found for request');
  }
}

class Type1Handler extends Handler {
  handle(request) {
    if (request.type === 'type1') {
      return `Handled type1: ${JSON.stringify(request)}`;
    }
    return super.handle(request);
  }
}

const handlerChain = new Type1Handler()
  .setNext(new Type2Handler())
  .setNext(new Type3Handler());

function handleRequest(request) {
  return handlerChain.handle(request);
}

备忘录模式的重构实现

备忘录模式可以捕获和恢复对象状态,重构工具能帮助识别需要状态保存的场景:

// 重构前
class Editor {
  constructor() {
    this.content = '';
    this.history = [];
  }

  type(text) {
    this.history.push(this.content);
    this.content += text;
  }

  undo() {
    if (this.history.length > 0) {
      this.content = this.history.pop();
    }
  }
}

// 备忘录模式重构
class EditorMemento {
  constructor(content) {
    this.content = content;
  }
}

class Editor {
  constructor() {
    this.content = '';
  }

  type(text) {
    this.content += text;
  }

  save() {
    return new EditorMemento(this.content);
  }

  restore(memento) {
    this.content = memento.content;
  }
}

class History {
  constructor() {
    this.states = [];
  }

  push(state) {
    this.states.push(state);
  }

  pop() {
    return this.states.pop();
  }
}

const editor = new Editor();
const history = new History();

editor.type('Hello');
history.push(editor.save());
editor.type(' World');
editor.restore(history.pop());

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

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