1. 程式人生 > 程式設計 >23種設計模式之工廠方法模式、抽象工廠模式

23種設計模式之工廠方法模式、抽象工廠模式

1、簡單工廠模式(靜態工廠方法模式)

1.1 定義

定義一個工廠類,根據傳入的引數不同返回不同的例項,被建立的例項具有共同的父類或介面。23種設計模式並不包括簡單工廠模式,它更像一種程式設計習慣。

1.2 模式結構

簡單工廠模式由三部分組成:

  • 工廠類(Creator)角色:擔任這個角色的是簡單工廠模式的核心,含有與應用緊密相關的商業邏輯。工廠類在客戶端的直接呼叫下建立產品物件。
  • 抽象產品(AbstractProduct)角色:擔任這個角色的類是由簡單工廠模式所建立的物件的父類,或它們共同擁有的介面。
  • 具體產品(ConcreteProduct)角色:簡單工廠模式所建立的任何物件都是這個角色的例項。
1.3 例項

1.3.1 電視機父類TV

public abstract class TV {
    abstract void openTV();
}
複製程式碼

1.3.2 海爾電視機和長虹電視機分別繼承TV

public class HaierTV extends TV {
    public void openTV() {
        System.out.println("open haier TV");
    }
}
複製程式碼
public class ChanghongTV extends TV {
    public void openTV() {
        System.out.println("open changhong TV"
); } } 複製程式碼

1.3.3 工廠類

public class Factory {

    public static TV watchTV(String type) throws Exception {
        if (type.equals("Haier")) {
            return new HaierTV();
        } else if (type.equals("Changhong")) {
            return new ChanghongTV();
        } else {
            throw new Exception();
        }
    }
}
複製程式碼

1.3.4 客戶端呼叫

public class Test {
    
    public static void main(String[] args) throws Exception {
        TV tv = Factory.watchTV("Haier");
        tv.openTV();
    }
}
複製程式碼
1.4 適用場景
  • 工廠類負責建立的物件比較小。
  • 客戶端只關心傳入工廠類的引數,不關心物件的建立過程。
1.5 在JDK中的應用

Calendar.class中的部分原始碼:

private static Calendar createCalendar(TimeZone zone,Locale aLocale)
{
    CalendarProvider provider =
        LocaleProviderAdapter.getAdapter(CalendarProvider.class,aLocale)
                             .getCalendarProvider();
    if (provider != null) {
        try {
            return provider.getInstance(zone,aLocale);
        } catch (IllegalArgumentException iae) {
            // fall back to the default instantiation
        }
    }

    Calendar cal = null;

    if (aLocale.hasExtensions()) {
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype != null) {
            switch (caltype) {
            case "buddhist":
            cal = new BuddhistCalendar(zone,aLocale);
                break;
            case "japanese":
                cal = new JapaneseImperialCalendar(zone,aLocale);
                break;
            case "gregory":
                cal = new GregorianCalendar(zone,aLocale);
                break;
            }
        }
    }
    if (cal == null) {
        // If no known calendar type is explicitly specified,// perform the traditional way to create a Calendar:
        // create a BuddhistCalendar for th_TH locale,// a JapaneseImperialCalendar for ja_JP_JP locale,or
        // a GregorianCalendar for any other locales.
        // NOTE: The language,country and variant strings are interned.
        if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone,aLocale);
        } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                   && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone,aLocale);
        } else {
            cal = new GregorianCalendar(zone,aLocale);
        }
    }
    return cal;
}
複製程式碼
1.6 優缺點

1.6.1 優點

  • 工廠類含有必要的判斷邏輯,可以決定在什麼時候建立哪一個產品類的例項,客戶端可以免除直接建立產品物件的責任,而僅僅“消費”產品;簡單工廠模式通過這種做法實現了對責任的分割,它提供了專門的工具類用於建立物件。
  • 客戶端無須知道所建立的具體產品類的類名,只需要知道產品類所對應的引數即可,對應一些複雜的類名,通過簡單工廠模式可以減少使用者的記憶量。
  • 通過引入配置檔案,可以在不修改任何客戶端程式碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。
  • 當需要引入新的產品是不需要修改客戶端的程式碼,只需要新增相應的產品類並修改工廠類就可以了,所以說從產品的角度上簡單工廠模式是符合“開-閉”原則的。

