大家好✋,我是知识汲取者😄,今天给大家带来一篇有关工厂模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性、可移植性、可维护性,同时还能让你的代码更加精炼,具备艺术美感。
在生活中资本家为了提高效率,将很多人组合到一起形成流水线,从而达到批量生产,于是工厂就诞生了。同样的,在代码的世界,有时候对象创建很频繁,为了提高效率,我们就会想着将对象的创建抽象出来形成一个类,这个类我们将其称之为工厂类。当然这就是工厂的粗略之谈,如果你想跟详细了解工厂模式,就请看完这篇文章吧,相信你一定能有所收获的😉,话不多说大家抓紧时间开搞。
推荐阅读:
由于博主水平有限,如有纰漏、错误还请您能给予指正😊
什么是工厂模式?
工厂模式(Factory Pattern)是一种创建型模式,它将对象创建的具体过程给屏蔽,将对象的创建交给工厂,工厂会根据条件创建不同的对象
工厂模式的作用:将”类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦,即:使用者可直接消费产品而不需要知道其生产的细节
工厂模式的优缺点
优点:
……
缺点:提高了系统的复杂度。没多一个对象,就需要多一个工厂类
工厂模式的应用场景
……
生活中的应用:产品在工厂生产,用户只管使用而不需要关注产品的生产过程
Spring中的应用:在创建SqlSession对象时,是使用工厂模式构建的
工厂模式的分类(以下三者抽象程度从上至下抽象程度逐渐提高,并且更具一般性)
特别说明:简单工厂其实不是一个标准的的设计模式。GOF 23 种设计模式中只有「工厂方法模式」与「抽象工厂模式」。简单工厂模式可以看为工厂方法模式的一种特例,为了统一整理学习,就都归为工厂模式
简单工厂模式(Simple Factory),又叫做静态工厂方法(Static Factory Method)模式,它的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
简单工厂模式的特点:根据给定的参数,决定究竟应该创建哪个具体类的对象(对象的创建由工厂决定)
简单工厂模式解决的问题:实现了对象创建和使用的职责分离
简单工厂模式存在的问题:违背了“开闭原则”,每次新增产品,都需要去修改原有的工厂类
简单工厂模式适用的场景:
简单工厂模式中的角色划分
简单工厂模式的核心步骤
实例:
问题描述:使用简单工厂模式设计一个可以创建不同几何形状(如圆形、方形和三角形等)绘图工具,每个几何形状都要有绘制draw()和擦除erase() 两个方法,要求在绘制不支持的几何图形时,提示一个UnSupportedShapeException
要求:设计一个简单工厂模式,通过修改配置文件让工厂类创建不同的对象

