23天读懂23种设计模式:工厂方法模式(创建型)

点击上方 蓝字 关注我们

创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其他关联对象的创建、组合和表示方式。

工厂方法模式目的:封装构造对象具体过程,解耦客户端与构造对象。

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();

总结

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

创建型模式还包括:原型模式、抽象工厂模式和建造者模式,待后续我们再细细解读。

往期推荐

《源码系列》

JDK之Object 类

JDK之BigDecimal 类

JDK之String 类

JDK之Lambda表达式

《经典书籍》

Java并发编程实战:第1章 多线程安全性与风险

Java并发编程实战:第2章 影响线程安全性的原子性和加锁机制

Java并发编程实战:第3章 助于线程安全的三剑客:final & volatile & 线程封闭

《服务端技术栈》

《Docker 核心设计理念

《Kafka史上最强原理总结》

《HTTP的前世今生》

《算法系列》

读懂排序算法(一):冒泡&直接插入&选择比较

《读懂排序算法(二):希尔排序算法》

《读懂排序算法(三):堆排序算法》

《读懂排序算法(四):归并算法》

《读懂排序算法(五):快速排序算法》

读懂排序算法(六):二分查找算法》

《设计模式》

设计模式之六大设计原则

设计模式之创建型(1):单例模式

扫描二维码

获取技术干货

后台技术汇

点个“在看”表示朕

已阅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值