Skip to content

抽象性:简化复杂性的关键

什么是抽象

每天早上, 当你打开水龙头时, 清水就会流出来。你不需要知道城市的自来水系统如何工作——水从哪个水库抽取、经过多少级过滤、通过什么管道输送。你只需要知道"转动水龙头, 水就会流出"这个简单的接口。这就是抽象的力量。

抽象(Abstraction)是面向对象编程的核心原则之一, 它的本质是:

  1. 隐藏复杂性: 将复杂的实现细节隐藏起来
  2. 暴露本质: 只暴露最必要、最重要的信息
  3. 定义接口: 为外部提供简单、清晰的使用方式

抽象让我们能够在更高的层面思考问题, 而不必陷入底层细节的泥潭。

抽象的层次

让我们通过一个汽车的例子理解抽象的不同层次:

javascript
// 最底层 - 具体实现(通常被隐藏)
class Engine {
  #cylinders;
  #fuelType;
  #rpm;

  constructor(cylinders, fuelType) {
    this.#cylinders = cylinders;
    this.#fuelType = fuelType;
    this.#rpm = 0;
  }

  #ignite() {
    console.log("点火系统启动...");
  }

  #injectFuel() {
    console.log(`注入${this.#fuelType}...`);
  }

  #combustion() {
    console.log(`${this.#cylinders}个气缸燃烧...`);
  }

  start() {
    this.#ignite();
    this.#injectFuel();
    this.#combustion();
    this.#rpm = 800;
    console.log("发动机已启动\n");
  }

  accelerate(amount) {
    this.#rpm += amount;
    console.log(`RPM: ${this.#rpm}`);
  }

  stop() {
    this.#rpm = 0;
    console.log("发动机已停止");
  }
}

// 中间层 - 部分抽象
class Vehicle {
  #engine;
  #speed;
  #isRunning;

  constructor(engine) {
    this.#engine = engine;
    this.#speed = 0;
    this.#isRunning = false;
  }

  start() {
    if (this.#isRunning) {
      console.log("车辆已经启动");
      return;
    }

    console.log("启动车辆...");
    this.#engine.start();
    this.#isRunning = true;
    console.log("车辆已启动\n");
  }

  accelerate(speedIncrease) {
    if (!this.#isRunning) {
      throw new Error("请先启动车辆");
    }

    this.#speed += speedIncrease;
    // 根据速度计算需要的发动机转速
    const rpmIncrease = speedIncrease * 50;
    this.#engine.accelerate(rpmIncrease);

    console.log(`当前速度: ${this.#speed} km/h\n`);
  }

  brake(speedDecrease) {
    this.#speed = Math.max(0, this.#speed - speedDecrease);
    console.log(`减速到: ${this.#speed} km/h\n`);
  }

  stop() {
    console.log("停止车辆...");
    this.#speed = 0;
    this.#engine.stop();
    this.#isRunning = false;
    console.log("车辆已停止\n");
  }

  getSpeed() {
    return this.#speed;
  }
}

// 高层抽象 - 简单易用的接口
class Car {
  #vehicle;
  #make;
  #model;

  constructor(make, model) {
    this.#make = make;
    this.#model = model;
    // 用户不需要知道发动机的细节
    this.#vehicle = new Vehicle(new Engine(4, "汽油"));
  }

  // 最简单的接口
  turnOn() {
    console.log(`${this.#make} ${this.#model} 准备出发`);
    this.#vehicle.start();
  }

  drive(targetSpeed) {
    const currentSpeed = this.#vehicle.getSpeed();
    const speedDiff = targetSpeed - currentSpeed;

    if (speedDiff > 0) {
      console.log(`加速到 ${targetSpeed} km/h`);
      this.#vehicle.accelerate(speedDiff);
    } else if (speedDiff < 0) {
      console.log(`减速到 ${targetSpeed} km/h`);
      this.#vehicle.brake(-speedDiff);
    }
  }

  park() {
    console.log("停车");
    this.#vehicle.stop();
  }
}

// 使用 - 最高层的抽象, 非常简单
const myCar = new Car("Toyota", "Camry");

myCar.turnOn(); // 用户不需要知道发动机如何启动
myCar.drive(60); // 不需要知道如何控制油门和发动机转速
myCar.drive(100);
myCar.drive(40);
myCar.park(); // 不需要知道如何停止发动机

抽象类与接口

虽然 JavaScript 没有内置的抽象类和接口概念, 但我们可以模拟它们:

模拟抽象类

