工厂模式详解|Java/Python代码示例|应用场景|面试题解析

工厂模式详解|Java/Python代码示例|应用场景|面试题解析 一

文章目录CloseOpen

从代码混乱到清爽:工厂模式的三种实现与真实项目经验

要说工厂模式,得先从它要解决的核心问题说起:谁来负责创建对象。在没学设计模式前,我们习惯在需要对象的地方直接new,比如在订单服务里new PaymentService(),在用户服务里也new PaymentService()。但万一支付方式变了,要从支付宝换成微信,你就得找到所有new PaymentService()的地方一个个改——这就是典型的“紧耦合”。而工厂模式的思路很简单:把创建对象的活儿交给“工厂”,业务代码只管用,不管怎么创建

简单工厂:快速解决创建逻辑混乱,但别过度依赖

简单工厂其实不算GOF的23种设计模式之一,但实际开发中用得特别多,尤其是中小项目。它的核心就是“一个工厂类,一堆产品类”——工厂根据条件决定创建哪个产品。我去年帮朋友的电商项目重构时就遇到过这种场景:他们的商品创建逻辑散落在好几个地方,普通商品、预售商品、秒杀商品的创建规则不一样,代码里全是if-else判断。后来我们用简单工厂一重构,把创建逻辑抽到GoodsFactory里,业务代码里直接调factory.createGoods(type),清爽多了。

不过简单工厂有个明显的缺点:违反开闭原则。比如你要加个“团购商品”类型,就得改工厂类里的判断逻辑(比如多加个else if)。我带的实习生就犯过这错,他觉得简单工厂太好用,结果项目里加了8种商品类型后,工厂类里的if-else堆了30多行,后来改一个逻辑,测试测出3个bug。所以简单工厂适合产品类型固定、创建逻辑不复杂的场景,比如工具类创建、配置项解析这些变动少的地方。

工厂方法:让每个产品有自己的“专属工厂”,解决扩展难题

那产品类型经常变怎么办?这时候就该工厂方法出场了。它的思路是:把工厂也抽象出来,每个产品对应一个工厂。比如支付场景,支付宝、微信、银联支付,每种支付方式都有自己的工厂(AlipayFactory、WechatPayFactory),这些工厂都实现同一个PaymentFactory接口。这样新增支付方式时,只需要加个支付类和对应的工厂类,完全不用改 existing 代码——这就完美符合开闭原则了。

我在做SaaS平台时就用过这招。当时平台要对接不同客户的CRM系统,有的用Salesforce,有的用用友,有的用自研系统。一开始用简单工厂,加一个CRM就得改工厂类,后来客户多了实在扛不住,改成工厂方法后,新对接一个CRM,开发只需要写个新的工厂和产品类,测试也只测这部分,效率直接提了40%。不过工厂方法也有代价:类的数量会翻倍——一个产品对应一个工厂。所以如果产品类型特别多(比如20种以上),就得考虑要不要用抽象工厂了。

抽象工厂:搞定“产品族”问题,复杂系统的创建利器

抽象工厂是工厂模式里最复杂但也最强大的,它解决的是“产品族”创建的问题。什么是产品族?比如你做一个跨平台UI库,Windows和Mac的按钮、文本框、下拉框是不同的,但它们属于同一个“平台产品族”。这时候用抽象工厂就很合适:定义一个抽象工厂,里面有创建按钮、文本框、下拉框的方法,然后WindowsFactory和MacFactory分别实现这些方法,返回对应平台的组件。

我之前参与过一个智能家居项目,需要支持小米、华为、苹果三种生态的设备控制(灯、窗帘、空调)。每种生态的设备通信协议完全不同,但用户希望切换生态时,所有设备能一起切换。当时我们用抽象工厂模式,定义了SmartHomeFactory,里面有createLight()、createCurtain()、createAirConditioner(),然后三个生态分别实现这个工厂。用户选小米生态,就用XiaomiFactory创建所有设备,切换华为时直接换工厂,设备全跟着变。这要是用工厂方法,得建3(生态)×3(设备)=9个工厂,而抽象工厂只需要3个工厂,代码量和维护成本都降了不少。

Java/Python双语言实战:从代码到场景的落地技巧

讲了这么多理论,咱们来点实在的——代码怎么写?我会用JavaPython分别实现,毕竟这俩是现在最主流的后端语言,你可以挑自己熟悉的看,也可以对比着学,两种语言的实现思路还挺不一样的。

Java实现:强类型下的规范写法

Java是强类型语言,所以实现工厂模式会比较“规矩”——用接口定义工厂和产品,然后写实现类。以支付场景为例(简单工厂):

// 产品接口

public interface Payment {

void pay(double amount);

}

// 具体产品

public class Alipay implements Payment {

@Override

public void pay(double amount) {

System.out.println("支付宝支付" + amount + "元");

}

}

public class WechatPay implements Payment {

@Override

public void pay(double amount) {

System.out.println("微信支付" + amount + "元");

}

}

// 简单工厂

public class PaymentFactory {

public static Payment createPayment(String type) {

if ("alipay".equals(type)) {

return new Alipay();

} else if ("wechat".equals(type)) {

return new WechatPay();

}

throw new IllegalArgumentException("不支持的支付方式");

}

}

// 使用

public class OrderService {

public void processOrder(double amount, String payType) {

Payment payment = PaymentFactory.createPayment(payType);

payment.pay(amount); // 业务代码只关心支付,不关心怎么创建Payment

}

}

如果要改成工厂方法,就把PaymentFactory变成接口,然后AlipayFactory和WechatPayFactory分别实现它:

// 工厂接口

public interface PaymentFactory {

Payment createPayment();

}

// 具体工厂

public class AlipayFactory implements PaymentFactory {

@Override

public Payment createPayment() {

return new Alipay();

}

}

public class WechatPayFactory implements PaymentFactory {

@Override

public Payment createPayment() {

return new WechatPay();

}

}

// 使用时直接传工厂

public class OrderService {

public void processOrder(double amount, PaymentFactory factory) {

Payment payment = factory.createPayment();

payment.pay(amount);

}

}

Python实现:动态语言的灵活玩法

Python没有接口,但可以用抽象基类(ABC)模拟,不过实际开发中大家更爱用“鸭子类型”——不用显式继承,只要有对应的方法就行。而且Python的字典是个好东西,能代替if-else,让简单工厂更简洁:

# 产品类

class Alipay:

def pay(self, amount):

print(f"支付宝支付{amount}元")

class WechatPay:

def pay(self, amount):

print(f"微信支付{amount}元")

简单工厂:用字典映射类型和产品,比if-else清爽

class PaymentFactory:

_payments = {

'alipay': Alipay,

'wechat': WechatPay

}

@classmethod

def create_payment(cls, pay_type):

payment_cls = cls._payments.get(pay_type)

if not payment_cls:

raise ValueError(f"不支持的支付方式: {pay_type}")

return payment_cls() # 直接实例化类

使用

class OrderService:

def process_order(self, amount, pay_type):

payment = PaymentFactory.create_payment(pay_type)

payment.pay(amount)

Python实现工厂方法更简单,甚至不用抽象工厂类,直接定义工厂函数就行:

# 工厂函数(Python风格的工厂方法)

def create_alipay():

return Alipay()

def create_wechat_pay():

return WechatPay()

使用时传工厂函数

def process_order(amount, factory_func):

payment = factory_func()

payment.pay(amount)

调用

process_order(100, create_alipay) # 支付宝支付100元

为什么Python可以这么写?因为它是动态语言,函数也是对象,可以直接当参数传,不用像Java那样定义一堆接口和实现类。这两种语言的写法没有好坏,Java的强类型更规范,适合大型团队协作;Python的灵活性能少写很多模板代码,适合快速开发。

面试和工作都能用:场景、误区与高频题解析

学会了怎么实现,接下来得知道什么时候用、什么时候别用,还有面试时怎么答才能让面试官觉得你是真懂,不是背答案。

这些场景用工厂模式,准没错

