工廠方法模式&變形

工廠方法模式(Factory Method)

定義
一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
通用類圖UML:
在這裏插入圖片描述

在工廠方法模式中,抽象產品類Product負責定義產品的共性,實現對事物最抽象的定
義;Creator爲抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體的實現工廠
ConcreteCreator完成的。工廠方法模式的變種較多,我們來看一個比較實用的通用源碼。

Product 產品抽象類:

package com.lushunde.desingn.factorymethod;

public abstract class Product {
	// 產品類的公共方法
	public void method1() {
		// 業務邏輯處理
	}

	// 抽象方法
	public abstract void method2();
}

ConcreteProduct1 產品實現:1:

package com.lushunde.desingn.factorymethod;

public class ConcreteProduct1 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct1");
	}
}

ConcreteProduct2 產品實現類2:

package com.lushunde.desingn.factorymethod;

public class ConcreteProduct2 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct2");
	}
}

Creator 工廠抽象類:

package com.lushunde.desingn.factorymethod;

public abstract class Creator {
	/*
	 * 創建一個產品對象,其輸入參數類型可以自行設置 通常爲String、Enum、Class等,當然也可以爲空
	 */
	public abstract <T extends Product> T createProduct(Class<T> c);
}

ConcreteCreator 工廠實現類:

package com.lushunde.desingn.factorymethod;

public class ConcreteCreator extends Creator {
	@SuppressWarnings("unchecked")
	public <T extends Product> T createProduct(Class<T> c) {
		Product product = null;
		try {
			product = (Product) Class.forName(c.getName()).newInstance();
		} catch (Exception e) {
			// 異常處理
		}
		return (T) product;
	}
}

Client 場景類:

package com.lushunde.desingn.factorymethod;

public class Client {
	public static void main(String[] args) {
		Creator creator = new ConcreteCreator();
		Product product1 = creator.createProduct(ConcreteProduct1.class);
		Product product2 = creator.createProduct(ConcreteProduct2.class);
		/*
		 * 繼續業務處理
		 */

		product1.method2();
		product2.method2();
	}
}

工廠方法模式的優點

  • 良好的封裝性,代碼結構清晰,降低耦合性。調用者創建產品不需要了解創建對象的過程。
  • 工廠方法模式的擴展性非常優秀,增加產品實現類即可,其他都不需要改變
  • 屏蔽產品類(非常重要)產品類的實現如何變化,調用者都不需要關心,它只需要關心產品的接口。例如:用JDBC連接數據庫
  • 工廠方法模式是典型的解耦框架。高層模塊值需要知道產品的抽象類,其他的實現類都不用關心,符合迪米特法則,我不需要的就不要去交流;也符合依賴倒置原則,只依賴產品類的抽象;當然也符合里氏替換原則,使用產品子類替換產品父類

工廠方法模式的使用場景

  • Calendar JDK類實現
  • Mybatis中的 事務管理
  • JDBC連接數據庫,只需要提供數據庫類型名稱即可創建相應的連接實例

4種擴展介紹

簡單工廠模式(Simple Factory Pattern)

介紹:
一個模塊僅需要一個工廠類,沒有必要把它產生出來,使用靜態的方法就可以了。

簡單工廠模式(Simple Factory Pattern),也叫做靜態工廠模式。在實際項目中,採用該方法的案例還是比較多的,其缺點是工廠類的擴展比較困難,不符合開閉原則,但它仍然是一個非常實用的設計模式。

UML類圖:
在這裏插入圖片描述
Product 產品抽象類:

package com.lushunde.desingn.factorymethod.simplefactorymethod;

public abstract class Product {
	// 產品類的公共方法
	public void method1() {
		// 業務邏輯處理
	}

	// 抽象方法
	public abstract void method2();
}

ConcreteProduct1 產品實現:1:

package com.lushunde.desingn.factorymethod.simplefactorymethod;

public class ConcreteProduct1 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct1");
	}
}

ConcreteProduct2 產品實現類2:

package com.lushunde.desingn.factorymethod.simplefactorymethod;

public class ConcreteProduct2 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct2");
	}
}

ConcreteCreator 工廠靜態方法實現:
```java
package com.lushunde.desingn.factorymethod.simplefactorymethod;

public class ConcreteCreator  {
	@SuppressWarnings("unchecked")
	public static <T extends Product> T createProduct(Class<T> c) {
		Product product = null;
		try {
			product = (Product) Class.forName(c.getName()).newInstance();
		} catch (Exception e) {
			// 異常處理
		}
		return (T) product;
	}
}

Client 場景類:

package com.lushunde.desingn.factorymethod.simplefactorymethod;

/**
 * 一個模塊僅需要一個工廠類,沒有必要把它產生出來,使用靜態的方法就可以了,
 * @author bellus
 *
 */
public class Client {
	public static void main(String[] args) {
		Product product1 = ConcreteCreator.createProduct(ConcreteProduct1.class);
		Product product2 = ConcreteCreator.createProduct(ConcreteProduct2.class);
		/*
		 * 繼續業務處理
		 */

		product1.method2();
		product2.method2();
	}
}

多個工廠模式

介紹
一個比較複雜的項目時,經常會遇到初始化一個對象很耗費精力的情況,所有的產品類都放到一個工廠方法中進行初始化會使代碼結構不清晰。所以我們可以爲每中類型產品都單獨創建一個工廠,同時可以實現每個產品不同邏輯。

UML類圖:
在這裏插入圖片描述
Product 產品抽象類:

package com.lushunde.desingn.factorymethod.manyfactorymethod;

public abstract class Product {
	// 產品類的公共方法
	public void method1() {
		// 業務邏輯處理
	}

	// 抽象方法
	public abstract void method2();
}

ConcreteProduct1 產品實現:1:

package com.lushunde.desingn.factorymethod.manyfactorymethod;

public class ConcreteProduct1 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct1");
	}
}

ConcreteProduct2 產品實現類2:

package com.lushunde.desingn.factorymethod.manyfactorymethod;

public class ConcreteProduct2 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct2");
	}
}

Creator 工廠抽象類:

package com.lushunde.desingn.factorymethod.manyfactorymethod;

public abstract class Creator {
	/*
	 * 創建一個產品對象,其輸入參數類型可以自行設置 通常爲String、Enum、Class等,當然也可以爲空
	 */
	public abstract <T extends Product> T createProduct();
}

ConcreteCreator1 產品1的工廠實現:

package com.lushunde.desingn.factorymethod.manyfactorymethod;

public class ConcreteCreator1 extends Creator  {
	@SuppressWarnings("unchecked")
	public  <T extends Product> T createProduct() {
		Product product = null;
		try {
			product = new ConcreteProduct1();
		} catch (Exception e) {
			// 異常處理
		}
		return (T) product;
	}
}

ConcreteCreator2 產品2工廠實現類2:

package com.lushunde.desingn.factorymethod.manyfactorymethod;

public class ConcreteCreator2 extends Creator  {
	@SuppressWarnings("unchecked")
	public  <T extends Product> T createProduct() {
		Product product = null;
		try {
			product = new ConcreteProduct2();
		} catch (Exception e) {
			// 異常處理
		}
		return (T) product;
	}
}

Client 場景類:

package com.lushunde.desingn.factorymethod.manyfactorymethod;

/**
 * 多個工廠模式,適用於複雜場景或者 創建產品有不同邏輯的產品
 * @author bellus
 *
 */
public class Client {
	public static void main(String[] args) {
		// 創建第一個產品工廠 和產品
		Creator factory1 = new ConcreteCreator1();
		Product product1 = factory1.createProduct();
				
		// 創建第二個產品工廠 和產品
		Creator factory2 = new ConcreteCreator2();
		Product product2 = factory2.createProduct();
		/*
		 * 繼續業務處理
		 */

		product1.method2();
		product2.method2();
	}
}

替代單例模式

產品構造私有化,產品工廠通過 反射創建實例。(不推薦,製作瞭解和學習)
在這裏插入圖片描述

延遲初始化

一個對象被消費完畢後,並不立刻釋放,工廠類保持其初始狀態,等待再次被使用。

實用場景:

  • JDBC連接數據庫,都會要求設置一個MaxConnections最大連接數量,該數量就是內存中最大實例化的數量。
  • 延遲加載還可以用在對象初始化比較複雜的情況下,例如硬件訪問,涉及多方面的交互,則可以通過延遲加載降低對象的產生和銷燬帶來的複雜性。

通用UML類圖:
在這裏插入圖片描述
Product 產品抽象類:

package com.lushunde.desingn.factorymethod.lazy;

public abstract class Product {
	// 產品類的公共方法
	public void method1() {
		// 業務邏輯處理
	}

	// 抽象方法
	public abstract void method2();
}

ConcreteProduct1 產品實現:1:

package com.lushunde.desingn.factorymethod.lazy;

public class ConcreteProduct1 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct1");
	}
}

ConcreteProduct2 產品實現類2:

package com.lushunde.desingn.factorymethod.lazy;

public class ConcreteProduct2 extends Product{
	public void method2() {
		// 業務邏輯處理
		System.out.println("ConcreteProduct2");
	}
}

ProductFactory 實現延時加載的工廠:

package com.lushunde.desingn.factorymethod.lazy;

import java.util.HashMap;
import java.util.Map;

public class ProductFactory {
	private static final Map<String, Product> prMap = new HashMap<>();

	public static synchronized Product createProduct(String type) throws Exception {
		Product product = null;
		// 如果Map中已經有這個對象
		if (prMap.containsKey(type)) {
			product = prMap.get(type);
		} else {
			if (type.equals("Product1")) {
				product = new ConcreteProduct1();
			} else {
				product = new ConcreteProduct2();
			}
			// 同時把對象放到緩存容器中
			prMap.put(type, product);
		}
		return product;
	}
}

Client 場景類:

package com.lushunde.desingn.factorymethod.lazy;

/**
 * 延遲初始化 一個對象被消費完畢後,並不立刻釋放,工廠類保持其初始狀態,等待再次被使用.
 * 
 * @author bellus
 *
 */
public class Client {
	public static void main(String[] args) throws Exception {

		// 創建第一個產品
		Product product1 = ProductFactory.createProduct("product1");
		Product product2 = ProductFactory.createProduct("product1");
		Product product3 = ProductFactory.createProduct("product");

		product1.method2();
		product2.method2();
		product3.method2();

		if (product1 == product2) {
			System.out.println("兩次獲取的同一個對象,使用了緩存 ");
		} else {
			System.out.println("兩次獲取的不同對象,使用了緩存 ");
		}
	}
}

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