1. 程式人生 > >java的三種工廠模式

java的三種工廠模式

工廠模式是用工廠方法代替了new操作, 將選擇實現類建立物件統一管理和控制.從而將呼叫者(Client)與實現類進行解耦.實現了建立者與呼叫者分離;

java中的工廠模式又有三種模式,分別是靜態工廠、工廠模式和抽象模式。

使用場景:

  1. JDK中Calendar的getInstance方法;
  2. JDBC中Connection物件的獲取;
  3. MyBatis中SqlSessionFactory建立SqlSession;
  4. SpringIoC容器建立並管理Bean物件;
  5. 反射Class物件的newInstance;
  6. ….

一、靜態工廠

靜態工廠模式是工廠模式中最簡單的一種,他可以用比較簡單的方式隱藏建立物件的細節,一般只需要告訴工廠類所需要的型別,工廠類就會返回需要的產品類,而客戶端看到的也只是產品的抽象物件(interface),因此無需關心到底是返回了哪個子類

1、介面類

實現一個產品工廠,有兩種產品分別是A、B,使用靜態工廠完成產品的資訊輸出。

在介面中定義方法描述每種產品的資訊

package com.lq.demo;

public interface IProduct {
	//描述每種產品的資訊
	public void desc();
}

2、產品A

重寫介面的方法,實現產品A的資訊輸出

package com.lq.demo;

public class ProductA implements IProduct{

	@Override
	public void desc() {
		System.out.println("產品A的編號是001,名稱是A,最大金額是10000");
		
	}

}

3、產品B

重寫介面的方法,實現產品B的資訊輸出

package com.lq.demo;

public class ProductB implements IProduct{

	@Override
	public void desc() {
		System.out.println("產品B的編號是002,名稱是B,最大金額是20000");
		
	}

}

4.產品工廠

在工廠中定義產品型別,通過傳入的值判斷具體的產品

package com.lq.demo;

public class ProductFactory {
	public static int type_A = 1;//產品A
	public static int type_B = 2;//產品B
	
	public static IProduct createProduct(int type){
		
		switch(type){
		case 1:
			return new ProductA();
		case 2:
		default:
			return new ProductB();
		}
	}
}

5.測試類

package com.lq.test;

import com.lq.demo.IProduct;
import com.lq.demo.ProductFactory;

public class TestProduct {
	
	public static void main(String[] args) {
		//選擇了產品A
		IProduct p = ProductFactory.createProduct(ProductFactory.type_A);
		p.desc();
	}
}

測試結果:

  • 優點

    1. 隱藏了物件建立的細節,將產品的例項化過程放到了工廠中實現。
    2. 客戶端基本不用關心使用的是哪個產品,只需要知道用工廠的那個方法(或傳入什麼引數)就行了.
    3. 方便新增新的產品子類,每次只需要修改工廠類傳遞的型別值就行了。
    4. 遵循了依賴倒轉原則。
  • 缺點

    1. 適用於產品子型別差不多, 使用的方法名都相同的情況.
    2. 每新增一個產品子類,都必須在工廠類中新增一個判斷分支(或一個方法),這違背了OCP(開放-封閉原則)

二、工廠模式

工廠模式會提供一個用於建立物件的介面(工廠介面),讓其實現類(工廠實現類)決定例項化哪一個類(產品類),並且由該實現類建立對應類的例項。

提供一個產品類的介面。產品類均要實現這個介面(也可以是abstract類,即抽象產品)。 提供一個工廠類的介面。工廠類均要實現這個介面(即抽象工廠)。 由工廠實現類建立產品類的例項。工廠實現類應有一個方法,用來例項化產品類。

1、產品介面

實現產品的資訊輸出

package com.lq.demo;

public interface IProduct {
	public void desc();
}

2、產品A

package com.lq.demo;

public class ProductA implements IProduct{

	@Override
	public void desc() {
		System.out.println("產品型別是A,產品編號是001,最大金額是10000");
		
	}
	
}

3、產品B

package com.lq.demo;

public class ProductB implements IProduct{

	@Override
	public void desc() {
		System.out.println("產品型別是B,產品編號是002,最大金額是20000");
		
	}
	
}

4、工廠介面

通過工廠介面建立工廠例項

package com.lq.demo;

public interface IProductFactory {
	
	public IProduct createProduct(String type);
	
}

5、工廠例項

package com.lq.demo;


public class ProductFactory implements IProductFactory{