1.6.2 缺點

  • 由於工廠類集中了所有產品建立邏輯,工廠類一般被我們稱作“全能類”或者“上帝類”,因為所有的產品建立它都完成,這看似是好事,但仔細想想是有問題的。這樣一旦不能正常工作,整個系統都要受到影響。
  • 系統擴充套件困難,一旦新增新產品就要修改工廠邏輯,在產品型別較多時,有可能造成工廠邏輯過於複雜,不利於系統的擴充套件和維護。所以說從工廠的角度來說簡單工廠模式是不符合“開-閉”原則的。
  • 由於使用靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構。

2、工廠方法模式(虛擬構造器模式、多型工廠模式)

2.1 定義

工廠父類負責定義建立產品物件的公共介面,而工廠子類則負責生成具體的產品物件,這樣做的目的是將產品類的例項化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該例項化哪一個具體產品類。

2.2 模式結構

工廠方法模式由四部分組成:

  • 抽象工廠(AbstractCreater)角色:擔任這個角色的是工廠方法模式的核心,與呼叫者直接互動用來提供產品。任何在模式中建立物件的工廠類必須繼承或者實現這個介面。
  • 具體工廠(ConcreteCreator)角色:擔任這個角色的是實現了抽象工廠介面的具體實現類。具體工廠角色含有與應用密切相關的邏輯,並且受到應用程式的呼叫以建立產品物件。
  • 抽象產品(AbstractProduct)角色:工廠方法模式所建立的物件的超類。主要目的是定義產品的規範,所有的產品實現都必須遵循這些規範。
  • 具體產品(ConcreteProduct)角色:實現了抽象產品角色所宣告的介面,工廠方法所建立的每一個物件都是某個具體產品角色的例項。
2.3 例項

2.3.1 電視機父類TV

public abstract class TV {
    abstract void openTV();
}
複製程式碼

2.3.2 海爾電視機和長虹電視機分別繼承TV

public class HaierTV extends TV {
    public void openTV() {
        System.out.println("open haier TV");
    }
}
複製程式碼
public class Changhong extends TV {
    public void openTV() {
        System.out.println('open changhong TV');
    }
}
複製程式碼

2.3.3 抽象工廠類

public abstract class Factory {
    public abstract TV watchTV();
}
複製程式碼

2.3.4 具體工廠類

public class HaierFactory extends Factory {
    public TV watchTV() {
        return new HaierTV();
    }
}
複製程式碼
public class ChanghongFactory extends Factory {
    public TV watchTV() {
        return new ChanghongTV();
    }
}
複製程式碼

2.3.5 客戶端呼叫

public class Test {
    public static void main(String[] args) throws Exception {
        Factory factory = new HaierFactory();
        TV tv = factory.watchTV();
        tv.openTV();
    }
}
複製程式碼
2.4 適用場景
  • 客戶端不需要知道它所場景的物件的類,只需要知道建立具體產品的工廠類。
  • 客戶端可以通過子類來指定建立對應的物件。
2.5 優缺點
  • 工廠方法用來建立客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被例項化這一細節,使用者只需要關心所需產品對應的工廠,無須關心建立細節。
  • 基於工廠角色和產品角色的多型性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部。
  • 在系統加入新產品時,無須修改抽象工廠和抽象產品提供的介面,無須修改客戶端,也無須修改其他的具體工廠和具體產品。這樣系統的可擴充套件性變得良好,完全符合“開-閉“原則。

3、抽象工廠模式(Kit模式)

3.1 定義

提供一個建立一系列相關或相互依賴物件的介面,而無須指明它們具體的類。

3.2 產品族和等級等級結構
  • 產品等級結構:產品等級結構即產品的繼承結構,如一個抽象類是電視機,取子類有海爾電視機、長虹電視機,則抽象電視機與具體品牌的電視機之間構成了一個產品等級結構。
  • 產品族:在抽象工廠模式中,產品族是指同一個工廠生產的,位於不同產品等級結構中的一組產品,如海爾電器工廠生產的海爾電視機、海爾電冰箱,海爾電視機位於電視機產品等級結構中,海爾電冰箱位於電冰箱產品等級結構中。

