零基础设计模式——创建型模式 - 工厂方法模式
第二部分:创建型模式 - 工厂方法模式 (Factory Method Pattern)
上一节我们学习了单例模式,它关注如何保证一个类只有一个实例。现在,我们来看另一个重要的创建型模式——工厂方法模式。它关注的是如何创建对象,但将创建的决定权推迟到子类。
- 核心思想:定义一个创建对象的接口,但让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
工厂方法模式 (Factory Method Pattern)
“定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。”
想象一下,你有一家饮料工厂。这家工厂(父类)知道如何生产饮料(定义了生产饮料的流程/接口),但具体生产什么类型的饮料(可乐、雪碧、橙汁等)则由各个专门的生产线(子类工厂)来决定。
1. 目的 (Intent)
工厂方法模式的主要目的:
- 封装对象创建:将对象的创建过程封装起来,客户端不需要知道具体的产品类名,只需要知道它所需要的产品的抽象类型和对应的工厂。
- 解耦:将产品的创建和使用分离开。客户端代码仅依赖于抽象产品和抽象工厂,而不依赖于具体产品和具体工厂。这使得系统更加灵活,更容易扩展。
- 符合开闭原则:当需要增加新的产品时,只需要增加对应的具体产品类和具体工厂类,而不需要修改已有的工厂或客户端代码。
2. 生活中的例子 (Real-world Analogy)
-
餐厅点餐:
- 抽象工厂 (Abstract Factory):菜单(定义了可以点什么菜,比如“主食”、“汤”)。
- 具体工厂 (Concrete Factory):中餐厅菜单、西餐厅菜单。它们都提供“主食”,但中餐厅提供米饭、面条,西餐厅提供牛排、意面。
- 抽象产品 (Abstract Product):菜品(比如“主食”这个概念)。
- 具体产品 (Concrete Product):米饭、面条、牛排、意面。
你(客户端)只需要告诉服务员(通过菜单这个工厂接口)你要一份“主食”,具体是米饭还是牛排,则由你选择的餐厅(具体工厂)决定。
-
汽车制造厂:
- 一个大型汽车集团(比如大众集团)可以看作是一个抽象工厂的框架。它定义了制造汽车的标准流程。
- 旗下的不同品牌工厂(大众工厂、奥迪工厂、保时捷工厂)就是具体工厂。它们都遵循集团的制造标准,但各自生产不同型号和品牌的汽车。
- 客户想要一辆“轿车”(抽象产品),可以选择去大众工厂买帕萨特(具体产品),也可以去奥迪工厂买A4(具体产品)。
-
各种笔的制造:
- 抽象工厂:
PenFactory
(定义一个createPen()
方法)。 - 具体工厂:
PencilFactory
(实现createPen()
返回铅笔),BallpointPenFactory
(实现createPen()
返回圆珠笔)。 - 抽象产品:
Pen
(定义write()
方法)。 - 具体产品:
Pencil
(实现write()
写出石墨痕迹),BallpointPen
(实现write()
写出油墨痕迹)。
- 抽象工厂:
3. 结构 (Structure)
工厂方法模式通常包含以下角色:
- Product (抽象产品):定义工厂方法所创建的对象的接口。所有具体产品都必须实现这个接口。
- ConcreteProduct (具体产品):实现 Product 接口的具体类。这些是工厂方法实际创建的对象。
- Creator (抽象创建者/抽象工厂):声明工厂方法
factoryMethod()
,该方法返回一个 Product 类型的对象。Creator 也可以定义一个默认的工厂方法实现,返回一个默认的 ConcreteProduct 对象。 - ConcreteCreator (具体创建者/具体工厂):重写(实现)工厂方法
factoryMethod()
,以返回一个具体的 ConcreteProduct 实例。
4. 适用场景 (When to Use)
- 当一个类不知道它所必须创建的对象的类的时候。即,你希望由子类来指定创建哪个对象。
- 当一个类希望通过其子类来指定它所创建的对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
- 需要解耦框架和具体实现:例如,一个UI框架可能定义了创建按钮的工厂方法,但具体的按钮样式(Windows按钮、Mac按钮)由框架的使用者(子类)来实现。
- 日志记录器:可以有文件日志记录器工厂、数据库日志记录器工厂等,它们都创建符合日志记录器接口的对象。
- 数据库访问:可以有
MySQLConnectionFactory
、OracleConnectionFactory
等,它们都创建符合Connection
接口的对象。
5. 优缺点 (Pros and Cons)
优点:
- 良好的封装性,代码结构清晰:创建过程在具体工厂中实现,客户端代码只关心抽象接口。
- 扩展性好:增加新的产品时,只需要增加对应的具体产品类和具体工厂类,无需修改现有代码,符合开闭原则。
- 解耦:将产品创建和产品使用分离,降低了模块间的耦合度。
- 更灵活:父类只需要关心产品接口,具体创建哪个产品由子类决定,增加了系统的灵活性。
缺点:
- 类的个数增多:每增加一个产品,就需要增加一个具体产品类和一个对应的具体工厂类,这会使得系统中的类的个数成倍增加,增加了系统的复杂度。
- 增加了系统的抽象性和理解难度:对于初学者来说,引入了更多的抽象层,可能需要更多时间来理解。
6. 实现方式 (Implementations)
让我们通过一个支付的例子来看看工厂方法模式的实现。
假设我们有一个电商系统,支持多种支付方式,如支付宝支付、微信支付。
抽象产品 (Payment)
// payment.go
package paymentimport "fmt"// Payment 支付接口 (抽象产品)
type Payment interface {Pay(amount float64) string
}
// Payment.java
package com.example.payment;// 支付接口 (抽象产品)
public interface Payment {String pay(double amount);
}
具体产品 (Alipay, WechatPay)
// alipay.go
package paymentimport "fmt"// Alipay 支付宝支付 (具体产品)
type Alipay struct{}func (a *Alipay) Pay(amount float64) string {return fmt.Sprintf("使用支付宝支付了 %.2f 元", amount)
}// wechatpay.go
package paymentimport "fmt"// WechatPay 微信支付 (具体产品)
type WechatPay struct{}func (w *WechatPay) Pay(amount float64) string {return fmt.Sprintf("使用微信支付了 %.2f 元", amount)
}
// Alipay.java
package com.example.payment;// 支付宝支付 (具体产品)
public class Alipay implements Payment {@Overridepublic String pay(double amount) {return String.format("使用支付宝支付了 %.2f 元", amount);}
}// WechatPay.java
package com.example.payment;// 微信支付 (具体产品)
public class WechatPay implements Payment {@Overridepublic String pay(double amount) {return String.format("使用微信支付了 %.2f 元", amount);}
}
抽象工厂 (PaymentFactory)
// payment_factory.go
package payment// PaymentFactory 支付工厂接口 (抽象创建者)
type PaymentFactory interface {CreatePayment() Payment
}
// PaymentFactory.java
package com.example.payment;// 支付工厂接口 (抽象创建者)
public interface PaymentFactory {Payment createPayment();
}
具体工厂 (AlipayFactory, WechatPayFactory)
// alipay_factory.go
package payment// AlipayFactory 支付宝工厂 (具体创建者)
type AlipayFactory struct{}func (af *AlipayFactory) CreatePayment() Payment {return &Alipay{}
}// wechatpay_factory.go
package payment// WechatPayFactory 微信支付工厂 (具体创建者)
type WechatPayFactory struct{}func (wf *WechatPayFactory) CreatePayment() Payment {return &WechatPay{}
}
// AlipayFactory.java
package com.example.payment;// 支付宝工厂 (具体创建者)
public class AlipayFactory implements PaymentFactory {@Overridepublic Payment createPayment() {System.out.println("支付宝工厂: 创建支付宝支付对象");return new Alipay();}
}// WechatPayFactory.java
package com.example.payment;// 微信支付工厂 (具体创建者)
public class WechatPayFactory implements PaymentFactory {@Overridepublic Payment createPayment() {System.out.println("微信支付工厂: 创建微信支付对象");return new WechatPay();}
}
客户端使用
// main.go (示例用法)
/*
package mainimport ("fmt""./payment" // 假设 payment 包在当前目录下
)func main() {var factory payment.PaymentFactory// 用户选择支付宝支付factory = &payment.AlipayFactory{}alipay := factory.CreatePayment()fmt.Println(alipay.Pay(100.50))// 用户选择微信支付factory = &payment.WechatPayFactory{}wechatPay := factory.CreatePayment()fmt.Println(wechatPay.Pay(88.88))// 如果要增加一种新的支付方式,比如银联支付 UnionPay// 1. 创建 UnionPay struct 实现 Payment 接口// 2. 创建 UnionPayFactory struct 实现 PaymentFactory 接口// 3. 客户端就可以使用: factory = &payment.UnionPayFactory{}
}
*/
// Main.java (示例用法)
/*
package com.example;import com.example.payment.*;public class Main {public static void main(String[] args) {PaymentFactory factory;// 用户选择支付宝支付factory = new AlipayFactory();Payment alipay = factory.createPayment();System.out.println(alipay.pay(100.50));// 用户选择微信支付factory = new WechatPayFactory();Payment wechatPay = factory.createPayment();System.out.println(wechatPay.pay(88.88));// 如果要增加一种新的支付方式,比如银联支付 UnionPay// 1. 创建 UnionPay class 实现 Payment 接口// 2. 创建 UnionPayFactory class 实现 PaymentFactory 接口// 3. 客户端就可以使用: factory = new UnionPayFactory();}
}
*/
7. 与简单工厂模式的区别
你可能听说过“简单工厂模式”(Simple Factory Pattern),它不是23种经典GoF设计模式之一,但经常被提及。
-
简单工厂模式:有一个专门的工厂类,根据传入的参数来决定创建哪种产品类的实例。这个工厂类通常包含一个静态方法或一个实例方法,里面用
if/else
或switch
来判断。- 优点:简单直观。
- 缺点:当需要增加新产品时,必须修改工厂类的判断逻辑,违反了开闭原则。工厂类职责过重。
-
工厂方法模式:定义一个创建对象的接口,但将实际创建工作推迟到子类。每个具体工厂负责创建一个具体产品。
- 优点:符合开闭原则,扩展性好。
- 缺点:类的数量会增加。
核心区别:简单工厂只有一个工厂类,而工厂方法模式有一组工厂类(一个抽象工厂和多个具体工厂)。工厂方法模式将简单工厂的内部判断逻辑转移到了客户端(客户端选择使用哪个具体工厂),或者说,将判断的职责分散到了各个具体工厂子类中。
8. 总结
工厂方法模式通过引入抽象工厂和具体工厂,将对象的创建过程延迟到子类中进行,从而实现了更好的解耦和扩展性。当你的系统中需要创建一系列相关的或相互依赖的对象,并且希望将这些对象的创建与使用分离时,工厂方法模式是一个非常好的选择。
记住它的核心:定义创建接口,子类实现创建。
相关文章:

