【java】設計模式之工廠模式

學設計模式,我們要知道這個設計模式是用來解決什麼問題的,並且知道,所有設計模式的設計初衷都是爲了讓代碼更加符合面向對象的原則。

但是對於像我這樣工作經驗不多的小白,面對一些設計模式的確很難想到相應的應用場景,尤其是對於剛畢業的大學生。這種情況下不如先看看它的代碼。


工廠模式有簡單工廠模式(靜態工廠模式),工廠方法模式和抽象工廠模式。


首先是簡單工廠模式,

定義:由一個對象決定實例化接口的哪一個子類。

(這裏的“決定”,不是讓對象決定,而是對象(的這個方法)有了實例化不同類的權利,就是說,代碼寫完,我都不一定知道實際運行時,工廠會產生什麼對象,但是我賦予了它產生一類(接口的子類)對象的權利)

假設:這個社會有一個種產品,客戶端購買,那麼,我們可以這樣寫:

public class Product {
	private String name;

	public Product(String name) {
		this.name = name;
	}
}

Product p = new Product();
然後,社會進步了,這種產品出現了三個類型(第一種,第二種,第三種);
你可以這樣寫:
Product p1 = new Product(“first”);
Product p2 = new Product(“second”);
Product p3 = new Product(“third");

如果出現一萬種,你的客戶端是不是都這樣寫了呢?你發現問題了嗎,既然客戶端購買這個產品,當客戶端又兼顧生產這種產品的職責(new Product())。
不如把這個工作,交給一個工廠,這個工廠負責生產不同的產品,而你只需要告訴他你要什麼就好了,產品類的初始化交給它去做

這是類圖


接口IProduct,public方法 show()。

public interface IProduct {
	
 public void show();

}

下面有三個類,FirstProduct,SecondProduct,ThirdProduct。

就只貼一個吧。

public class FirstProduct implements IProduct {

	@Override
	public void show() {
		System.out.println("第一個零件");

	}

}

工廠類,Factory,它有一個靜態方法,返回接入IProduct實例的make方法。(不要靜態也行,客戶端可以實例化一個工廠類,有了靜態方法就不用實例化工廠類了)

public class Factory {
	public static IProduct make(int i) {
		if(i==1){
			return new FirstProduct() ;
		}
		if(i==2){
			return new SecondProduct() ;
		}
		if(i==3){
			return new ThirdProduct() ;
		}	
		return null;
		
	}
	

測試類代碼:

public static void main(String[] args) {

		IProduct product1 = Factory.make(1);
		IProduct product2 = Factory.make(2);
		IProduct product3 = Factory.make(3);	
		product1.show();
		product2.show();
		product3.show();
	}

這樣,類的初始化,由客戶端,轉移到工廠類裏面。

如果不用接口,工廠想要生產這麼多產品,必須針對每一個產品類,都要寫一個方法,(改天寫一篇面向接口編程的心得,推薦看head first 設計模式-工廠模式篇,它談到需求改變,A a = new A()的不迎合變化的缺點,它認爲簡單工廠更像是一種編程習慣而非模式,事實上,簡單工廠模式也非GOF的二十三種模式之一)

而且,夜不能放在一個方法裏面。如果放在一個方法裏面,返回類型是什麼?所以,接口的出現,讓這些類可以用同一個返回類型,這個返回類型就是“繼承了這個藉口的所有子類的類型”。

工廠模式的這一個點很重要——“延遲始化”(Lazy Initialization)。

延遲始化,顧名思義,延遲初始化,延遲產品類的初始化。

爲什麼要延遲初始化?很多情況下,我們需要在類初始化的時候,做一些事情,ok,我們可以把這些東西放到了這個類的構造方法裏面,可是,有時候又需要處理另外一些情況怎麼辦?加判斷條件,或者寫約束。但這無疑加重的類的任務,增加了耦合度,這不符合我們的OO設計理念。

我們要處理的這些情況也不能交給客戶端去做。所以,我們把這些事情,放到延遲初始化裏面。

除此之外,還可以這樣利用:在我們的類的實例可以重複利用的情況下,假如,這個類已經實例化過了, 何不返回一個已經產生的實例?

簡單說一下,上面的代碼沒有寫出。就是在工廠類裏面,私有一個泛型爲IProduct的arraylist,然後在make方法裏面,判斷一下,當前需要初始化的這個類有沒有被初始化過,如果沒有,那麼初始化,存入arraylist,然後返回實例,如果有,直接從arraylist裏面取出,返回。

當然,延遲始化還有很多作用,這裏腦洞就不開太大了。我也突然發現我之前遇到的一些問題,可以用延遲始化來解決:比如,對不同類,不同情況下,初始化的處理,如果在每一個類自己的初始化裏面做各種分析、條件判斷,不僅找起來麻煩,找到也不好修改,不如都放在工廠的make方法下,便於處理,最起碼代碼好看多了,也方便。

親孃來,我就是回顧一下以前學的東西,倆小時過去了。

推薦一本書《設計模式之禪》,真心不錯。

順帶說一句,書這個東西嘛,沒事多找幾本自己可能會用到的書,存着,等哪天用到了查就好了,沒有必要一口氣看下來,你要是時間多,系統地看也無妨。

話說這些年我買的書,沒有一本全看完的,都是看對當時的我有用的部分,或者 需要的時候查看一下。

無生也有涯,而知也無涯, 以有涯隨無涯,豈不傻叉嘛。


接下來是 工廠方法模式。

定義:定義了一個創造對象的接口,由其子類決定實例化的類是哪一個。工廠方法讓類把實例化推遲到了子類。

其實工廠方法模式就是在簡單工廠模式之上,給工廠抽一個一個接口。

爲啥麼呢。假如,我們又有了新的產品,新的工廠,怎麼辦?在原有的工廠的make方法里加判斷?這不符合OCP。

那麼我們另起一個工廠。但是如果之後又加了新的產品、新的工廠,你能想象客戶端亂七八糟的樣子嗎?

所以,所有工廠抽取一個工廠接口,這樣,就符合OCP原則了。


這是類圖,Factory1只生產Product1和Product2兩種產品,Factory2只生產Product3這種產品,這樣我們就不用修改原來的代碼,從而讓產品3進入市場了,嘿嘿。

public static void main(String[] args) {
		
		IFactory factoryA = new Factory1();
		IFactory factoryB = new Factory1();

		IProduct productA = factoryA.createProduct(1);
		productA.productMethdod();	
		
		IProduct productB = factoryB.createProduct(3);
		productB.productMethdod();	
	}
這是“市場”(客戶端)

或許你會說“這樣看來,簡單工廠模式並不完全符合OCP原則啊”。是啊,但是那也要看在什麼情況下,如果產品是人類的話,區別產品的條件是性別,無非有 男、女、無性別、雙性人這四種。在既有條件下,不會出現新的產品,那麼簡單工廠和工廠方法都符合OCP。你可能會想到:對啊現在有ABC三種商品,如果這些商品區分男女,或者用其他條件分類怎麼辦?這就是下面要說的-抽象工廠模式。


抽象工廠模式。

定義:爲創建一組相關或相互依賴的對象,提供一個接口,且無需指明具體的對象。

在說抽象工廠之前,明確一個態度,那就是:提出設計模式是爲了解決問題,但是,並不是說,給你一個問題,你繞過了設計模式尋求其他的方法,或許在這個問題面前,確實有其他的解決辦法,但我們談的是設計模式,所以就說設計模式。

假設:市面上有產品A ,產品B,工廠1能生產出男人用的A 和 B,而工廠2生產出女人用的 A 和 B。

在這裏,要了解一個概念,產品族和產品等級結構。上述提到的,產品A和產品B,是屬於同一個產品等級結構,但是男用AB和女用AB,就是不同的產品族。

類圖:


產品接口IProductA和IProductB將產品分爲2種,A和B。

工廠接口聲明瞭兩種方法,生產A和生產B。

因爲要生產這兩種的產品,所以工廠接口代碼如下:

public interface IFactory {
	public IProductA makeProductA();
	public IProductB makeProductB();
}

Factory1.java

public class Factory1 implements IFactory {
	@Override
	public IProductA makeProductA() {
		return new ProductA1();
	}
	@Override
	public IProductB makeProductB() {
		return new ProductB1();
	}

}
Factory2.java

public class Factory2 implements IFactory {
	@Override
	public IProductA makeProductA() {
		return new ProductA2();
	}
	@Override
	public IProductB makeProductB() {
		return new ProductB2();
	}
}
產品類就列出一個:

public class ProductA1 implements IProductA {
	@Override
	public void show() {
		System.out.println("產品A1");
	}
}
test.java

	public static void main(String[] args) {
		IFactory factory1 = new Factory1();
		IFactory factory2 = new Factory2();
		IProductA pA1 = factory1.makeProductA();
		IProductB pB1 = factory1.makeProductB();
		IProductA pA2 = factory2.makeProductA();
		IProductB pB2 = factory2.makeProductB();
	
		System.out.println("這是工廠1,生產:");
		pA1.show();
		pB1.show();
		System.out.println("這是工廠2,生產:");
		pA2.show();
		pB2.show();
	}

運行結果:


抽象工廠模式是針對多個產品等級結構,所以在工廠接口裏,對與每一個產品接口,它都要有一個方法。或許你會問,那這樣的話, 添加新種類的產品怎麼辦?工廠接口裏是不是要修改呢?

是的,就是要修改,但是呢,有利就有弊,反之亦然。抽象工廠模式下,工廠是用來創造一個產品族的,而工廠方法模式下,工廠只是用來創造產品,不用考慮產品族的問題。

我們的目的是解耦,根據不同的情況,選擇適合的模式。

發佈了43 篇原創文章 · 獲贊 7 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章