手撕设计模式之「工厂方法模式」(Java描述)

前言

工厂方法模式是对简单工厂模式的改进,它通过对工厂类进行抽象形成一个抽象工厂接口,再让具体的工厂负责对应产品的创建,使得在增加产品的场景中也满足“开闭原则”。希望通过本文的学习,你可以掌握这种设计模式。

为了方便学习和交流,我会把「手撕设计模式」系列的代码上传到Github,需要的小伙伴可以到上面下载(记得顺手star一下哦~)。

GitHub地址:https://github.com/VeggieOrz/DesignPattern

文章目录

  • 前言
  • 1. 模式动机
  • 2. 模式定义
  • 3. 模式结构
  • 4. 模式实现
  • 5. 模式总结
    • 5.1 优点
    • 5.2 缺点
    • 5.3 应用场景
  • 参考资料

1. 模式动机

从上一篇博文「简单工厂模式」中,我们了解到它在增添新产品时必须修改工程类,不满足“开闭原则”。为了在简单工厂模式的基础上提高工厂类的拓展性,我们就引入了简单工厂模式,它的工厂类多了一层抽象工厂,具体产品对象的创建由和与之对应的具体工厂来负责

工厂方法模式降低了单个工厂的职责,每次新增产品时,只要加入新的具体产品类和具体工程类即可,符合开闭原则。(可以结合下图理解)
手撕设计模式之「工厂方法模式」(Java描述)_第1张图片

2. 模式定义

工厂方法模式(Factory Method Pattern)简称工厂模式,属于类创建模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类负责生成具体的产品类对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

3. 模式结构

工厂方法模式的结构图如下:
手撕设计模式之「工厂方法模式」(Java描述)_第2张图片
其中包含四个角色:

  • Product(抽象产品):抽象产品时定义产品的接口,是工厂方法模式所创建的超类,也是所有具体产品类的共同父类或接口。
  • ConcreteProduct(具体产品角色):具体产品实现了抽象产品接口,某种类型的具体产品3由专门的工厂创建,它们之间一一对应。
  • Factory(抽象工厂):这是工厂方法模式的核心,类中声明了用于创建产品的factoryMethod(),任何在模式中创建对象的工厂都必须实现该接口。
  • ConcreteFactory(具体工厂):具体工厂是抽象工厂的子类,实现了抽象工厂中定义的factoryMethod()方法,负责具体产品的创建。

4. 模式实现

(1)抽象产品

public interface Product {
	public void use();
}

(2)具体产品

public class ConcreteProductA implements Product{
	public void use() {
		System.out.println("This is Product A.");
	}
}

(3)抽象工厂

需要注意:工厂方法的返回类型必须是抽象产品类

public interface Factory {
	// 工厂方法factoryMethod
	public Product createProduct();
}

(4)具体工厂

public class ConcreteFactoryA implements Factory{

	public Product createProduct() {
		System.out.println("Product A is being produced.");
		return new ConcreteProductA();
	}
}

(5)工具类 and 配置文件

工具类主要用来进行配置文件的读取,读出想要创建的类型,然后通过Java提供的反射机制创建对应的对象。

public class XMLUtil {
	// 读取XML配置文件中具体类的类名,并返回一个实例对象
	public static Object getBean() throws Exception {
		try {
			// 创建文档对象
			DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dFactory.newDocumentBuilder();
			Document doc;
			doc = builder.parse(new File("./src/factoryMethod/config.xml"));
			
			// 获取包含类名的文本节点
			NodeList nodeList = doc.getElementsByTagName("className");
			Node classNode = nodeList.item(0).getFirstChild();
			String className = classNode.getNodeValue();
			
			// 通过类名生成实例对象并将其返回
			Class<?> c = Class.forName(className);
			Object obj = c.newInstance();
			return obj;
		} catch (Exception e) {
			// 如果该类名找不到就抛出异常
			throw new Exception("Sorry,This kind of product is not found");
		}
	}
}

写配置文件时候,要注意类的路径需要加上包名,否则会抛出java.lang.ClassNotFoundException异常。


<config>
    <className>factoryMethod.factory.ConcreteFactoryAclassName>
config>

(6)客户端

public class Client {

	public static void main(String[] args) {
		try {
			Factory factory;
			Product product;
			// 先通过读取配置文件和Java反射机制创建工厂
			factory = (Factory)XMLUtil.getBean();
			// 工厂负责创建相应的产品
			product = factory.createProduct();
			product.use();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
}

完成了这些只后,就知道了工厂方法模式的具体实现,但是上面代码还没体现到这种设计模式的优点。我们不妨思考一下,如果现在要挑添加一个ConcreteProductB,并将要创建的产品改为添加的这个产品,应该如何操作呢?

  • 先新建一个实现Product接口的ConcreteProductB类;
  • 然后再新建一个实现了Factory接口的ConcreteFactoryB类,专门负责产品类ConcreteProductB的创建;
  • 最后再将配置文件修改为ConcreteFactoryB类的路径即可。

由此可见,工厂方法模式添加新产品时也不需要对模式中原有的代码进行修改!

5. 模式总结

5.1 优点

  • 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
  • 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。
  • 拓展性好,在系统中加入新产品时,不需要对已有代码进行修改,只要加上新的产品类和对应工厂类即可,完全符合开闭原则。

5.2 缺点

  • 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一定的额外开销。
  • 增加了系统的抽象性和理解难度。

5.3 应用场景

  • 客户端不知道它所需要的对象的类,客户端不需要知道具体产品类的类名, 只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建。
  • 抽象工厂类通过其子类来指定创建哪个对象

参考资料

  1. 《Java设计模式》
  2. Factory Method | GitHub

如果看完本文感觉有所收获,别忘了点赞哦~

你可能感兴趣的:(设计模式,java,反射,工厂方法模式)