	@Override
	public IProduct createProduct(String type) {
		IProduct product;
		if(type.equals("A")){
			product = new ProductA();
		}else if(type.equals("B")){
			product = new ProductB();
		}else{
			System.out.println("產品不存在,因此預設生產A");
			product = new ProductA();
		}
		return product;
	}



}

6、測試類

package com.lq.test;

import com.lq.demo.IProduct;
import com.lq.demo.IProductFactory;
import com.lq.demo.ProductFactory;

public class TestFactory {
	
	public static void main(String[] args) {
		//建立工廠
		IProductFactory factory = new ProductFactory();
		//通過工廠建立產品例項
		IProduct product;
		//建立產品A 
		product = factory.createProduct("A");
		product.desc();
		//建立產品B
		product = factory.createProduct("B");
		product.desc();
		
	}
}

優點 基本與靜態工廠模式一致,多的一點優點就是遵循了開放-封閉原則,使得模式的靈活性更強。

缺點 與靜態工廠模式差不多, 但是增加了類組織的複雜性;

小結 雖然根據理論原則, 需要使用工廠方法模式, 但實際上, 常用的還是靜態工廠模式.

三、抽象工廠

抽象工廠模式: 提供一個建立一系列相關或相互依賴物件的介面, 而無需指定他們具體的類.

抽象工廠模式與工廠方法模式的區別:

  • 抽象工廠模式是工廠方法模式的升級版本,他用來建立一組相關或者相互依賴的物件。他與工廠方法模式的區別就在於,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則是針對的多個產品等級結構, 在程式設計中,通常一個產品結構,表現為一個介面或者抽象類,也就是說,工廠方法模式提供的所有產品都是衍生自同一個介面或抽象類,而抽象工廠模式所提供的產品則是衍生自不同的介面或抽象類

1、產品的編號介面

package com.lq.demo;

public interface IProductNumber {
	public void getNumber();
}
class ProductANumber implements IProductNumber{

	@Override
	public void getNumber() {
		System.out.println("產品編號是001");
	}
	
}
class ProductBNumber implements IProductNumber{

	@Override
	public void getNumber() {
		System.out.println("產品編號是002");
	}
	
}

2、產品的名稱介面

package com.lq.demo;

public interface IProductName {
	public void getName();
}
class ProductAName implements IProductName{

	@Override
	public void getName() {
		System.out.println("產品名稱是A");
	}
	
}
class ProductBName implements IProductName{

	@Override
	public void getName() {
		System.out.println("產品名稱是B");
	}
	
}

3、產品的金額介面

package com.lq.demo;

public interface IProductMoney {
	public void getMax();
}
class ProductAMoney implements IProductMoney{

	@Override
	public void getMax() {
		System.out.println("最大金額是10000");
	}
	
}
class ProductBMoney implements IProductMoney{

	@Override
	public void getMax() {
		System.out.println("最大金額是20000");
	}
	
}

4、產品工廠

package com.lq.demo;

public interface IProduct {
	public IProductNumber getNumber();
	public IProductName getName();
	public IProductMoney getMoney();
}

5、產品具體實現類A、B

package com.lq.demo;

public class ProductA implements IProduct{

	@Override
	public IProductNumber getNumber() {
		return new ProductANumber();
	}

	@Override
	public IProductName getName() {
		return new ProductAName();
	}

	@Override
	public IProductMoney getMoney() {
		return new ProductAMoney();
	}
	
}




package com.lq.demo;

public class ProductB implements IProduct{

	@Override
	public IProductNumber getNumber() {
		return new ProductBNumber();
	}

	@Override
	public IProductName getName() {
		return new ProductBName();
	}

	@Override
	public IProductMoney getMoney() {
		return  new ProductBMoney();
	}
	
}

6、測試

package com.lq.test;

import com.lq.demo.IProduct;
import com.lq.demo.ProductA;

public class TestProduct {
	public static void main(String[] args) {
		//例項化產品A
		IProduct A = new ProductA();
		A.getNumber().getNumber();;
		A.getName().getName();;
		A.getMoney().getMax();;
	}
}

結果:

  • 優點
    1. 封裝了產品的建立,使得不需要知道具體是哪種產品,只需要知道是哪個工廠就行了。
    2. 可以支援不同型別的產品,使得模式靈活性更強。
    3. 可以非常方便的使用一族中間的不同型別的產品。
  • 缺點
    1. 結構太過臃腫,如果產品型別比較多,或者產品族類比較多,就會非常難於管理。
    2. 每次如果新增一組產品,那麼所有的工廠類都必須新增一個方法,這樣違背了開放-封閉原則。所以一般適用於產品組合產品族變化不大的情況。

參考部落格: