學設計模式,我們要知道這個設計模式是用來解決什麼問題的,並且知道,所有設計模式的設計初衷都是爲了讓代碼更加符合面向對象的原則。
但是對於像我這樣工作經驗不多的小白,面對一些設計模式的確很難想到相應的應用場景,尤其是對於剛畢業的大學生。這種情況下不如先看看它的代碼。
工廠模式有簡單工廠模式(靜態工廠模式),工廠方法模式和抽象工廠模式。
首先是簡單工廠模式,
定義:由一個對象決定實例化接口的哪一個子類。
(這裏的“決定”,不是讓對象決定,而是對象(的這個方法)有了實例化不同類的權利,就是說,代碼寫完,我都不一定知道實際運行時,工廠會產生什麼對象,但是我賦予了它產生一類(接口的子類)對象的權利)
假設:這個社會有一個種產品,客戶端購買,那麼,我們可以這樣寫:
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();
}
運行結果:
抽象工廠模式是針對多個產品等級結構,所以在工廠接口裏,對與每一個產品接口,它都要有一個方法。或許你會問,那這樣的話, 添加新種類的產品怎麼辦?工廠接口裏是不是要修改呢?
是的,就是要修改,但是呢,有利就有弊,反之亦然。抽象工廠模式下,工廠是用來創造一個產品族的,而工廠方法模式下,工廠只是用來創造產品,不用考慮產品族的問題。
我們的目的是解耦,根據不同的情況,選擇適合的模式。