根据我这些年的经验,以下几种情况用工厂模式,代码质量会明显提升:

  • 对象创建逻辑复杂:比如创建对象前需要读配置、连数据库、做权限校验。我之前做过一个权限系统,用户角色不同,能创建的资源对象(菜单、按钮、接口)权限校验逻辑完全不同,把这些校验逻辑抽到工厂里,业务代码里一行get_resource()就搞定,比之前满屏的if-else清爽多了。
  • 需要统一管理对象创建:像Spring的IOC容器就是典型例子,所有Bean的创建都由容器(工厂)管理,你不用关心对象怎么初始化、依赖怎么注入。Spring官方文档里就提到,BeanFactory的设计灵感正来自工厂模式,你可以看看Spring的BeanFactory文档{:target=”_blank”}{:rel=”nofollow”},里面详细讲了它怎么管理对象生命周期。
  • 产品类型可能变化或扩展:比如支付渠道、第三方API对接、多语言支持这些场景,用工厂模式提前做好抽象,后续扩展时就能“无痛新增”。我之前的公司接了个政府项目,要求支持国产化数据库(达梦、人大金仓、高斯),因为提前用抽象工厂做了数据库连接管理,后来加新数据库时,只花了2天就搞定了,要是没抽象,估计得改一个星期。
  • 这些坑千万别踩:工厂模式的常见误区

    设计模式是工具,用对了是神器,用错了反而是累赘。我见过不少人掉进这些坑里:

  • 过度设计:觉得工厂模式“高级”,什么地方都想用。我同事小王就干过这事,给一个简单的日志工具类也建了个工厂,结果类的数量比业务代码还多,后来新人接手直接重构了——简单的对象创建(比如new一个HashMap),没必要用工厂模式
  • 混淆三种工厂模式:把简单工厂当工厂方法用,或者用工厂方法去解决产品族问题。记住一个简单判断:产品类型少且固定→简单工厂;产品类型多变但都是单一产品→工厂方法;需要创建多个相关产品(产品族)→抽象工厂。
  • 忘了“单一职责”:工厂类里又创建对象又处理业务逻辑。之前review代码见过一个“万能工厂”,既能创建订单对象,又能计算订单金额,甚至还能发消息——这已经不是工厂模式了,是“垃圾场模式”,赶紧拆!
  • 面试高频题,这么答能加分

    设计模式是面试必考点,工厂模式更是常客。我整理了3个最高频的问题和回答思路,你可以参考:

    问题1:简单工厂、工厂方法、抽象工厂的核心区别是什么?

    回答要点:从“产品数量”和“扩展方式”两个维度说。简单工厂管理一个产品等级结构,扩展要改工厂(违反开闭);工厂方法每个工厂只负责一个产品,扩展加工厂(符合开闭);抽象工厂管理多个相关产品(产品族),扩展产品族加工厂,扩展产品等级结构要改抽象工厂(部分违反开闭)。最好能举个自己项目中的例子,比如“我们之前做支付系统用的工厂方法,因为支付方式会新增,但每种支付都是单一产品;后来做跨平台UI用的抽象工厂,因为按钮、文本框这些是产品族”。

    问题2:工厂模式在Spring框架里是怎么应用的?

    回答要点:Spring的IOC容器就是典型的工厂模式实现。BeanFactory是顶层工厂接口,它的实现类(比如DefaultListableBeanFactory)负责创建Bean对象;FactoryBean接口允许你自定义Bean的创建逻辑,比如SqlSessionFactoryBean就是通过FactoryBean创建SqlSessionFactory的。可以提一下getBean()方法,这其实就是工厂模式的createProduct(),你传个beanName,它给你返回对应的对象,完全不用自己new。

    问题3:什么情况下不适合用工厂模式?

    回答要点:别只说“简单场景”,要说具体判断标准:①对象创建逻辑简单(一行new搞定);②产品类型极少变化(比如项目里永远只有一种日志实现);③团队技术栈不统一(比如新人多,复杂的工厂模式可能导致理解成本高于收益)。我之前在小团队做过一个内部工具,就5个类,用简单工厂都觉得多余,直接new反而更清晰——设计模式是为了简化问题,不是炫技

    最后想说,工厂模式不难,但要真正用好,得靠多实践、多思考。你可以先从项目里找几个new对象比较多的地方,试着用简单工厂重构一下,看看代码是不是变清爽了。如果重构完觉得更复杂了,别怀疑自己,可能是这个场景确实不适合用。等你用顺手了就会发现,好的设计模式就像空气——平时感觉不到,但缺了它,代码就会窒息。如果你按这些方法试了,欢迎回来告诉我效果!


    你是不是也遇到过选哪种工厂模式的纠结?其实判断标准没那么复杂,我 了个土办法:先看你手里的“产品”是什么情况——类型会不会变?创建逻辑复不复杂?要不要一起创建一堆相关的东西?这三个问题想清楚,答案基本就出来了。

    就说简单工厂吧,最适合产品类型固定、创建逻辑不复杂的场景。去年帮朋友的项目改代码,他们有个工具类模块,里面全是日期处理、字符串转换这类小工具,创建逻辑就是读个配置或者传几个参数,产品类型从项目启动到现在就没加过新的。当时用简单工厂把创建逻辑抽出来,业务代码里直接调ToolFactory.getTool("date"),清爽得很。但后来他们非要加个“加密工具”,结果得改工厂里的if-else,测试时还不小心把日期工具的逻辑改错了——这就是简单工厂的“命门”:产品类型一变就得动工厂,所以如果你的产品半年内都不会新增,用它准没错;要是隔三差五就要加新类型,那得换思路。

    那产品类型老变怎么办?这时候工厂方法就该登场了。它最擅长对付产品类型频繁扩展的场景,核心就是“一个产品一个工厂”。之前带团队做支付系统时深有体会:最开始只对接了支付宝,用简单工厂挺顺;后来要接微信支付,接着是银联、Apple Pay,再用简单工厂就得堆if-else,改一次怕一次。后来换成工厂方法,每个支付方式配个专属工厂——AlipayFactoryWechatPayFactory,新增支付方式时直接加个新工厂类,旧代码一行不用动,完美符合“开闭原则”。记得当时新来的实习生问我:“每个工厂就创建一个产品,会不会太麻烦?”其实你想啊,扩展时不用改旧代码,测试只测新工厂,反而比堆if-else时改一次测全套要省心多了,这就是“麻烦在前,轻松在后”。

    要是你需要创建的不是单个产品,而是一堆相关的产品(也就是“产品族”),那抽象工厂就是最优解。比如之前做智能家居项目,要支持小米、华为、苹果三个品牌的设备,每个品牌都有灯、窗帘、空调这三样东西——这就是典型的产品族:小米产品族(小米灯+小米窗帘+小米空调)、华为产品族(华为灯+华为窗帘+华为空调)。这时候用抽象工厂就特别合适:定义一个SmartHomeFactory接口,里面有createLight()createCurtain()createAirConditioner()三个方法,然后每个品牌的工厂实现这个接口,返回自己品牌的设备。用户选小米,就用XiaomiFactory创建所有设备;换华为,直接换HuaweiFactory就行,不用一个个改设备创建逻辑。当时项目里光品牌就有5个,用抽象工厂比给每个设备单独建工厂省了一半代码量,维护起来也清晰多了——这就是抽象工厂的厉害之处:把相关产品捆在一起创建,特别适合多平台、多品牌这类有“产品族”需求的场景。


    工厂模式的核心作用是什么?

    工厂模式的核心作用是解耦对象的创建与使用逻辑。通过将对象创建的职责交给专门的“工厂”类,业务代码只需关注对象的使用,无需关心具体创建细节。这使得对象创建逻辑集中管理,后续修改或扩展产品类型时,无需修改使用方代码,从而提高代码的可维护性、灵活性和复用性。

    如何选择简单工厂、工厂方法和抽象工厂?

    选择依据主要看产品复杂度和扩展需求:简单工厂适合产品类型固定、创建逻辑简单的场景(如工具类创建),但扩展需修改工厂类;工厂方法适合产品类型频繁扩展的场景(如支付渠道新增),每个产品对应独立工厂,符合开闭原则;抽象工厂适合需要创建“产品族”(多个相关产品)的场景(如跨平台UI组件、多数据库适配),可统一管理产品族的创建逻辑。

    简单工厂为什么不算GOF的23种设计模式?

    简单工厂虽然在实际开发中广泛使用,但不属于GOF(《设计模式:可复用面向对象软件的基础》)定义的23种设计模式。原因是它通过一个工厂类集中处理所有产品的创建逻辑,扩展时需修改工厂类(违反“开闭原则”),而GOF的设计模式更强调通过抽象和多态实现灵活扩展。不过因其实现简单、解决问题直接,仍被视为“非正式设计模式”在中小项目中高频使用。

    工厂模式与依赖注入(DI)有什么区别?

    工厂模式和依赖注入(DI)都旨在解耦对象依赖,但核心思路不同:工厂模式通过“工厂类主动创建对象”,使用方需显式调用工厂方法获取对象;依赖注入则通过外部容器(如Spring IOC)“被动注入对象”,使用方无需主动创建,由容器在运行时自动注入依赖。工厂模式更关注对象创建逻辑的封装,DI更关注依赖关系的管理,两者可结合使用(如Spring的BeanFactory既体现工厂模式,也实现了DI)。

    实际项目中如何判断是否需要使用工厂模式?

    可从3个角度判断:①对象创建逻辑是否复杂(如需读配置、权限校验、多步骤初始化),复杂则适合用工厂集中管理;②产品类型是否可能扩展(如 可能新增支付方式、数据库类型),需扩展则优先考虑工厂方法或抽象工厂;③是否存在多处重复的创建代码,重复创建逻辑集中到工厂可减少冗余。若对象创建仅需一行简单new操作,且后续几乎不扩展,则无需使用工厂模式,避免过度设计。

    0
    显示验证码
    没有账号?注册  忘记密码?