Step1:创建抽象产品
Shape:
package com.hhxy.product.shape;
/**
* @author ghp
* @date 2022/9/23
* @Title
* @description
*/
public interface Shape {
/**
* 形状的绘制方法
*/
void draw();
/**
* 形状的擦除方法
*/
void erase();
}
Step2:创建具体产品
1)Circular:
package com.hhxy.product.shape.imp;
import com.hhxy.product.shape.Shape;
/**
* @author ghp
* @date 2022/9/23
* @Title 圆形类
* @description
*/
public class Circular implements Shape {
/**
* 形状的绘制方法
*/
@Override
public void draw() {
System.out.println("绘制了一个圆形");
}
/**
* 形状的擦除方法
*/
@Override
public void erase() {
System.out.println("将绘制的圆形擦除了");
}
}
2)Rectangle
具体代码请参考Gitee或Github仓库,略……
3)Triangle
具体代码请参考Gitee或Github仓库,略……
Step3:创建工厂类
SimpleFactory
package com.hhxy.factory;
import com.hhxy.shape.Shape;
import com.hhxy.shape.imp.Circular;
import com.hhxy.shape.imp.Rectangle;
import com.hhxy.shape.imp.Triangle;
/**
* @author ghp
* @date 2022/9/23
* @Title 简单工厂类
* @description 用于创建Shape对象
*/
public class SimpleFactory {
public static Shape getShape(String shapeName){
Shape shape;
if("circular".equalsIgnoreCase(shapeName)){
shape = new Circular();
}else if ("rectangle".equalsIgnoreCase(shapeName)){
shape = new Rectangle();
}else if("triangle".equalsIgnoreCase(shapeName)){
shape = new Triangle();
}else{
throw new RuntimeException("UnSupportedShapeException");
}
return shape;
}
}
Step4:编写配置文件
shape-config.xml:
<config>
<shapeName>CircularshapeName>
<shapeName>RectangleshapeName>
<shapeName>TriangleshapeName>
<shapeName>123shapeName>
config>
Step5:编写配置文件读取类
ReadShapeConfig:
package com.hhxy.read;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
/**
* @author ghp
* @date 2022/9/23
* @Title
* @description 读取用于设置形状对象的配置文件
*/
public class ReadShapeConfig {
public static String getXML(){
try{
//1、将配置文件加载到内存中,获取DOM对象
//1.1 获取DOM解析器工厂对象DocumentBuilderFactory,用于创建DOM解析器
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//1.2 获取DOM解析器DocumentBuilder
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//1.3 加载配置文件
// Document document = documentBuilder.parse(new FileInputStream("Factory/src/shape-config.xml"));
//让代码和模块名进行解耦,比上面那种方法要更优
Document document = documentBuilder.parse(ReadShapeConfig.class.getResourceAsStream("/shape-config.xml"));
//2、获取配置文件中的数据
//2.1 从DOM中获取指定的结点的结点列表
NodeList nodeList = document.getElementsByTagName("shapeName");
//2.2 获取指定位置的结点
Node classNode = nodeList.item(1).getFirstChild();
//2.3 获取指定结点中的数据(排除空格)
String name = classNode.getNodeValue().trim();
//3、返回从配置文件中读取的形状的名字
return name;
} catch (Exception e) {
//如果异常就打印异常信息,同时返回一个空
e.printStackTrace();
return null;
}
}
}
Step6:测试
package com.hhxy.test;
import com.hhxy.factory.SimpleFactory;
import com.hhxy.read.ReadShapeConfig;
import com.hhxy.shape.Shape;
/**
* @author ghp
* @date 2022/9/23
* @Title 测试类1
* @description 用于测试简单工厂
*/
public class Test1 {
public static void main(String[] args) {
//1、读取配置文件,得到要创建对象的名字
String shapeName = ReadShapeConfig.getXML();
//2、使用简单工厂创建对象
Shape shape = SimpleFactory.getShape(shapeName);
//3、测试
shape.draw();
shape.erase();
}
}
测试结果:

前面的简单工厂模式虽然实现了对象创建和对象操作的分离,但是存在一个严重的不足之处,那就是每次要新增产品,都需要去修改工厂类,这是不符合"开闭原则"的。
针对这一现象,我们可以进行改进,从工厂类中抽象出一个工厂接口,不同的产品对应不同的工厂类,这些工厂类统一实现工厂接口,这样每次新增产品就只需要新增一个工厂类,然后实现一下工厂接口就可以了,而不需要像之前一样还要去修改工厂类的代码,这样就符合开闭原则了😃,使用这种方式实现的工厂模式我们称之为工厂方法模式(Factory Method),简称工厂模式,也称虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式
工厂方法模式的特点:将对象的创建与实例化延迟到子类(对象的创建由子类决定)
工厂方法模式解决的问题:不符合开闭原则(由于增加产品需要去修改原有代码,变成现在只需要添加代码)
工厂方法模式存在的问题:增加了系统的复杂度。每次新增产品都需要编写一个具体工厂类和具体产品类,系统中类变多了,自然而然就让系统的复杂度增多了
工厂方法模式的适用场景:
工厂方法模式中的角色划分:
工厂模式的核心步骤:
示例:
问题描述:宝马(BMW)工厂制造宝马汽车,奔驰(Benz)工厂制造奔驰汽车,使用工厂方法模式模拟改场景,并用Java语言实现。并且扩展一个比亚迪工厂生产比亚迪汽车
要求:设计一个工厂方法模式,抽象工厂对象能够根据配置文件中的内容创建不同的对象

