设计模式系列:创建型-工厂方法模式(Factory Method Pattern)
简介
工厂方法模式(Factory Method Pattern)是一种常用的创建型设计模式,它提供了一种创建对象的最佳方式。这种模式属于类创建型模式,是虚拟构造器(Virtual Constructor)模式或多态性工厂模式的别称。
在工厂方法模式中,定义一个用于创建对象的接口,让子类决定实例化哪一个类。父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。这种设计模式的目的是将类实例化操作延迟到子类中完成,即由子类来决定应该实例化哪个类。
使用工厂方法模式可以带来很多好处。首先,用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。其次,灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。此外,它是一种典型的解耦框架,高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
在实际应用中,工厂方法模式可以用于各种需要创建对象的场景,例如创建数据库连接、网络连接、游戏对象等。通过使用工厂方法模式,可以将对象的创建和使用分离,提高代码的可维护性和可复用性。同时,它还可以降低系统的耦合度,使得系统更加灵活和可扩展。然而,过度使用工厂方法模式也可能会导致代码变得复杂和难以理解。因此,在使用时需要权衡利弊,合理运用。
结构
工厂方法模式的主要角色:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
案例实现
需求:设计一个咖啡店点餐系统。
设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。
具体类的设计如下:
在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。
使用工厂方法模式对上例进行改进,类图如下:
代码如下:
抽象工厂:
public interface CoffeeFactory {
Coffee createCoffee();
}
具体工厂:
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
咖啡店类:
public class CoffeeStore {
private CoffeeFactory factory;
public CoffeeStore(CoffeeFactory factory) {
this.factory = factory;
}
public Coffee orderCoffee(String type) {
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addsugar();
return coffee;
}
}
从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。
工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
优缺点
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
缺点:
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
源码中的应用
JDK
java.util.Calendar 类
Java 中的 Calendar
类是用于操作日期和时间的类,它提供了一个静态工厂方法 getInstance()
,用于获取 Calendar
对象的实例。根据不同的时区和区域设置,getInstance()
方法会返回一个 Calendar
对象的实例,而无需直接调用其构造函数。
Calendar calendar = Calendar.getInstance();
java.util.concurrent.Executors 类
Java 中的 Executors
类是用于创建线程池的工具类,它提供了一系列静态工厂方法来创建不同类型的线程池。例如,newFixedThreadPool()
方法用于创建固定大小的线程池,newCachedThreadPool()
方法用于创建根据需求创建新线程的线程池,等等。
ExecutorService executor = Executors.newFixedThreadPool(10);
java.util.regex.Pattern 类
Java 中的 Pattern
类表示一个正则表达式的编译结果。它提供了一个工厂方法 compile()
,用于根据给定的正则表达式字符串创建一个 Pattern
对象的实例。compile()
方法根据指定的正则表达式字符串返回一个 Pattern
对象的实例。
Pattern pattern = Pattern.compile("regex");
java.nio.charset.Charset 类
Java 中的 Charset
类表示一个字符编码方案。它提供了一个工厂方法 forName()
,用于根据字符编码名称获取 Charset
对象的实例。forName()
方法根据指定的字符编码名称返回一个 Charset
对象的实例。
Charset charset = Charset.forName("UTF-8");
Spring
BeanFactory 接口及其实现类
在 Spring 框架中,BeanFactory 接口是用于管理 Bean 对象的核心接口。它定义了一系列的工厂方法,用于创建和获取 Bean 对象的实例。Spring 框架提供了多种 BeanFactory 的实现类,如 XmlBeanFactory、DefaultListableBeanFactory 等,这些实现类通过工厂方法来创建和管理 Bean 对象。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
ApplicationContext 接口及其实现类
在 Spring 框架中,ApplicationContext 接口是 BeanFactory 接口的扩展,它提供了更多的功能,如国际化、事件发布、AOP 等。ApplicationContext 接口也定义了一系列的工厂方法,用于创建和获取 Bean 对象的实例。Spring 框架提供了多种 ApplicationContext 的实现类,如 ClassPathXmlApplicationContext、AnnotationConfigApplicationContext 等。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
FactoryBean 接口及其实现类
在 Spring 框架中,FactoryBean 接口是一个特殊的工厂接口,用于创建特定类型的 Bean 对象。FactoryBean 接口定义了一个工厂方法 getObject()
,用于创建 Bean 对象的实例。Spring 框架提供了多种 FactoryBean 的实现类,如 BeanFactory、ListableBeanFactory 等。
public class MyFactoryBean implements FactoryBean<MyBean> {
@Override
public MyBean getObject() throws Exception {
// 创建并返回 MyBean 对象的实例
return new MyBean();
}
}
微信:17873041739
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?