一、前期回顧
上一篇《Java設計模式之單例模式》詳細介紹了單例模式,介紹了單例模式的使用場景,優缺點,同時也寫了兩種常見的單例模式寫法,懶漢式單例模式和餓漢氏單例模式,當然,單例模式的寫法還有很多,比如,列舉單例模式,靜態內部類單例模式等。有興趣的可以自行查詢資料。本篇開始介紹單例模式的第二篇,工廠方法模式,以及工廠模式的升級版,抽象工廠模式。
二、工廠方法模式的定義與實踐
定義:Define an interface for creating an object,but let subclasses decide which class to instantiation.Factory Mehod lets a class defer instantiation to subclasses.翻譯過來就是,定義一個用於建立物件的介面,讓其子類決定例項化哪個類。工廠方法讓一個類的例項化延遲到其子類。
上面的定義說明好像很繞口,如果不理解沒關係,我們用個例子來說明這個定義:
//定義產品介面
public interface Iproduct {
public void doSomething();
}
//產品實現類
public class ProductA implements Iproduct {
@Override
public void doSomething() {
System.out.println("我是產品A,我可以搞事情");
}
}
//定義工廠類介面
public interface IFactory {
//建立產品工廠方法
< T extends Iproduct> T creatProduct(Class<T> clz);
}
//工廠方法實現類
public class ProductFactory implements IFactory {
@Override
public <T extends Iproduct> T creatProduct(Class<T> clz) {
Iproduct product=null;
try {
product= clz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (T) product;
}
}
//場景類
public class Client {
public static void main(String[] args) {
IFactory factory=new ProductFactory();
Iproduct product= factory.creatProduct(ProductA.class);
product.doSomething();
}
}
複製程式碼
上述例子,實現了一個簡單的工廠方法模式,定義了一個工廠介面類,然後具體的工廠方法實現了建立物件的邏輯。看到這裡,有人肯定會問,這裡要new一個工廠類的例項,和我new一個具體的物件有什麼區別呢?反正都要自己new,幹嘛要搞一個工廠類這麼繞呢?對,沒錯。這裡是要new一個工廠類,但是上面的例子建立產品的物件是比較簡單,所以感覺不出來,如果ProductA的例項建立是一個非常複雜的過程,那麼這個時候我通過工廠方法模式建立物件的例項就很方便了。
肯定還有人問,我覺得上面這個方式還有點囉嗦,有沒有更簡便的方式實現工廠模式呢?答案是,有。我們接下來看看簡單工廠模式。簡單工廠方法模式就是上述工廠方法模式的簡單版本。我們只要把上述工廠類的介面去掉,然後把工廠實現類改為靜態類就可以實現簡單工廠方法模式了。程式碼是這樣的:
//這裡只去掉了介面,改為靜態方法
public class ProductFactory {
public static <T extends Iproduct> T creatProduct(Class<T> clz) {
Iproduct product=null;
try {
product= clz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (T) product;
}
}
public class Client {
public static void main(String[] args) {
Iproduct product= ProductFactory.creatProduct(ProductA.class);
product.doSomething();
}
}
複製程式碼
這裡就不用自己去new 工廠類啦,程式碼也簡潔了很多。但是去掉了介面也就意味著後面的擴充套件是不友好的,所以違反了開閉原則。
三、抽象工廠模式的定義與實踐
定義:Provide an interface for creating families of related or dependent objects without specifying their concrete classes.翻譯: 提供一個建立相關或者相互依賴的物件的介面,並且無需指定他們的具體實現類。
這裡理解可能有點困難,為了便於理解,我們先引入兩個概念。
產品等級結構: 產品等級結構即產品的繼承結構,如一個抽象類是汽車,其子類就是各個品牌的汽車,比如賓士,寶馬汽車。汽車和賓士就構成了一個產品等級結構,抽象汽車是父類,而具體品牌的汽車是其子類。
產品族 : 在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品,如賓士工廠生產的賓士跑車,寶馬公司生產的跑車,賓士跑車位於賓士產品等級結構中,寶馬跑車位於寶馬產品等級結構中。
所以,汽車和品牌是一個產品等級結構,而每個品牌下面有多個產品線,也就是說賓士牌下面的汽車有多個產品族,比如跑車族,SUV族。賓士跑車和寶馬跑車是一個產品族,賓士SUV和寶馬SUV是一個產品族。所以上面定義中“相關或者相互依賴的物件”就是場景中包含,同一個等級結構中有不同的產品族。 ,我們來用一個實際場景來說明下。
假設,我需要一個汽車工廠,汽車可以生產跑車和SUV車型,那麼這個時候用之前的工廠方法模式就不滿足了,方法工廠模式只能滿足生產一個產品,現在需要生產兩個產品。所以我們在方法工廠模式的基礎上改造下來滿足這個場景。
//汽車介面
public interface ICar {
void dirver();
}
//抽象汽車類
public abstract class AbstractProduct implements ICar{
@Override
public void dirver() {
System.out.println("我是汽車,我可以開動");
}
/***
* 展示不同車的亮點*/
public abstract void showSpecial();
}
//寶馬SUV實現類
public class BmwSuv extends AbstractProduct {
@Override
public void showSpecial() {
System.out.println("我是寶馬牌SUV,我的特點就是多功能用途");
}
}
//寶馬跑車實現類
public class BmwProductRoadster extends AbstractProduct {
@Override
public void showSpecial() {
System.out.println("我是寶馬跑車,我的特點就是跑的快");
}
}
//賓士SUV
public class BenzSuv extends AbstractProduct {
@Override
public void showSpecial() {
System.out.println("我是賓士牌SUV,我的特點就是多功能用途");
}
}
//賓士跑車
public class BenzProductRoadster extends AbstractProduct {
@Override
public void showSpecial() {
System.out.println("我是賓士跑車,我的特點就是跑的快");
}
}
//抽象工廠類
public abstract class AbstractFactory {
//建立跑車
public abstract < T extends ICar> T createRoadster();
//建立SUV
public abstract < T extends ICar> T createSuv();
}
//賓士汽車工廠類
public class BenzFactory extends AbstractFactory {
@Override
public <T extends ICar> T createRoadster() {
return (T) new BenzProductRoadster();
}
@Override
public <T extends ICar> T createSuv() {
return (T) new BenzSuv();
}
}
//寶馬汽車工廠類
public class BmwFactory extends AbstractFactory {
@Override
public <T extends ICar> T createRoadster() {
return (T) new BmwProductRoadster();
}
@Override
public <T extends ICar> T createSuv() {
return (T) new BmwSuv();
}
}
//場景類
public class Client {
public static void main(String[] args) {
AbstractFactory bmwFactory=new BmwFactory();
AbstractFactory benzFactory=new BenzFactory();
AbstractProduct bmwRoadster= bmwFactory.createRoadster();
AbstractProduct bmwSuv=bmwFactory.createSuv();
AbstractProduct benzRoadster=benzFactory.createRoadster();
AbstractProduct benzSuv=benzFactory.createSuv();
bmwRoadster.showSpecial();
bmwSuv.showSpecial();
benzRoadster.showSpecial();
benzSuv.showSpecial();
}
}
複製程式碼
細心的讀者應該發現,我們的改造相對於工廠方法模式而言,只是抽象工廠模式新增了多一個建立方法。這也是抽象工廠模式和工廠方法模式最明顯的區別,抽象工廠模式可以滿足多型別,多業務的場景,比如汽車工廠可以生產多種型別汽車,就適用於抽象工廠模式。而普通工廠方法模式就是針對生產某一種物件而言比較適用。總結來說就是,抽象工廠模式適用於多產品族的情況,普通工廠方法模式適用於單產品族情況。
四、總結
上面介紹了工廠方法模式與抽象工廠模式的定義與實踐,同時也衍生了一個簡單工廠模式。我們先來總結下工廠模式的優點與缺點。
優點:
1.良好的封裝性,程式碼結構清晰,不用關注具體物件的建立過程,符合迪米特法則。
2.良好的擴充套件性,我們擴充套件了產品,只要新建對應的工廠類,實現自己的實現邏輯即可。
缺點:
1.對於抽象工廠模式,產品族的擴充套件比較困難,需要修改頂部抽象工廠類,這會導致所有實現類都要修改,不符合開閉原則。
2.對於簡單工廠模式,由於沒有介面和抽象父類的約束,從而也會導致擴充套件只能修改程式碼,不符合開閉原則。
最後我們列一個表格來總結下工廠方法模式,簡單工廠模式,抽象工廠模式的區別。
簡單工廠模式 | 工廠方法模式 | 抽象工廠模式 | |
---|---|---|---|
有無介面或抽象類 | 無 | 有 | 有 |
適用場景 | 一般不涉及擴充套件,單產品族 | 涉及單產品族,便於擴充套件 | 多產品族場景,可以擴充套件等級結構,不便於擴充套件產品族 |
實現複雜度 | 最簡單 | 簡單 | 一般 |
五、參考
《設計模式之禪》