3.3 模式結構

抽象工廠模式和工廠方法模式類似都是四部分組成的,不同的是,抽象工廠模式中具體工廠不再是隻建立一種產品,一個具體的工廠可以建立一個產品族的產品。

3.4 例項

3.4.1 電視機父類TV

public abstract class TV {
    abstract void openTV();
}
複製程式碼

3.4.2 海爾電視機和長虹電視機分別繼承TV

public class HaireTV extends TV {
    public void openTV() {
        System.out.println("open haire TV");
    }
}
複製程式碼
public class ChanghongTV extends TV {
    public void openTV() {
        System.out.println('open changhong TV');
    }
}
複製程式碼

3.4.3 冰箱父類Refrigerator

public abstract class Refrigerator {
    abstract void openRefrigerator();
}
複製程式碼

3.4.4 海爾冰箱和長虹冰箱分別繼承Refrigerator

public class HaierRefrigerator extends Refrigerator {
    public void openRefrigerator() {
        System.out.println("open haier refrigerator");
    }
}
複製程式碼
public class ChanghongRefrigerator extends Refrigerator {
    public void openRefrigerator() {
        System.out.println("open changhong refrigerator");
    }
}
複製程式碼

3.4.5 抽象工廠類,定義同一族產品的兩個不同等級結構的產品結構

public abstract class Factory {
    public abstract TV watchTV();
    public abstract Refrigerator takeThings();
}
複製程式碼

3.4.6 具體工廠類

public class HaierFactory extends Factory {
    public TV watchTV() {
        return new HaierTV();
    }

    public Refrigerator takeThings() {
        return new HaierRefrigerator();
    }
}
複製程式碼
public class ChanghongFactory extends Factory {
    public TV watchTV() {
        return new ChanghongTV();
    }
    
    public Refrigerator takeThings() {
        return new ChanghongRefrigerator();
    }
}
複製程式碼

3.4.7 客戶端呼叫

public class Test {

    public static void main(String[] args) throws Exception {
        Factory factory = new HaierFactory();
        TV tv = factory.watchTV();
        tv.openTV();
        Refrigerator refrigerator = factory.takeThings();
        refrigerator.openRefrigerator();
    }
}
複製程式碼
3.5 適用場景
  • 一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節,這對於所有形態的工廠模式都是重要的。
  • 系統的產品有多於一個的產品族,而系統只消費群體某一族的產品。
  • 同屬於同一產品族的產品是在一起使用的,這一約束必須在系統的設計中體現出來。 *系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴與實現。
3.6 優缺點

3.6.1 優點

  • 抽象工廠模式隔離了具體類的生成,使得客戶不需要知道什麼被建立。由於這種隔離,更換一個具體工廠就變得相對容易。所有的具體工廠都實現了抽象工廠中定義的那些公共介面,因此只需改變具體工廠的例項,就可以在某種程式上改變整個系統的行為。另外,應用抽象工廠模式可以實現高內聚低耦合的設計目的。
  • 當一個產品族中的多個物件被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的物件。這對一些需要根據當前環境來決定其行為的軟體系統來說,是一種非常使用的設計模式。
  • 增加新的具體工廠和產品族很方便,無須改動其他,符合“開閉”原則。

3.6.2 缺點

  • 在新增新的產品物件時,難以擴充套件抽象工廠來生產新種類的產品,這是因為在抽象工廠角色中規定了所有可能被場景的產品集合,要支援新種類的產品就意味著要對介面進行擴充套件,而這將涉及到對抽象工廠角色及其所有子類的修改,顯然帶來較大的不便。
  • 開閉原則的傾斜性(增加新的工廠和產品族容易,增加新的產品等級結構麻煩)。

特別宣告:1、如若文中有錯之處,歡迎大神指出。 2、文章是參考網上一些大神的文章,自己整理出來的,如若有侵權,可聯絡我刪除。