零基础设计模式——创建型模式 - 工厂方法模式
第二部分:创建型模式 - 工厂方法模式 (Factory Method Pattern) 上一节我们学习了单例模式,它关注如何保证一个类只有一个实例。现在,我们来看另一个重要的创建型模式——工厂方法模式。它关注的是如何创建对象,但将创建的决定权…...
LeetCode 404.左叶子之和的递归求解:终止条件与递归逻辑的深度剖析
一、题目解析:左叶子的定义与递归求解思路 题目描述 LeetCode 404. 左叶子之和要求计算二叉树中所有左叶子节点的值之和。左叶子的严格定义是:如果一个节点是其父节点的左子节点,并且它本身没有左右子节点,则称为左叶子。 关键…...
蓝桥杯5130 健身
问题描述 小蓝要去健身,他可以在接下来的 1∼n 天中选择一些日子去健身。 他有 m 个健身计划,对于第 i 个健身计划,需要连续的 天,如果成功完成,可以获得健身增益 si ,如果中断,得不到任何…...

电商虚拟户:重构资金管理逻辑,解锁高效归集与智能分账新范式
一、电商虚拟户的底层架构与核心价值 在数字经济浪潮下,电商交易的复杂性与日俱增,传统账户体系已难以满足平台企业对资金管理的精细化需求。电商虚拟户作为基于银行或持牌支付机构账户体系的创新解决方案,通过构建“主账户子账户”的虚拟账户…...

