

创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其他关联对象的创建、组合和表示方式。
工厂方法模式目的:封装构造对象具体过程,解耦客户端与构造对象。
winter
工厂方法模式也是创建型的设计模式之一,本文是设计模式系列(共24节)的第3篇文章。
设计模式都从六大原则出发进行总结:《第一节:设计模式的六大原则》
创建型设计模式共5种:
- 单例模式(Singleton Pattern):一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
- 工厂方法模式(Factory Pattern):在工厂方法模式中,工厂类成为了抽象类,实际的创建工作将由其具体子类来完成。
- 抽象工厂模式(Abstract Factory):抽象工厂可以向客户提供一个接口,创建多个产品族中的产品对象,强调的是“对象系列”的变化。
- 建造者模式(Builder Pattern):把构造对象实例的逻辑移到了类的内部,在类的外部定义了该类的构造逻辑,强调的是产品的构造过程。
- 原型模式(Prototype Pattern):原型模式和工厂模式一样,同样对客户隐藏了对象创建工作具体的实现细节,通过复制一个现有的对象生成新对象。


工厂方法模式是什么
在工厂方法模式中,工厂类成为了抽象类,实际的创建工作将由其具体子类来完成。工厂的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中,它强调的是“单个对象”的变化。


工厂方法模式的应用
工厂方法的内涵:对外客户端提供统一的接口,对内封装聚合复杂对象的创建过程,以实现低耦合高内聚。
举个例子1:
在数学中构造图形时,我们会有矩形、圆形、等边三角形等等,譬如构造一个三角形我们要定义好边的长度,矩形则要定义好长宽,圆形则要定义好半径...
对于图形构造这种相对简单的操作,我们可以统一封装到工厂类里面,对外只需要暴露通用的API。需要创建图形时,客户端只需要告知需要那种图形,工厂类会自动完成构建并返回。
下面我们定义了一个工厂类 ShapeFactory,对外提供getShape()方法:
//普通工厂类
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
对于各类图形可以抽象出Shape接口(定义了画图的动作),并具体类实现这个接口:
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
测试用例:
public class TestFactoryPattern {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
UML:

好处:
客户端与产品的创建分离,客户端不需要知道产品创建的逻辑,只需要消费该产品即可。
扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。


工厂方法模式在Spring源码的应用
Spring源码也有运用到工厂方法的地方。
举例子2:
Spring 框架对bean加载步骤较多,其中的对xml配置资源文件的解析工作包含一个逻辑:在确定 xml 资源文件的验证模式后,spring 框架随后进行 Document 加载,该加载过程是经过 DocumentBuilder 建造器来实现的。这里的建造器 DocumentBuilder 恰恰是被 DocumentBuilderFactory 工厂类生产而来。
下面的源码节选自 DefaultDocumentLoader:
//加载资源文件到Document
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
//封装了工厂类创建产品(DocumentBuilder )的细节
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException
//这里正是使用了 DocumentBuilderFactory 的开放对外的统一API:newDocumentBuilder()。
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
//篇幅所限,该方法不详细展开
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {}
代码分析:
createDocumentBuilderFactory() :受保护的方法封装了创建 DocumentBuilderFactory 工厂类的细节。(碍于篇幅,不作发散,后续spring源码讲解再一起探究)
createDocumentBuilder():受保护的封装了工厂类创建产品(DocumentBuilder )的细节,这里正是使用了 DocumentBuilderFactory 的开放对外的统一API:newDocumentBuilder()。
DocumentBuilder docBuilder = factory.newDocumentBuilder();


总结
工厂方法模式符合开闭原则和单一职责原则,工厂类支持拓展并且专注于某一类对象的创建工作;日常开发时,稍加留意一下,就会发现在很多工具类设计中都能看到工厂方法的身影。理解工厂方法的优缺点,对我们阅读源码还有开发业务都有很大的帮助。
创建型模式还包括:原型模式、抽象工厂模式和建造者模式,待后续我们再细细解读。

扫描二维码
获取技术干货
后台技术汇