javascript
class AbstractDataStore {
  constructor() {
    // 防止直接实例化抽象类
    if (new.target === AbstractDataStore) {
      throw new Error("AbstractDataStore 是抽象类, 不能直接实例化");
    }
  }

  // 抽象方法 - 必须由子类实现
  async connect() {
    throw new Error("connect() 必须由子类实现");
  }

  async disconnect() {
    throw new Error("disconnect() 必须由子类实现");
  }

  async save(key, value) {
    throw new Error("save() 必须由子类实现");
  }

  async load(key) {
    throw new Error("load() 必须由子类实现");
  }

  async delete(key) {
    throw new Error("delete() 必须由子类实现");
  }

  async exists(key) {
    throw new Error("exists() 必须由子类实现");
  }

  // 通用方法 - 子类可以使用
  async saveMultiple(items) {
    const results = [];
    for (const [key, value] of Object.entries(items)) {
      await this.save(key, value);
      results.push(key);
    }
    return results;
  }

  async loadMultiple(keys) {
    const results = {};
    for (const key of keys) {
      results[key] = await this.load(key);
    }
    return results;
  }
}

// 具体实现 - LocalStorage
class LocalStorageStore extends AbstractDataStore {
  async connect() {
    // LocalStorage 不需要连接
    console.log("LocalStorage: 已就绪");
    return true;
  }

  async disconnect() {
    console.log("LocalStorage: 无需断开");
    return true;
  }

  async save(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
    console.log(`LocalStorage: 保存 ${key}`);
    return true;
  }

  async load(key) {
    const value = localStorage.getItem(key);
    console.log(`LocalStorage: 读取 ${key}`);
    return value ? JSON.parse(value) : null;
  }

  async delete(key) {
    localStorage.removeItem(key);
    console.log(`LocalStorage: 删除 ${key}`);
    return true;
  }

  async exists(key) {
    return localStorage.getItem(key) !== null;
  }
}

// 具体实现 - Memory
class MemoryStore extends AbstractDataStore {
  #data = new Map();
  #connected = false;

  async connect() {
    this.#connected = true;
    console.log("MemoryStore: 已连接");
    return true;
  }

  async disconnect() {
    this.#connected = false;
    this.#data.clear();
    console.log("MemoryStore: 已断开");
    return true;
  }

  async save(key, value) {
    if (!this.#connected) {
      throw new Error("未连接到存储");
    }
    this.#data.set(key, value);
    console.log(`MemoryStore: 保存 ${key}`);
    return true;
  }

  async load(key) {
    if (!this.#connected) {
      throw new Error("未连接到存储");
    }
    console.log(`MemoryStore: 读取 ${key}`);
    return this.#data.get(key) || null;
  }

  async delete(key) {
    if (!this.#connected) {
      throw new Error("未连接到存储");
    }
    const result = this.#data.delete(key);
    console.log(`MemoryStore: 删除 ${key}`);
    return result;
  }

  async exists(key) {
    return this.#data.has(key);
  }
}

// 具体实现 - API
class APIStore extends AbstractDataStore {
  #baseUrl;
  #apiKey;
  #connected = false;

  constructor(baseUrl, apiKey) {
    super();
    this.#baseUrl = baseUrl;
    this.#apiKey = apiKey;
  }

  async connect() {
    console.log(`APIStore: 连接到 ${this.#baseUrl}`);
    // 验证 API 连接
    this.#connected = true;
    return true;
  }

  async disconnect() {
    this.#connected = false;
    console.log("APIStore: 已断开");
    return true;
  }

  async save(key, value) {
    if (!this.#connected) {
      throw new Error("未连接到API");
    }

    console.log(`APIStore: POST ${this.#baseUrl}/data/${key}`);
    // 模拟 API 调用
    // await fetch(`${this.#baseUrl}/data/${key}`,  { ... })
    return true;
  }

  async load(key) {
    if (!this.#connected) {
      throw new Error("未连接到API");
    }

    console.log(`APIStore: GET ${this.#baseUrl}/data/${key}`);
    // 模拟 API 调用
    return { data: "模拟数据" };
  }

  async delete(key) {
    if (!this.#connected) {
      throw new Error("未连接到API");
    }

    console.log(`APIStore: DELETE ${this.#baseUrl}/data/${key}`);
    return true;
  }

  async exists(key) {
    if (!this.#connected) {
      throw new Error("未连接到API");
    }

    console.log(`APIStore: HEAD ${this.#baseUrl}/data/${key}`);
    return true;
  }
}

// 使用抽象接口的应用层代码
class DataManager {
  #store;