Step1:创建抽象产品
Car:
package com.hhxy.product.car;
/**
* @author ghp
* @date 2022/9/26
* @Title
* @description
*/
public interface Car {
/**
* 汽车启动的方法
*/
void start();
}
Step2:创建具体产品
1)Benz
package com.hhxy.product.car.imp;
import com.hhxy.product.car.Car;
/**
* @author ghp
* @date 2022/9/26
* @Title
* @description
*/
public class Benz implements Car {
@Override
public void start() {
System.out.println("奔驰汽车启动了!");
}
}
2)Bmw
和上面类似,具体代码请参考Gitee或Github仓库,略……
Step3:创建抽象工厂
Factory:
package com.hhxy.factory;
import com.hhxy.product.car.Car;
/**
* @author ghp
* @date 2022/9/23
* @Title 工厂类接口
* @description 用于宝马类工厂和奔驰类工厂实现,
*/
public interface Factory {
/**
* 获取汽车对象的方法
*/
Car getCar();
}
Step4:创建具体工厂
1)BenzFactory:
package com.hhxy.factory.imp;
import com.hhxy.product.car.Car;
import com.hhxy.product.car.imp.Benz;
import com.hhxy.factory.Factory;
/**
* @author ghp
* @date 2022/9/23
* @Title 奔驰工厂类
* @description 用于生产奔驰类的实例化对象
*/
public class BenzFactory implements Factory {
/**
* 获取奔驰汽车对象的方法
*/
@Override
public Car getCar() {
return new Benz();
}
}
2)BmwFactory
和上面的类似,具体代码请参考Gitee或Github仓库,略……
Step5:编写配置文件
car-config.xml:
<config>
<factoryName>com.hhxy.factory.imp.BenzFactoryfactoryName>
<factoryName>com.hhxy.factory.imp.BmwFactoryfactoryName>
<factoryName>testfactoryName>
config>
Step6:编写配置文件读取类
ReadCarConfig:
package com.hhxy.read;
import com.hhxy.factory.Factory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Constructor;
/**
* @author ghp
* @date 2022/9/23
* @Title
* @description 读取用于设置汽车对象的配置文件,同时返回一个Factory对象
*/
public class ReadCarConfig {
public static Object getFactory(){
try{
//1、将配置文件加载到内存中,获取DOM对象
//1.1 获取DOM解析器工厂对象DocumentBuilderFactory,用于创建DOM解析器
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//1.2 获取DOM解析器DocumentBuilder
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//1.3 加载配置文件
// Document document = documentBuilder.parse(new FileInputStream("Factory/src/car-config.xml"));
//让代码和模块名进行解耦,比上面那种方法更优
Document document = documentBuilder.parse(ReadCarConfig.class.getResourceAsStream("/car-config.xml"));
//2、获取配置文件中的数据
//2.1 从DOM中获取指定的结点的结点列表
NodeList nodeList = document.getElementsByTagName("factoryName");
//2.2 获取指定位置的结点
Node classNode = nodeList.item(1).getFirstChild();
//2.3 获取指定结点中的数据(排除空格)
String factoryName = classNode.getNodeValue().trim();
//3、使用反射获取获取Factory对象
//3.1 获取类对象
Class cls = Class.forName(factoryName);
//3.2 获取该类对象的构造器对象
Constructor constructor = cls.getDeclaredConstructor();
//3.3 暴力反射,防止构造器私有化导致无法创建对象
constructor.setAccessible(true);
//3.4 获取工厂对象
Factory factory = (Factory) constructor.newInstance();
//4、返回通过配置文件获取的工厂对象
return factory;
} catch (Exception e) {
//如果异常就打印异常信息,同时返回一个空
e.printStackTrace();
throw new RuntimeException("未找到该工厂类,请检查配置文件或者添加一个工厂类!");
}
}
}
Step7:测试
package com.hhxy.test;
import com.hhxy.car.Car;
import com.hhxy.factory.Factory;
import com.hhxy.read.ReadCarConfig;
/**
* @author ghp
* @date 2022/9/26
* @Title 测试类2
* @description 用于测试工厂方法
*/
public class Test2 {
public static void main(String[] args) {
//1、读取配置文件,获取要创建对象的工厂对象
Factory factory = (Factory) ReadCarConfig.getFactory();
//2、使用工厂获取对应的汽车对象
Car car = factory.getCar();
//3、测试输出
car.start();
}
}
测试结果:

抽象工厂模式(Abstract Factory Pattern)提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式
前置知识:
- 产品族:指由同一个工厂生产的,位于不同产品等级结构中的一组产品
- 产品等级结构:产品等级结构指的是产品的继承结构,例如一个空调抽象类,它有海尔空调、格力空调、美的空调等一系列的子类,那么这个空调抽象类和他的子类就构成了一个产品等级结构
示例:
问题描述:小米,华为,联想四家公司都能够生产电视、手机、冰箱三件产品,,使用抽象工厂模式模拟以上场景,并用Java语言实现
要求:使用抽象工厂模式进行设计,通过读取配置文件获取工厂对象


Step1:创建抽象产品
具体代码请参考Gitee或Github仓库,略……
Step2:创建具体产品
具体代码请参考Gitee或Github仓库,略……
Step3:创建抽象工厂
AbstractFactory:
package com.hhxy.factory;
import com.hhxy.product.conditioner.Conditioner;
import com.hhxy.product.phone.Phone;
import com.hhxy.product.tv.TV;
/**
* @author ghp
* @date 2022/9/28
* @Title
* @description
*/
public interface AbstractFactory {
/**
* 创建手机对象的方法
*/
Phone getPhone();
/**
* 创建空调对象的方法
*/
Conditioner getConditioner();
/**
* 创建电视对象的方法
*/
TV getTV();
}
Step4:创建具体工厂
1)HuaweiAbstractFactory:
package com.hhxy.factory.imp;
import com.hhxy.factory.AbstractFactory;
import com.hhxy.product.conditioner.Conditioner;
import com.hhxy.product.conditioner.imp.HuaweiConditioner;
import com.hhxy.product.phone.Phone;
import com.hhxy.product.phone.imp.HuaweiPhone;
import com.hhxy.product.tv.TV;
import com.hhxy.product.tv.imp.HuaweiTV;
/**
* @author ghp
* @date 2022/9/28
* @Title
* @description
*/
public class HuaweiAbstractFactory implements AbstractFactory {
/**
* 创建华为手机对象的方法
*/
@Override
public Phone getPhone() {
return new HuaweiPhone();
}
/**
* 创建华为空调对象的方法
*/
@Override
public Conditioner getConditioner() {
return new HuaweiConditioner();
}
/**
* 创建电视对象的方法
*/
@Override
public TV getTV() {
return new HuaweiTV();
}
}
2)XiaomiAbstractFactory
和上面类似,具体请参考Gitee或Github,略……
Step5:编写配置文件
factory-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<abstractFactoryName>com.hhxy.factory.imp.HuaweiAbstractFactory</abstractFactoryName>
<abstractFactoryName>com.hhxy.factory.imp.XiaomiAbstractFactory</abstractFactoryName>
<abstractFactoryName>com.hhxy.factory.imp.LianXiangAbstractFactory</abstractFactoryName>
<abstractFactoryName>test</abstractFactoryName>
</config>
Step6:编写配置文件读取类
和工厂方法模式的类似,具体请参考Gitee或Github,略……
Step7:测试
package com.hhxy.test;
import com.hhxy.factory.AbstractFactory;
import com.hhxy.read.ReadFactoryConfig;
/**
* @author ghp
* @date 2022/9/28
* @Title 测试类3
* @description 用于测试抽象工厂模式
*/
public class Test3 {
public static void main(String[] args) {
//测试使用华为工厂创建华为这一产品族的对象
/*
方式一:
AbstractFactory huaweiAbstractFactory = new HuaweiAbstractFactory();
huaweiAbstractFactory.getTV().watch();
huaweiAbstractFactory.getPhone().call();
huaweiAbstractFactory.getConditioner().open();
*/
//方式二:通过读取配置文件获取工厂对象。这种方式能够降低耦合度,提高了代码的复用性,很方便测试
AbstractFactory abstractFactory = ReadFactoryConfig.getAbstractFactory();
abstractFactory.getTV().watch();
abstractFactory.getPhone().call();
abstractFactory.getConditioner().open();
}
}
测试结果:

三个工厂模式的比较:
总的来讲三种模式各有优劣,从上往下抽象等级依次增加,这也表明往下更具一般性,适用范围更广
自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O

上一篇:每日一个设计模式之【原型模式】
下一篇:每日一个设计模式之【建造者模式】
参考文章:
在此致谢