03 | 为什么你的界面总是像个“缝合怪”?——抽象工厂模式

0 阅读4分钟

先聊聊那个让我掉坑里的项目。 当时我们要给 App 做一套“夜间模式”和一套“春节红模式”。 界面上主要有两个元素:Button(按钮)和 NavBar(导航栏)。

我一开始的做法特别简单粗暴: 在渲染按钮的时候,判断一下当前是啥主题;在渲染导航栏的时候,再判断一下。

结果上线后出了个大乌龙: 因为逻辑分散在各个角落,有个页面的配置没读对,导致用户看到的是“夜间模式”的黑底导航栏,配上了“春节模式”的大红按钮。 那画面,简直就是个“缝合怪”,丑得我都不敢看。

这就是问题的核心:你需要的不是创建一个对象,而是要保证创建出来的“一整套”对象,风格必须是统一的。

它是为了“全家桶”而生的

如果说“工厂方法模式”是让你点一道菜(只要是鱼香肉丝就行,不管谁炒的); 那么“抽象工厂模式”就是让你点 “肯德基全家桶”

你买了**“奥尔良套餐”**,里面的汉堡、鸡翅、可乐,必须全都是奥尔良风味的。 你不能汉堡是奥尔良的,鸡翅却给你塞了个麦当劳的麦辣鸡翅,那就不配套了。

抽象工厂的底层逻辑就是: 提供一个接口,用来创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。

它的核心价值在于**“约束”** —— 强行把一整套东西捆绑在一起,防止你乱搭。

看看代码是怎么“成套”生产的

咱们用 JS 模拟一下那个换肤的场景。

如果不适用模式,我们很容易写出这种分散的代码:

// 这种写法,很容易出现 button 是 dark,navbar 却是 light 的情况
const button = new DarkButton();
const navbar = new LightNavbar(); // 完了,搞混了

现在,我们用抽象工厂把它们“捆”起来:

// 1. 定义两套具体的组件(产品族)
class DarkButton { render() { console.log('渲染:黑色按钮') } }
class DarkNavbar { render() { console.log('渲染:黑色导航') } }

class LightButton { render() { console.log('渲染:白色按钮') } }
class LightNavbar { render() { console.log('渲染:白色导航') } }

// 2. 定义“全家桶”工厂
// 这个工厂不仅造按钮,还造导航,确保它们是一家的
class DarkThemeFactory {
  createButton() { return new DarkButton(); }
  createNavbar() { return new DarkNavbar(); }
}

class LightThemeFactory {
  createButton() { return new LightButton(); }
  createNavbar() { return new LightNavbar(); }
}

// 3. 业务逻辑只认“工厂”
function renderPage(factory) {
  // 我根本不用担心样式不统一,因为 factory 保证了它们是一套的
  const btn = factory.createButton();
  const nav = factory.createNavbar();
  
  btn.render();
  nav.render();
}

// 使用
renderPage(new DarkThemeFactory()); // 产出全套黑色系

两种写法的直观对比

容易出问题的写法: 在业务代码里,一会儿 new Button('dark'),一会儿 new Navbar('light')后果:极易出现“风格混搭”的 Bug,而且每次加新主题,要把所有组件的实例化代码都翻一遍。

更稳健的抽象工厂写法: 直接传一个 Factory 对象进去。 后果:无论你怎么调用,只要工厂没选错,里面的按钮和导航栏绝对是配套的。这就叫**“高内聚”**。

给你的 3 条行动建议

  1. 区分“产品”和“产品族”:这是判断用哪个工厂模式的金标准。如果你的业务只需要创建一种东西(比如只有按钮),用工厂方法。如果你需要创建一整套东西(按钮+导航+背景+字体),且它们必须配套,请用抽象工厂

  2. JS 里的“鸭子类型”:在 Java 里写这个模式需要定义复杂的 Interface。但在 JS 里,只要你的 DarkFactoryLightFactory 都有 createButton 方法就行,不用非得写个父类,别把代码搞得太重。

  3. 别为了用而用:说实话,这个模式在前端业务里用得不算多,除非你在做跨端框架(同时生成安卓组件和 iOS 组件)或者换肤系统。如果是简单的业务逻辑,硬套这个模式反而会增加复杂度。

我以前总觉得设计模式是把简单问题复杂化。 后来才发现,它是为了在面对真正的复杂问题时,能帮你守住代码的底线。

抽象工厂就是那个帮你守住“风格统一”底线的人。

希望这点复盘,能帮你省下以后去修“缝合怪”UI 的时间。