  constructor(store) {
    // 接受任何实现了 AbstractDataStore 接口的存储
    if (!(store instanceof AbstractDataStore)) {
      throw new Error("store 必须继承自 AbstractDataStore");
    }
    this.#store = store;
  }

  async init() {
    await this.#store.connect();
  }

  async saveUser(user) {
    const key = `user:${user.id}`;
    await this.#store.save(key, user);
    console.log(`✓ 用户 ${user.name} 已保存\n`);
  }

  async getUser(userId) {
    const key = `user:${userId}`;
    return await this.#store.load(key);
  }

  async cleanup() {
    await this.#store.disconnect();
  }
}

// 可以轻松切换不同的存储实现
async function demo() {
  console.log("=== 使用 MemoryStore ===\n");
  const memoryManager = new DataManager(new MemoryStore());
  await memoryManager.init();
  await memoryManager.saveUser({
    id: 1,
    name: "Alice",
    email: "[email protected]",
  });

  console.log("\n=== 使用 APIStore ===\n");
  const apiManager = new DataManager(
    new APIStore("https://api.example.com", "key123")
  );
  await apiManager.init();
  await apiManager.saveUser({ id: 2, name: "Bob", email: "[email protected]" });

  // 尝试实例化抽象类会失败
  try {
    new AbstractDataStore();
  } catch (error) {
    console.log(`\n❌ ${error.message}`);
  }
}

// demo();

抽象层次的设计

好的抽象应该有清晰的层次结构:

javascript
// 最底层:硬件抽象
class NetworkAdapter {
  #isConnected = false;

  connect(ssid, password) {
    console.log(`连接到 WiFi: ${ssid}`);
    // 底层硬件操作
    this.#isConnected = true;
    return true;
  }

  disconnect() {
    this.#isConnected = false;
    return true;
  }

  sendBytes(bytes) {
    if (!this.#isConnected) {
      throw new Error("未连接网络");
    }
    console.log(`发送 ${bytes.length} 字节`);
    return bytes.length;
  }

  receiveBytes(maxBytes) {
    if (!this.#isConnected) {
      throw new Error("未连接网络");
    }
    // 模拟接收数据
    return new Uint8Array(maxBytes);
  }
}

// 第二层:协议抽象
class HTTPClient {
  #adapter;

  constructor(adapter) {
    this.#adapter = adapter;
  }

  request(method, url, headers = {}, body = null) {
    console.log(`\n${method} ${url}`);

    // 构建 HTTP 请求
    const request = this.#buildRequest(method, url, headers, body);

    // 发送请求
    const requestBytes = new TextEncoder().encode(request);
    this.#adapter.sendBytes(requestBytes);

    // 接收响应
    const responseBytes = this.#adapter.receiveBytes(4096);
    return this.#parseResponse(responseBytes);
  }

  #buildRequest(method, url, headers, body) {
    let request = `${method} ${url} HTTP/1.1\r\n`;

    for (const [key, value] of Object.entries(headers)) {
      request += `${key}: ${value}\r\n`;
    }

    if (body) {
      request += `Content-Length: ${body.length}\r\n`;
    }

    request += "\r\n";

    if (body) {
      request += body;
    }

    return request;
  }

  #parseResponse(bytes) {
    // 简化的响应解析
    return {
      status: 200,
      headers: {},
      body: "响应内容",
    };
  }
}

// 第三层:REST API 抽象
class APIClient {
  #http;
  #baseUrl;
  #token;

  constructor(baseUrl, token) {
    const adapter = new NetworkAdapter();
    adapter.connect("MyWiFi", "password");

    this.#http = new HTTPClient(adapter);
    this.#baseUrl = baseUrl;
    this.#token = token;
  }

  #getHeaders() {
    return {
      Authorization: `Bearer ${this.#token}`,
      "Content-Type": "application/json",
    };
  }

  async get(endpoint) {
    return this.#http.request(
      "GET",
      `${this.#baseUrl}${endpoint}`,
      this.#getHeaders()
    );
  }

  async post(endpoint, data) {
    return this.#http.request(
      "POST",
      `${this.#baseUrl}${endpoint}`,
      this.#getHeaders(),
      JSON.stringify(data)
    );
  }

  async put(endpoint, data) {
    return this.#http.request(
      "PUT",
      `${this.#baseUrl}${endpoint}`,
      this.#getHeaders(),
      JSON.stringify(data)
    );
  }

  async delete(endpoint) {
    return this.#http.request(
      "DELETE",
      `${this.#baseUrl}${endpoint}`,
      this.#getHeaders()
    );
  }
}

// 第四层:业务抽象
class UserService {
  #api;

  constructor(apiClient) {
    this.#api = apiClient;
  }

