前端抽象化,打破框架枷锁:面向对象思想一定要class吗?

引言
本次不讲某个示例了,我们来聚焦其核心理念在现代前端开发中的应用。

传统上,面向对象编程依赖类来组织代码,但随着React和Vue等现代框架的兴起,特别是Hook的普及,函数式编程逐渐占据主导。然而,这导致工程化设计被忽视,我认为这并非好事。前端开发迭代迅速,缺乏深思熟虑的设计会使代码受框架特性限制,难以适应新旧版本的不兼容问题(如React或Vue的API变更,应该很多前端都能明显感觉到),从而阻碍迭代升级。

本文将展示如何用TypeScript设计一个信息持久化的抽象结构,然后用Hook实现它,展示通过良好的工程设计,前端架构可以摆脱框架特性的限制,保持迭代升级的灵活性。无论是快速适配新的Hook,还是在未来Hook被淘汰时切换到其他范式,这种设计都能确保代码的适应性和可维护性。。


系统设计

我们将构建三个模块:

  1. Persistence Module(持久化模块):提供数据存储功能。
  2. Settings Module(设置模块):管理用户设置,依赖Persistence Module。
  3. SortedFavorites Module(排序收藏模块):管理用户收藏并根据设置排序,依赖Settings Module和Persistence Module。

模块抽象设计

首先,让我们用TypeScript接口来定义这些模块的抽象结构:

/**
 * 持久化模块接口
 * 负责数据的存储和读取
 */
interface IPersistenceModule {
  /** 保存数据 */
  save<T>(key: string, data: T): Promise<void>;
  /** 读取数据 */
  load<T>(key: string): Promise<T | null>;
  /** 删除数据 */
  remove(key: string): Promise<void>;
}

/**
 * 设置模块接口
 * 管理用户设置,依赖持久化模块
 */
interface ISettingsModule {
  /** 获取设置 */
  getSetting<T>(key: string): Promise<T | null>;
  /** 更新设置 */
  updateSetting<T>(key: string, value: T): Promise<void>;
  /** 重置设置 */
  resetSettings(): Promise<void>;
}

/**
 * 收藏项类型
 */
interface FavoriteItem {
  /** 收藏ID */
  id: string;
  /** 收藏标题 */
  title: string;
  /** 收藏时间戳 */
  timestamp: number;
  /** 收藏数据 */
  data: unknown;
}

/**
 * 排序收藏模块接口
 * 管理用户收藏并根据设置排序
 */
interface ISortedFavoritesModule {
  /** 添加收藏 */
  addFavorite(item: FavoriteItem): Promise<void>;
  /** 获取排序后的收藏列表 */
  getSortedFavorites(): Promise<FavoriteItem[]>;
  /** 删除收藏 */
  removeFavorite(id: string): Promise<void>;
}

/**
 * 模块工厂接口
 * 用于创建各个模块的实例
 */
interface IModuleFactory {
  /** 创建持久化模块 */
  createPersistenceModule(): IPersistenceModule;
  /** 创建设置模块 */
  createSettingsModule(persistence: IPersistenceModule): ISettingsModule;
  /** 创建排序收藏模块 */
  createSortedFavoritesModule(
    persistence: IPersistenceModule,
    settings: ISettingsModule
  ): ISortedFavoritesModule;
}

类图 这个能让你更直观理解

uml diagram

这个设计采用了接口定义的方式,而不是传统的类继承。

接下来,我们将展示如何使用Hook来实现这些模块,证明面向对象的设计思想可以脱离类的束缚。

hook方式的实现

下面我们使用 React Hook 来实现这些模块。为了简化示例,我们使用 localStorage 作为存储方式。

/**
 * 持久化模块的 Hook 实现
 * 使用 localStorage 作为存储方式,实际项目中可以替换为其他存储方式
 */
const usePersistence = (): IPersistenceModule => {
  // 保存数据到 localStorage
  const save = async <T>(key: string, data: T): Promise<void> => {
    localStorage.setItem(key, JSON.stringify(data));
  };

  // 从 localStorage 读取数据
  const load = async <T>(key: string): Promise<T | null> => {
    const data = localStorage.getItem(key);
    return data ? JSON.parse(data) : null;
  };

  // 从 localStorage 删除数据
  const remove = async (key: string): Promise<void> => {
    localStorage.removeItem(key);
  };

  return { save, load, remove };
};

