public abstract class Computer {
/**
* 產品的抽象方法,由具體的產品類去實現
*/
public abstract void start();
}
(2)創建具體產品類
//聯想電腦
public class LenovoComputer extends Computer{
@Override
public void start() {
System.out.println("聯想電腦啓動");
}
}
//惠普電腦
public class HpComputer extends Computer{
@Override
public void start() {
System.out.println("惠普電腦啓動");
}
}
//華碩電腦
public class AsusComputer extends Computer{
@Override
public void start() {
System.out.println("華碩電腦啓動");
}
}
(3)創建工程類
public class ComputerFactory {
public static Computer createComputer(String type){
Computer mComputer=null;
switch (type) {
case "lenovo":
mComputer=new LenovoComputer();
break;
case "hp":
mComputer=new HpComputer();
break;
case "asus":
mComputer=new AsusComputer();
break;
}
return mComputer;
}
}
(4)客戶端調用工廠類
public class CreatComputer {
public static void main(String[]args){
ComputerFactory.createComputer("hp").start();
}
}
3、優點和缺點
-
工廠類含有必要的判斷邏輯,可以決定在什麼時候創建哪一個產品類的實例,客戶端可以免除直接創建產品對象的責任,而僅僅“消費”產品;簡單工廠模式通過這種做法實現了對責任的分割,它提供了專門的工廠類用於創建對象。
-
客戶端無須知道所創建的具體產品類的類名,只需要知道具體產品類所對應的參數即可,對於一些複雜的類名,通過簡單工廠模式可以減少使用者的記憶量。
-
通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。
-
由於工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
-
使用簡單工廠模式將會增加系統中類的個數,在一定程序上增加了系統的複雜度和理解難度。
-
系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,同樣破壞了“開閉原則”;在產品類型較多時,有可能造成工廠邏輯過於複雜,不利於系統的擴展和維護。
-
簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構。
-
工廠類負責創建的對象比較少:由於創建的對象較少,不會造成工廠方法中的業務邏輯太過複雜。
-
客戶端只知道傳入工廠類的參數,對於如何創建對象不關心:客戶端既不需要關心創建細節,甚至連類名都不需要記住,只需要知道類型所對應的參數。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);
(2)獲取不同加密算法的密鑰生成器。
KeyGenerator keyGen=KeyGenerator.getInstance("DESede");
二、工廠方法模式(Factory Method Pattern)
1、工廠方法模式結構圖
在工廠方法模式結構圖中包含如下幾個角色:
-
Factory(抽象工廠類):在抽象工廠類中,聲明瞭工廠方法(Factory Method),用於返回一個產品。抽象工廠是工廠方法模式的核心,所有創建對象的工廠類都須實現該接口。
-
ConcreteFactory(具體工廠類):它是抽象工廠類的子類,實現了抽象工廠中定義的工廠方法,並可由客戶端調用,返回一個具體產品類的實例。
-
Product(抽象產品類):它是定義產品的接口,是工廠方法模式所創建對象的超類型,也就是產品對象的公共父類。
-
(ConcreteProduct具體產品類):它實現了抽象產品接口,某種類型的具體產品由專門的具體工廠創建,具體工廠和具體產品之間一一對應。
與簡單工廠模式相比,工廠方法模式最重要的區別是引入了抽象工廠角色,抽象工廠可以是接口,也可以是抽象類或者具體類。
public abstract class Cake {
void prepare(){
System.out.println("步驟:step 1......");
System.out.println("步驟:step 2......");
System.out.println("步驟:step 3......");
System.out.println("步驟:step 4......");
}
void bake(){
System.out.println("烘焙:bake");
}
void box(){
System.out.println("裝盒:box");
}
}
(2)具體產品
public class CenterCheeseCake extends Cake {
public CenterCheeseCake(){
name = "center cheese cake";
}
@Override
public void bake(){
System.out.println("不用烘箱,我要用火烤!");
}
}
public class CollegeFruitCake extends Cake {
public CollegeFruitCake(){
name = "center fruit cake";
}
@Override
public void box(){
System.out.println("不用圓盒子打包,我愛國,用五角星盒子!");
}
}
(3)抽象工廠
public abstract class CakeStore {
public Cake orderCake(String type) {
Cake cake;
cake = createCake(type);
cake.bake();
cake.box();
return cake;
}
protected abstract Cake createCake(String type);
}
(4)具體工廠類
public class CenterCakeStore extends CakeStore {
@Override
protected Cake createCake(String type) {
Cake cake = null;
if ("cheese".equals(type)) {
cake = new CenterCheeseCake();
} else if ("fruit".equals(type)) {
cake = new CenterFruitCake();
} else if ("cream".equals(type)) {
cake = new CenterCreamCake();
}
return cake;
}
}
public class CollegeCakeStore extends CakeStore {
@Override
protected Cake createCake(String type) {
Cake cake = null;
if ("cheese".equals(type)) {
cake = new CollegeCheeseCake();
} else if ("fruit".equals(type)) {
cake = new CollegeFruitCake();
} else if ("cream".equals(type)) {
cake = new CollegeCreamCake();
}
return cake;
}
}
3、優點和缺點
-
工廠方法用來創建客戶端所需要的產品,同時還向客戶端隱藏了哪種具體產品類將被實例化這一細節,客戶端只需要關心所需產品對應的工廠,無須關心創建細節,甚至無須知道具體產品類的類名。
-
基於工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定創建何種產品對象,而如何創建這個對象的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱爲多態工廠模式,是因爲所有的具體工廠類都具有同一抽象父類。
-
使用工廠方法模式的另一個優點是在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的接口,無須修改客戶端,也無須修改其他的具體工廠和具體產品,而只要添加一個具體工廠和具體產品就可以了。這樣,系統的可擴展性也就變得非常好,完全符合“開閉原則”。
-
在添加新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷。
-
由於考慮到系統的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度
-
一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。
-
一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。
-
將創建對象的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或數據庫中。
-
使用多個工廠方法:在抽象工廠角色中可以定義多個工廠方法,從而使具體工廠角色實現這些不同的工廠方法,這些方法可以包含不同的業務邏輯,以滿足對不同的產品對象的需求。
-
產品對象的重複使用:工廠對象將已經創建過的產品保存到一個集合(如數組、List等)中,然後根據客戶對產品的請求,對集合進行查詢。如果有滿足要求的產品對象,就直接將該產品返回客戶端;如果集合中沒有這樣的產品對象,那麼就創建一個新的滿足要求的產品對象,然後將這個對象在增加到集合中,再返回給客戶端。
-
多態性的喪失和模式的退化:如果工廠僅僅返回一個具體產品對象,便違背了工廠方法的用意,發生退化,此時就不再是工廠方法模式了。一般來說,工廠對象應當有一個抽象的父類型,如果工廠等級結構中只有一個具體工廠類的話,抽象工廠就可以省略,也將發生了退化。當只有一個具體工廠,在具體工廠中可以創建所有的產品對象,並且工廠方法設計爲靜態方法時,工廠方法模式就退化成簡單工廠模式。
三、 抽象工廠模式(Abstrract Factory Pattern)
1、抽象工廠模式結構圖
-
AbstractFactory:抽象工廠,它聲明瞭用來創建不同產品的方法。
-
ConcreteFactory:具體工廠,實現抽象工廠中定義的創建產品的方法。
-
AbstractProduct:抽象產品,爲每種產品聲明業務方法。比如上圖的AbstractProductA和 AbstractProductB。
-
ConcreteProduct:具體產品,定義具體工廠生產的具體產品,並實現抽象產品中定義的業務方法。
public abstract class DesktopComputer {
public abstract void start();
}
public abstract class NotebookComputer {
public abstract void start();
}
(2)具體產品類
public class LenovoDesktopComputer extends DesktopComputer {
@Override
public void start() {
System.out.println("聯想臺式電腦啓動");
}
}
public class HpDesktopComputer extends DesktopComputer {
@Override
public void start() {
System.out.println("惠普臺式電腦啓動");
}
}
public class LenovoNotebookComputer extends NotebookComputer {
@Override
public void start() {
System.out.println("聯想筆記本電腦啓動");
}
}
public class HpNotebookComputer extends NotebookComputer {
@Override
public void start() {
System.out.println("惠普筆記本電腦啓動");
}
}
(3)抽象工廠
public abstract class ComputerFactory {
public abstract DesktopComputer createDesktopComputer();
public abstract NotebookComputer createNotebookComputer();
}
(4)具體工廠
public class LenovoFactory extends ComputerFactory {
@Override
public DesktopComputer createDesktopComputer() {
return new LenovoDesktopComputer();
}
@Override
public NotebookComputer createNotebookComputer() {
return new LenovoNotebookComputer();
}
}
public class HpFactory extends ComputerFactory {
@Override
public DesktopComputer createDesktopComputer() {
return new HpDesktopComputer();
}
@Override
public NotebookComputer createNotebookComputer() {
return new HpNotebookComputer();
}
}
(5)客戶端調用
public class Client {
public static void main(String[]args) {
ComputerFactory lenocoFactory=new LenovoFactory();
lenocoFactory.createDesktopComputer().start();
lenocoFactory.createNotebookComputer().start();
ComputerFactory hpFactory=new HpFactory();
hpFactory.createDesktopComputer().start();
hpFactory.createNotebookComputer().start();
}
}
接下來給出這個例子的UML圖,更便於理解:
-
抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什麼被創建。由於這種隔離,更換一個具體工廠就變得相對容易。所有的具體工廠都實現了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實例,就可以在某種程度上改變整個軟件系統的行爲。另外,應用抽象工廠模式可以實現高內聚低耦合的設計目的,因此抽象工廠模式得到了廣泛的應用。
-
當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。這對一些需要根據當前環境來決定其行爲的軟件系統來說,是一種非常實用的設計模式。
-
增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。
-
在添加新的產品對象時,難以擴展抽象工廠來生產新種類的產品,這是因爲在抽象工廠角色中規定了所有可能被創建的產品集合,要支持新種類的產品就意味着要對該接口進行擴展,而這將涉及到對抽象工廠角色及其所有子類的修改,顯然會帶來較大的不便。
-
開閉原則的傾斜性(1) 增加產品族:對於增加新的產品族,抽象工廠模式很好地支持了“開閉原則”,只需要增加具體產品並對應增加一個新的具體工廠,對已有代碼無須做任何修改。(2) 增加新的產品等級結構:對於增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,違背了“開閉原則”。
-
一個系統不應當依賴於產品類實例如何被創建、組合和表達的細節,這對於所有類型的工廠模式都是很重要的,用戶無須關心對象的創建過程,將對象的創建和使用解耦。
-
系統中有多於一個的產品族,而每次只使用其中某一產品族。可以通過配置文件等方式來使得用戶可以動態改變產品族,也可以很方便地增加新的產品族。
-
屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。同一個產品族中的產品可以是沒有任何關係的對象,但是它們都具有一些共同的約束,如同一製作水果蛋糕用的水果--草莓和芒果,草莓和芒果之間沒有直接關係,但它們都是屬於水果。
-
產品等級結構穩定,設計完成之後,不會向系統中增加新的產品等級結構或者刪除已有的產品等級結構。
-
增加產品族:對於增加新的產品族,工廠方法模式很好的支持了“開閉原則”,對於新增加的產品族,只需要對應增加一個新的具體工廠即可,對已有代碼無須做任何修改。
-
增加新的產品等級結構:對於增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,所有的工廠類中都要增加生產新產品的方法,不能很好地支持“開閉原則”。
-
當抽象工廠模式中每一個具體工廠類只創建一個產品對象,也就是隻存在一個產品等級結構時,抽象工廠模式退化成工廠方法模式;
-
當工廠方法模式中抽象工廠與具體工廠合併,提供一個統一的工廠來創建產品對象,並將創建對象的工廠方法設計爲靜態方法時,工廠方法模式退化成簡單工廠模式。