  async getUser(userId) {
    console.log(`\n获取用户 ${userId}`);
    return await this.#api.get(`/users/${userId}`);
  }

  async createUser(userData) {
    console.log(`\n创建用户: ${userData.name}`);
    return await this.#api.post("/users", userData);
  }

  async updateUser(userId, updates) {
    console.log(`\n更新用户 ${userId}`);
    return await this.#api.put(`/users/${userId}`, updates);
  }

  async deleteUser(userId) {
    console.log(`\n删除用户 ${userId}`);
    return await this.#api.delete(`/users/${userId}`);
  }

  async searchUsers(query) {
    console.log(`\n搜索用户: ${query}`);
    return await this.#api.get(`/users/search?q=${encodeURIComponent(query)}`);
  }
}

// 最高层:应用层使用
const apiClient = new APIClient("https://api.example.com", "token123");
const userService = new UserService(apiClient);

// 应用层代码非常简洁, 不需要知道任何底层细节
// userService.getUser(123);
// userService.createUser({ name: "John",  email: "[email protected]" });

通过抽象实现依赖注入

抽象使得依赖注入变得简单和灵活:

javascript
// 定义抽象日志接口
class Logger {
  log(level, message, meta = {}) {
    throw new Error("log() 必须被实现");
  }

  info(message, meta) {
    this.log("INFO", message, meta);
  }

  warn(message, meta) {
    this.log("WARN", message, meta);
  }

  error(message, meta) {
    this.log("ERROR", message, meta);
  }
}

// 具体实现:控制台日志
class ConsoleLogger extends Logger {
  log(level, message, meta) {
    const timestamp = new Date().toISOString();
    const metaStr = Object.keys(meta).length > 0 ? JSON.stringify(meta) : "";
    console.log(`[${timestamp}] [${level}] ${message} ${metaStr}`);
  }
}

// 具体实现:文件日志
class FileLogger extends Logger {
  #filename;

  constructor(filename) {
    super();
    this.#filename = filename;
  }

  log(level, message, meta) {
    const timestamp = new Date().toISOString();
    const logEntry = `[${timestamp}] [${level}] ${message} ${JSON.stringify(
      meta
    )}\n`;
    console.log(`写入文件 ${this.#filename}: ${logEntry.trim()}`);
    // 实际应用中会写入文件
  }
}

// 具体实现:远程日志
class RemoteLogger extends Logger {
  #endpoint;

  constructor(endpoint) {
    super();
    this.#endpoint = endpoint;
  }

  log(level, message, meta) {
    const logData = {
      timestamp: new Date().toISOString(),
      level,
      message,
      meta,
    };
    console.log(`发送日志到 ${this.#endpoint}:`, logData);
    // 实际应用中会发送到远程服务器
  }
}

// 具体实现:多日志器
class MultiLogger extends Logger {
  #loggers;

  constructor(loggers) {
    super();
    this.#loggers = loggers;
  }

  log(level, message, meta) {
    for (const logger of this.#loggers) {
      logger.log(level, message, meta);
    }
  }
}

// 业务服务 - 依赖注入
class OrderService {
  #logger;
  #database;

  constructor(logger, database) {
    this.#logger = logger; // 注入抽象的日志器
    this.#database = database;
  }

  createOrder(orderData) {
    this.#logger.info("创建订单", { orderId: orderData.id });

    try {
      // 业务逻辑
      this.#database.save("order", orderData);

      this.#logger.info("订单创建成功", {
        orderId: orderData.id,
        amount: orderData.total,
      });

      return { success: true, orderId: orderData.id };
    } catch (error) {
      this.#logger.error("订单创建失败", {
        orderId: orderData.id,
        error: error.message,
      });

      throw error;
    }
  }

  cancelOrder(orderId) {
    this.#logger.warn("取消订单", { orderId });

    // 业务逻辑
    this.#database.delete("order", orderId);

    this.#logger.info("订单已取消", { orderId });
  }
}

// 使用 - 可以轻松切换不同的日志实现
console.log("\n=== 使用控制台日志 ===");
const consoleService = new OrderService(new ConsoleLogger(), {
  save: () => {},
  delete: () => {},
});
consoleService.createOrder({ id: "ORD-001", total: 299.99 });

console.log("\n=== 使用文件日志 ===");
const fileService = new OrderService(new FileLogger("/var/log/orders.log"), {
  save: () => {},
  delete: () => {},
});
fileService.createOrder({ id: "ORD-002", total: 499.99 });