/**
 * 设置模块的 Hook 实现
 * 依赖持久化模块来存储设置
 */
const useSettings = (persistence: IPersistenceModule): ISettingsModule => {
  // 获取设置值
  const getSetting = async <T>(key: string): Promise<T | null> => {
    return persistence.load<T>(`settings_${key}`);
  };

  // 更新设置值
  const updateSetting = async <T>(key: string, value: T): Promise<void> => {
    await persistence.save(`settings_${key}`, value);
  };

  // 重置所有设置
  const resetSettings = async (): Promise<void> => {
    // 获取所有设置键
    const keys = Object.keys(localStorage)
      .filter(key => key.startsWith('settings_'));
    // 删除所有设置
    await Promise.all(keys.map(key => persistence.remove(key)));
  };

  return { getSetting, updateSetting, resetSettings };
};

/**
 * 排序收藏模块的 Hook 实现
 * 依赖持久化模块存储收藏,依赖设置模块获取排序方式
 */
const useSortedFavorites = (
  persistence: IPersistenceModule,
  settings: ISettingsModule
): ISortedFavoritesModule => {
  // 添加收藏
  const addFavorite = async (item: FavoriteItem): Promise<void> => {
    const favorites = await persistence.load<FavoriteItem[]>('favorites') || [];
    favorites.push(item);
    await persistence.save('favorites', favorites);
  };

  // 获取排序后的收藏列表
  const getSortedFavorites = async (): Promise<FavoriteItem[]> => {
    const favorites = await persistence.load<FavoriteItem[]>('favorites') || [];
    const sortOrder = await settings.getSetting<'asc' | 'desc'>('sortOrder') || 'desc';

    // 根据时间戳排序
    return favorites.sort((a, b) =>
      sortOrder === 'desc' ? b.timestamp - a.timestamp : a.timestamp - b.timestamp
    );
  };

  // 删除收藏
  const removeFavorite = async (id: string): Promise<void> => {
    const favorites = await persistence.load<FavoriteItem[]>('favorites') || [];
    const newFavorites = favorites.filter(item => item.id !== id);
    await persistence.save('favorites', newFavorites);
  };

  return { addFavorite, getSortedFavorites, removeFavorite };
};

/**
 * 模块工厂的 Hook 实现
 * 负责创建和管理各个模块的实例
 */
const useModuleFactory = (): IModuleFactory => {
  // 创建各个模块实例
  const persistence = usePersistence();
  const settings = useSettings(persistence);
  const favorites = useSortedFavorites(persistence, settings);

  // 返回工厂方法
  return {
    createPersistenceModule: () => persistence,
    createSettingsModule: () => settings,
    createSortedFavoritesModule: () => favorites
  };
};

以上便是hook方式的实现,通过这种方式,我们实现了模块的解耦,并且可以很容易的进行单元测试。他没有用类编程,而是用函数编程,它既能让我们的程序依赖关系清晰且干净,又让我们使用了hook这种现代化框架特性。

这些本质是什么

既然面向对象编程没有必须用类,函数式编程是否必须用hook这种代码?是否必须要用现代框架的特性?当然也是否定的。
实现是可以快速迭代的,但是编程思想,设计模式等本质的东西是十分稳定、有效的。就像设计的一条原则:"高层策略不应该依赖低层实现"。
至于hook,类,框架,都是工具,工具是用来实现思想的,而不是被思想所束缚。

总览

  • "面向对象编程是对依赖关系进行控制,让高层策略和底层实现分开,让实现可以自由变化,而高层策略可以保持稳定。"
  • "hook思想是让逻辑拆分为独立插件,可以在特定地方去组合使用,而无需更改其他部分逻辑"
  • "函数式编程是对状态的修改进行了直接的限制,强调不可变性,无副作用让代码更易于理解和维护"
最后更新日期 6/24/2025, 10:29:18 AM