手撕設計模式之「工廠方法模式」(Java描述)

前言

工廠方法模式是對簡單工廠模式的改進,它通過對工廠類進行抽象形成一個抽象工廠接口,再讓具體的工廠負責對應產品的創建,使得在增加產品的場景中也滿足“開閉原則”。希望通過本文的學習,你可以掌握這種設計模式。

爲了方便學習和交流,我會把「手撕設計模式」系列的代碼上傳到Github,需要的小夥伴可以到上面下載(記得順手star一下哦~)。

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

1. 模式動機

從上一篇博文「簡單工廠模式」中,我們瞭解到它在增添新產品時必須修改工程類,不滿足“開閉原則”。爲了在簡單工廠模式的基礎上提高工廠類的拓展性,我們就引入了簡單工廠模式,它的工廠類多了一層抽象工廠,具體產品對象的創建由和與之對應的具體工廠來負責

工廠方法模式降低了單個工廠的職責,每次新增產品時,只要加入新的具體產品類和具體工程類即可,符合開閉原則。(可以結合下圖理解)
在這裏插入圖片描述

2. 模式定義

工廠方法模式(Factory Method Pattern)簡稱工廠模式,屬於類創建模式。在工廠方法模式中,工廠父類負責定義創建產品對象的公共接口,而工廠子類負責生成具體的產品類對象,這樣做的目的是將產品類的實例化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該實例化哪一個具體產品類。

3. 模式結構

工廠方法模式的結構圖如下:
在這裏插入圖片描述
其中包含四個角色:

  • 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異常。

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <className>factoryMethod.factory.ConcreteFactoryA</className>
</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

如果看完本文感覺有所收穫,別忘了點贊哦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章