腾讯2025年校招笔试真题手撕(二)
一、题目 最近以比特币为代表的数字货币市场非常动荡,聪明的小明打算用马尔科夫链来建模股市。如图所示,该模型有三种状态:“行情稳定”,“行情大跌”以及“行情大涨”。每一个状态都以一定的概率转化到下一个状态。比如…...
DeepSeek快速搭建个人网页
一、环境准备 注册DeepSeek账号(https://www.deepseek.com/)安装VSCode插件:DeepSeek Coder准备基础开发环境:# 推荐使用Node.js环境 npm install -g live-server二、三步搭建基础框架 步骤1:生成基础模板 在DeepSeek对话框输入: 生成一个响应式个人网页的HTML模板,包…...

安装完dockers后就无法联网了,执行sudo nmcli con up Company-WiFi,一直在加载中
Docker服务状态检查 执行 systemctl status docker 确认服务是否正常 若未运行,使用 sudo systemctl start docker && sudo systemctl enable docker 网络配置冲突 Docker会创建docker0虚拟网桥,可能与宿主机网络冲突 检查路由表 ip route sho…...

【深度学习新浪潮】2025年谷歌I/O开发者大会keynote观察
1. 2025年谷歌I/O开发者大会keynote重点信息 本次Google I/O大会的核心策略是降低AI使用门槛与加速开发者创新,通过端侧模型(Gemini Nano)、云端工具(Vertex AI)和基础设施(TPU)的全链路优化,进一步巩固其在生成式AI领域的领先地位。同时,高价订阅服务和企业级安全功…...
小球弹弹弹
一球从100米高度自由落下,每次落地后反跳回原高度的一半,再落下。求它在第十次落地时,共经过多少米?第十次反弹多高? 从第一次弹起到第二次落地前经过的路程为前一次弹起最高高度的一半乘以2,加上前面经过…...

案例分享——福建洋柄水库大桥智慧桥梁安全监测
项目背景 洋柄水库桥位于社马路(社店至马坪段)上,桥梁全长285m,桥梁中心桩号K15082跨径组合为 14x20m,全桥宽:33.8m,分左右双幅:上部结构采用空心板梁:桥采用柱式墩。 通过对桥梁结构长时间的定期观测,掌握桥梁在混凝…...

鸿蒙操作系统架构:构建全场景智慧生态的分布式操作系统
鸿蒙操作系统(HarmonyOS)是华为推出的面向全场景的分布式操作系统,旨在为智能手机、智能家居、智能穿戴、车机等多种设备提供统一的操作系统平台。鸿蒙架构的核心设计理念是“一次开发,多端部署”,通过分布式技术实现设备间的无缝协同。本文将深入探讨鸿蒙的分层架构、分布…...

NBA足球赛事直播源码体育直播M35模板赛事源码
源码名称:NBA足球赛事直播源码体育直播M35模板赛事源码 开发环境:帝国cms7.5 空间支持:phpmysql 带软件采集,可以挂着自动采集发布,无需人工操作! 演示地址:https://www.52muban.com/shop/184…...

自动化测试报告工具
自动化测试报告工具大全与实战指南 📊🔥 在自动化测试流程中,测试用例的执行只是第一步,而测试报告的生成与可视化则是闭环的重要一环。无论是个人项目还是团队协作,高质量的测试报告都能帮助我们快速定位问题、衡量测…...
Elasticsearch 实战面试题,每个题目都会单独解析
Elasticsearch 在 Java 中最常用的客户端是什么?如何初始化一个 RestHighLevelClient?如何用 Spring Boot 快速集成 Elasticsearch?Spring Data Elasticsearch 如何定义实体类与索引的映射? ES的倒排索引和正排索引的区别及适用场…...

python 中 SchedulerManager 使用踩坑
问题: 服务中我写了多个定时任务,如下: 发现到了定时时间,下面的任务就是不执行,,最后一个任务一个任务注释掉来测,发现了问题, self.scheduler_manager.add_cron_job(SearchQualit…...
Python后端框架新星Robyn:性能与开发体验的双重革命
引言:Python后端框架的进化之路 在Web开发领域,Python生态长期被Flask、Django等经典框架主导。随着异步编程需求的增长和高并发场景的普及,开发者对框架性能提出了更高要求。2023年,一款名为Robyn的新型Web框架横空出世…...

人工智能解析:技术革命下的认知重构
当生成式AI能够自主创作内容、设计方案甚至编写代码时,我们面对的不仅是工具革新,更是一场关于智能本质的认知革命。人工智能解析的核心,在于理解技术如何重塑人类解决问题和创造价值的底层逻辑——这种思维方式的转变,正成为数字…...

【Linux】基础开发工具
文章目录 一、软件包管理器1. Linux下安装软件补充知识1:操作系统的生态补充知识2:我的云服务器是怎么知道去哪找软件包的呢? 2. 查看软件包3. 安装软件4. 卸载软件5. 安装源 二、编辑器Vim1. 命令模式 三、编译器gcc / g1. 程序编译流程补充…...

OpenCV计算机视觉实战(7)——色彩空间详解
OpenCV计算机视觉实战(7)——色彩空间详解 0. 前言1. RGB/BGR 色彩空间2. HSV / Lab 色彩空间3. 颜色直方图分析与可视化小结系列链接 0. 前言 本文深入探讨了三种常见色彩空间:RGB/BGR、HSV 与 CIELAB,并介绍了 OpenCV 中色彩空…...
体育直播网站如何实现实时数据
⚽ 你是否曾好奇: 当你在看足球直播时,进球瞬间比分立刻刷新;篮球比赛中,球员数据实时跳动……这些毫秒级的赛事数据,究竟是如何"飞"到你手机上的? 今天,我们就来扒一扒体育直播网站…...

【AI模型学习】上/下采样
文章目录 分割中的上/下采样下采样SegFormer和PVT(使用卷积)Swin-Unet(使用 Patch Merging) 上采样SegFormer(interpolate)Swin-Unet(Patch Expanding)逐级interpolate的方式反卷的方…...

Unity Shader入门(更新中)
参考书籍:UnityShader入门精要(冯乐乐著) 参考视频:Bilibili《Unity Shader 入门精要》 写在前面:前置知识需要一些计算机组成原理、线性代数、Unity的基础 这篇记录一些学历过程中的理解和笔记(更新中&…...

嵌入式学习的第二十六天-系统编程-文件IO+目录
一、文件IO相关函数 1.read/write cp #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <stdio.h> #include<unistd.h> #include<string.h>int main(int argc, char **argv) {if(argc<3){fprintf(stderr, …...

珠宝课程小程序源码介绍
这款珠宝课程小程序源码,基于ThinkPHPFastAdminUniApp开发,功能丰富且实用。ThinkPHP提供稳定高效的后台服务,保障数据安全与处理速度;FastAdmin助力快速搭建管理后台,提升开发效率;UniApp则让小程序能多端…...

KNN模型思想与实现
KNN算法简介 核心思想:通过样本在特征空间中k个最相似样本的多数类别来决定其类别归属。"附近的邻居确定你的属性"是核心逻辑 决策依据:采用"多数表决"原则,即统计k个最近邻样本中出现次数最多的类别 样本相似性度量 …...
【信息系统项目管理师】第15章:项目风险管理 - 55个经典题目及详解
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第…...

fscan教程1-存活主机探测与端口扫描
实验目的 本实验主要介绍fscan工具信息收集功能,对同一网段的主机进行存活探测以及常见服务扫描。 技能增长 通过本次实验的学习,了解信息收集的过程,掌握fscan工具主机探测和端口扫描功能。 预备知识 fscan工具有哪些作用? …...
蓝桥杯1447 砝码称重
问题描述 你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。 请你计算一共可以称出多少种不同的重量? 注意砝码可以放在天平两边。 输入格式 输入的第一行包含一个整数 N。 第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN…...

腾讯2025年校招笔试真题手撕(三)
一、题目 今天正在进行赛车车队选拔,每一辆赛车都有一个不可以改变的速度。现在需要选取速度差距在10以内的车队(车队中速度的最大值减去最小值不大于10),用于迎宾。车队的选拔按照的是人越多越好的原则,给出n辆车的速…...

怎样通过神经网络估计股票走向
本博文将教会你如何通过神经网络建立股票模型并对其进行未来趋势估计,尽管博主已通过此方法取得一定利润,但是建议大家不要过分相信AI。本博文仅用于代码学习,请大家谨慎投资。 一、通过爬虫爬取股票往年数据 在信息爆炸的当今时代…...