新谈设计模式 Chapter 03 — 抽象工厂模式 Abstract Factory

张开发
2026/4/17 16:14:07 15 分钟阅读

分享文章

新谈设计模式 Chapter 03 — 抽象工厂模式 Abstract Factory
欢迎新朋友点赞、关注、收藏三连。Chapter 03 — 抽象工厂模式 Abstract Factory灵魂速记宜家风格套装——选了北欧风桌子椅子灯全是北欧风。秒懂类比你去宜家买家具不会一件件挑那样可能沙发是中式、茶几是欧式丑得要命。你选一个风格套装北欧风工厂生产北欧沙发 北欧茶几 北欧灯工业风工厂生产工业沙发 工业茶几 工业灯关键一个工厂生产一整套风格统一的产品而不是单个产品。问题引入// 灾难现场跨平台 UI到处 if-elsevoidcreateUI(conststd::stringos){if(oswindows){autobtnnewWinButton();autotxtnewWinTextBox();autochknewWinCheckbox();// Windows 三件套}elseif(osmacos){autobtnnewMacButton();autotxtnewMacTextBox();autochknewMacCheckbox();// Mac 三件套}// 如果 btn 是 Win 的txt 却是 Mac 的……界面就炸了}问题产品之间有配套关系混搭会出 bug每加一个平台所有 if-else 都要改模式结构┌──────────────────────┐ │ AbstractFactory │ ← 抽象工厂定义能生产哪些产品 ├──────────────────────┤ │ createButton() │ │ createTextBox() │ │ createCheckbox() │ └──────────┬───────────┘ │ 实现 ┌──────┴──────────┐ │ │ ┌───┴────┐ ┌────┴────┐ │WinFactory│ │MacFactory│ ← 具体工厂生产配套产品族 └───┬────┘ └────┬────┘ │ │ WinButton MacButton ← 同一接口不同风格 WinTextBox MacTextBox WinCheckbox MacCheckboxC 实现#includeiostream#includememory// 抽象产品 classButton{public:virtual~Button()default;virtualvoidrender()const0;};classTextBox{public:virtual~TextBox()default;virtualvoidrender()const0;};// Windows 产品族 classWinButton:publicButton{public:voidrender()constoverride{std::cout[Windows 按钮]\n;}};classWinTextBox:publicTextBox{public:voidrender()constoverride{std::cout[Windows 文本框]\n;}};// macOS 产品族 classMacButton:publicButton{public:voidrender()constoverride{std::cout[macOS 按钮]\n;}};classMacTextBox:publicTextBox{public:voidrender()constoverride{std::cout[macOS 文本框]\n;}};// 抽象工厂 classGUIFactory{public:virtual~GUIFactory()default;virtualstd::unique_ptrButtoncreateButton()const0;virtualstd::unique_ptrTextBoxcreateTextBox()const0;};// 具体工厂 classWinFactory:publicGUIFactory{public:std::unique_ptrButtoncreateButton()constoverride{returnstd::make_uniqueWinButton();}std::unique_ptrTextBoxcreateTextBox()constoverride{returnstd::make_uniqueWinTextBox();}};classMacFactory:publicGUIFactory{public:std::unique_ptrButtoncreateButton()constoverride{returnstd::make_uniqueMacButton();}std::unique_ptrTextBoxcreateTextBox()constoverride{returnstd::make_uniqueMacTextBox();}};// 客户端完全面向抽象编程 classApplication{public:// explicit 防止隐式转换——避免 Application app someFactory; 这种写法// 只接受 Application app(std::move(factory)); 或 Application app{std::move(factory)};explicitApplication(std::unique_ptrGUIFactoryfactory):factory_(std::move(factory)){}voidbuildUI(){autobtnfactory_-createButton();autotxtfactory_-createTextBox();btn-render();txt-render();// 同一个工厂出来的产品一定配套不会 Win 按钮配 Mac 文本框}private:std::unique_ptrGUIFactoryfactory_;};intmain(){// 整个程序只在这一处做平台判断的 if-elsestd::string osmacos;// 实际中从配置/环境变量读取std::unique_ptrGUIFactoryfactory;if(oswindows){factorystd::make_uniqueWinFactory();}else{factorystd::make_uniqueMacFactory();}Applicationapp(std::move(factory));app.buildUI();}输出[macOS 按钮] [macOS 文本框]关键洞察注意main里还是有 if-else —— 但只出现一次出现在最顶层。往下所有的业务代码Application、它调用的方法全都通过抽象接口工作再也不碰具体类名。抽象工厂的价值不是消灭 if-else而是把 if-else 关进笼子里只关一次。什么时候用✅ 适合❌ 别用多个相关产品必须配套使用产品之间没有关联需要切换整套产品族如换皮肤、换平台只生产一种产品用工厂方法就够了产品族种类会增加加新风格产品种类会增加加新零件——这会改接口⚠️抽象工厂的软肋如果你要给工厂加一种新产品比如加个 Slider所有工厂子类都得改。它擅长加新风格不擅长加新零件。防混淆抽象工厂 vs 工厂方法工厂方法抽象工厂产品个数1 个一族多个相关产品关键手段继承 重写一个方法组合 工厂对象有多个创建方法约束无配套约束保证产品配套类比分店各卖各的宜家风格套装一句话分清工厂方法造一个抽象工厂造一套。抽象工厂 vs Builder抽象工厂Builder目标创建一族相关对象创建一个复杂对象返回多个产品一个产品过程一步到位分步构建类比选风格套装配肯德基套餐现代 C 小贴士如果产品族在编译期就能确定比如你不需要运行时换平台一次编译只针对一个平台可以用模板代替虚函数。好处是零开销——没有虚函数调用的间接跳转templatetypenameButtonT,typenameTextBoxTclassGUIFactory{public:autocreateButton()const{returnstd::make_uniqueButtonT();}autocreateTextBox()const{returnstd::make_uniqueTextBoxT();}};// 类型别名编译期锁定整套产品族usingWinFactoryGUIFactoryWinButton,WinTextBox;usingMacFactoryGUIFactoryMacButton,MacTextBox;这里的auto返回类型C14 起让编译器自动推导返回std::unique_ptrButtonT省得手写模板返回类型。不过模板方案有个限制所有类型在编译期就定死了不能在运行时根据配置文件切换——需要运行时灵活切换的场景还是得用虚函数版本。

更多文章