console.log("\n=== 使用多个日志器 ===");
const multiService = new OrderService(
  new MultiLogger([
    new ConsoleLogger(),
    new FileLogger("/var/log/orders.log"),
    new RemoteLogger("https://logs.example.com/api"),
  ]),
  { save: () => {}, delete: () => {} }
);
multiService.createOrder({ id: "ORD-003", total: 999.99 });

抽象的设计原则

1. 接口隔离原则

接口应该小而专注, 不要强迫类实现它们不需要的方法:

javascript
// ❌ 不好的设计 - 臃肿的接口
class Worker {
  work() {}
  eat() {}
  sleep() {}
  getMaintenance() {} // 机器人Worker不需要这个
  chargeBattery() {} // 人类Worker不需要这个
}

// ✅ 好的设计 - 分离的接口
class Workable {
  work() {
    throw new Error("work() 必须被实现");
  }
}

class Eatable {
  eat() {
    throw new Error("eat() 必须被实现");
  }
}

class Sleepable {
  sleep() {
    throw new Error("sleep() 必须被实现");
  }
}

class Rechargeable {
  chargeBattery() {
    throw new Error("chargeBattery() 必须被实现");
  }
}

// 人类工人只实现需要的接口
class HumanWorker extends Workable {
  constructor(name) {
    super();
    this.name = name;
  }

  work() {
    console.log(`${this.name} 正在工作`);
  }

  eat() {
    console.log(`${this.name} 正在吃饭`);
  }

  sleep() {
    console.log(`${this.name} 正在睡觉`);
  }
}

// 机器人工人实现不同的接口
class RobotWorker extends Workable {
  constructor(id) {
    super();
    this.id = id;
  }

  work() {
    console.log(`机器人 ${this.id} 正在工作`);
  }

  chargeBattery() {
    console.log(`机器人 ${this.id} 正在充电`);
  }

  getMaintenance() {
    console.log(`机器人 ${this.id} 正在维护`);
  }
}

2. 依赖倒置原则

高层模块不应该依赖低层模块, 两者都应该依赖抽象:

javascript
// 抽象支付接口
class PaymentProcessor {
  process(amount) {
    throw new Error("process() 必须被实现");
  }
}

// 具体实现
class StripePayment extends PaymentProcessor {
  process(amount) {
    console.log(`通过 Stripe 处理 $${amount}`);
    return { success: true, provider: "Stripe" };
  }
}

class PayPalPayment extends PaymentProcessor {
  process(amount) {
    console.log(`通过 PayPal 处理 $${amount}`);
    return { success: true, provider: "PayPal" };
  }
}

// 高层模块依赖抽象
class CheckoutService {
  #paymentProcessor;

  constructor(paymentProcessor) {
    // 依赖抽象, 不依赖具体实现
    if (!(paymentProcessor instanceof PaymentProcessor)) {
      throw new Error("必须提供 PaymentProcessor 实例");
    }
    this.#paymentProcessor = paymentProcessor;
  }

  checkout(cart) {
    const total = cart.getTotal();
    console.log(`结账总额: $${total}`);

    // 使用注入的支付处理器
    const result = this.#paymentProcessor.process(total);

    if (result.success) {
      console.log(`支付成功 (${result.provider})\n`);
    }

    return result;
  }
}

// 使用 - 可以轻松切换支付方式
const cart = { getTotal: () => 99.99 };

const stripeCheckout = new CheckoutService(new StripePayment());
stripeCheckout.checkout(cart);

const paypalCheckout = new CheckoutService(new PayPalPayment());
paypalCheckout.checkout(cart);

抽象的层次感

好的抽象应该有清晰的层次, 每层只关心自己级别的细节:

javascript
// 低级抽象 - 数据访问
class Repository {
  findById(id) {}
  save(entity) {}
  delete(id) {}
}

// 中级抽象 - 业务逻辑
class Service {
  #repository;

  constructor(repository) {
    this.#repository = repository;
  }

  // 业务方法使用仓储抽象
}

// 高级抽象 - 应用流程
class Controller {
  #service;

  constructor(service) {
    this.#service = service;
  }

  // 控制器方法使用服务抽象
}

小结

抽象是管理复杂性的关键工具:

  • 隐藏细节: 将复杂的实现藏在简单的接口后面
  • 提升复用: 通过抽象接口, 代码可以适用于多种实现
  • 降低耦合: 依赖抽象而非具体实现, 使系统更灵活
  • 便于测试: 可以轻松替换实现进行测试

好的抽象应该:

  • 简单直观, 易于理解
  • 稳定可靠, 不频繁变化
  • 职责单一, 专注明确
  • 层次清晰, 不过度抽象