天天看點

JAVA 設計模式(三)—— 設計模式之工廠模式一、工廠模式概述二、簡單工廠模式三、工廠方法模式四、抽象工廠模式

文章目錄

  • 一、工廠模式概述
    • 1、工廠模式簡介
    • 2、工廠模式分類
  • 二、簡單工廠模式
    • 1、簡單工廠模式示例
    • 2、簡單工廠模式分析
  • 三、工廠方法模式
    • 1、工廠方法模式示例
    • 2、工廠方法模式分析
  • 四、抽象工廠模式
    • 1、抽象工廠模式示例
    • 2、抽象工廠模式分析

一、工廠模式概述

1、工廠模式簡介

(1)簡介

  • 簡單工廠模式是屬于建立型模式,是工廠模式的一種。簡單工廠模式是由一個工廠對象決定建立出哪一種産 品類的執行個體。簡單工廠模式是工廠模式家族中最簡單實用的模式;
  • 簡單工廠模式定義了一個建立對象的類,由這個類來封裝執行個體化對象的行為(代碼);
  • 在軟體開發中,當我們會用到大量的建立某種、某類或者某批對象時,就會使用到工廠模式。

(2)優缺點

優點:

  • 一個調用者想建立一個對象,隻要知道其名稱就可以了。
  • 擴充性高,如果想增加一個産品,隻要擴充一個工廠類就可以。
  • 屏蔽産品的具體實作,調用者隻關心産品的接口。

缺點:

  • 每次增加一個産品時,都需要增加一個具體類和對象實作工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這并不是什麼好事。

(3)使用場景:

  • 日志記錄器:記錄可能記錄到本地硬碟、系統事件、遠端伺服器等,使用者可以選擇記錄日志到什麼地方。
  • 資料庫通路,當使用者不知道最後系統采用哪一類資料庫,以及資料庫可能有變化時。
  • 設計一個連接配接伺服器的架構,需要三個協定,“POP3”、“IMAP”、“HTTP”,可以把這三個作為産品類,共同實作一個接口。

(4)注意事項

  • 作為一種建立類模式,在任何需要生成複雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是複雜對象适合使用工廠模式,而簡單對象,特别是隻需要通過 new 就可以完成建立的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的複雜度。

2、工廠模式分類

工廠模式根據抽象程度的不同分為三種:簡單工廠模式(也叫靜态工廠模式)、工廠方法模式、抽象工廠模式。

二、簡單工廠模式

1、簡單工廠模式示例

(1)建立一個接口Shape

public interface Shape {
   void draw();
}
           

(2)建立實作接口的實體類。

Rectangle 實體類

public class Rectangle implements Shape {

    @Override
    public void draw() {
        System.out.println("這是Rectangle");
    }
}
           

Square 實體類

public class Square implements Shape {

    @Override
    public void draw() {
        System.out.println("這是Square");
    }
}
           

Circle 實體類

public class Circle implements Shape {

    @Override
    public void draw() {
        System.out.println("這是Circle");
    }
}
           

(3)建立一個工廠,生成基于給定資訊的實體類的對象。

public class ShapeFactory {

    //使用 getShape 方法擷取形狀類型的對象
    public Shape getShape(String shapeType){
        if(shapeType == null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
            return new Rectangle();
        } else if(shapeType.equalsIgnoreCase("SQUARE")){
            return new Square();
        }
        return null;
    }
}
           

(4)使用該工廠,通過傳遞類型資訊來擷取實體類的對象。

public class FactoryPatternDemo {

    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        //擷取 Circle 的對象,并調用它的 draw 方法
        Shape shape1 = shapeFactory.getShape("CIRCLE");
        shape1.draw();

        //擷取 Rectangle 的對象,并調用它的 draw 方法
        Shape shape2 = shapeFactory.getShape("RECTANGLE");
        shape2.draw();

        //擷取 Square 的對象,并調用它的 draw 方法
        Shape shape3 = shapeFactory.getShape("SQUARE");
        shape3.draw();
    }
}
           

(5)輸出結果

這是Circle
這是Rectangle
這是Square
           

2、簡單工廠模式分析

(1)優點和缺點

優點

  • 工廠類含有必要的判斷邏輯,可以決定在什麼時候建立哪一個産品類的執行個體,用戶端可以免除直接建立産品對象的責任,而僅僅“消費”産品;簡單工廠模式通過這種做法實作了對責任的分割,它提供了專門的工廠類用于建立對象。
  • 用戶端無須知道所建立的具體産品類的類名,隻需要知道具體産品類所對應的參數即可,對于一些複雜的類名,通過簡單工廠模式可以減少使用者的記憶量。
  • 通過引入配置檔案,可以在不修改任何用戶端代碼的情況下更換和增加新的具體産品類,在一定程度上提高了系統的靈活性。

缺點

  • 由于工廠類集中了所有産品建立邏輯,一旦不能正常工作,整個系統都要受到影響。
  • 使用簡單工廠模式将會增加系統中類的個數,在一定程式上增加了系統的複雜度和了解難度。
  • 系統擴充困難,一旦添加新産品就不得不修改工廠邏輯,同樣破壞了“開閉原則”;在産品類型較多時,有可能造成工廠邏輯過于複雜,不利于系統的擴充和維護。
  • 簡單工廠模式由于使用了靜态工廠方法,造成工廠角色無法形成基于繼承的等級結構。

(2)适用環境

  • 工廠類負責建立的對象比較少:由于建立的對象較少,不會造成工廠方法中的業務邏輯太過複雜。
  • 用戶端隻知道傳入工廠類的參數,對于如何建立對象不關心:用戶端既不需要關心建立細節,甚至連類名都不需要記住,隻需要知道類型所對應的參數。

(3)JDK中使用到的簡單工廠模式

  • JDK類庫中廣泛使用了簡單工廠模式,如工具類java.text.DateFormat,它用于格式化一個本地日期或者時間。
public final static DateFormat getDateInstance(); 
public final static DateFormat getDateInstance(int style); 
public final static DateFormat getDateInstance(int style,Locale locale);
           
  • 擷取不同加密算法的密鑰生成器。

三、工廠方法模式

1、工廠方法模式示例

(1)建立一個抽象類,并設定一個抽象方法

public abstract class Computer {
    public abstract Computer show();
}
           

(2)建立實體類繼承抽象的Computer類

Lenvon 實體類

public class Lenvon extends Computer{
    @Override
    public Computer show() {
        System.out.println("這是聯想電腦");
        return null;
    }
}
           

Dell 實體類

public class Dell extends Computer {
    @Override
    public Computer show() {
        System.out.println("這是戴爾電腦");
        return null;
    }
}
           

(3)建立一個抽象工廠類,它是具體工廠的公共接口

public abstract class Factory{
    public abstract Computer getComputer();
}
           

(4)建立具體工廠類(繼承抽象工廠類),定義建立對應具體産品執行個體的方法;

Lenvon工廠

public class LenvonFactory extends Factory {
    @Override
    public Computer getComputer() {
        return new Lenvon();
    }
}
           

Dell工廠

public class DellFactory extends Factory {
    @Override
    public Computer getComputer() {
        return new Dell();
    }
}
           

(5)main方法類調用

public class Gcffms {
    public static void main(String[] args) {
        /*調用Lenvon電腦*/
        LenvonFactory lenvonFactory = new LenvonFactory();
        lenvonFactory.getComputer().show();
        /*調用Dell電腦*/
        DellFactory dellFactory = new DellFactory();
        dellFactory.getComputer().show();
    }
}
           

(6)輸出結果

這是聯想電腦
這是戴爾電腦
           

2、工廠方法模式分析

(1)優缺點

優點

  • 在工廠方法模式中,工廠方法用來建立用戶端所需要的産品,同時還向用戶端隐藏了哪種具體産品類将被執行個體化這一細節,用戶端隻需要關心所需産品對應的工廠,無須關心建立細節,甚至無須知道具體産品類的類名。
  • 基于工廠角色和産品角色的多态性設計是工廠方法模式的關鍵。它能夠使工廠可以自主确定建立何種産品對象,而如何建立這個對象的細節則完全封裝在具體工廠内部。工廠方法模式之是以又被稱為多态工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
  • 使用工廠方法模式的另一個優點是在系統中加入新産品時,無須修改抽象工廠和抽象産品提供的接口,無須修改用戶端,也無須修改其他的具體工廠和具體産品,而隻要添加一個具體工廠和具體産品就可以了。這樣,系統的可擴充性也就變得非常好,完全符合“開閉原則”。

缺點

  • 在添加新産品時,需要編寫新的具體産品類,而且還要提供與之對應的具體工廠類,系統中類的個數将成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和運作,會給系統帶來一些額外的開銷。
  • 由于考慮到系統的可擴充性,需要引入抽象層,在用戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和了解難度,且在實作時可能需要用到DOM、反射等技術,增加了系統的實作難度。

(2)适用環境

  • 一個類不知道它所需要的對象的類:在工廠方法模式中,用戶端不需要知道具體産品類的類名,隻需要知道所對應的工廠即可,具體的産品對象由具體工廠類建立;用戶端需要知道建立具體産品的工廠類。
  • 一個類通過其子類來指定建立哪個對象:在工廠方法模式中,對于抽象工廠類隻需要提供一個建立産品的接口,而由其子類來确定具體要建立的對象,利用面向對象的多态性和裡氏代換原則,在程式運作時,子類對象将覆寫父類對象,進而使得系統更容易擴充。
  • 将建立對象的任務委托給多個工廠子類中的某一個,用戶端在使用時可以無須關心是哪一個工廠子類建立産品子類,需要時再動态指定,可将具體工廠類的類名存儲在配置檔案或資料庫中。

(3)模式擴充

  • 使用多個工廠方法:在抽象工廠角色中可以定義多個工廠方法,進而使具體工廠角色實作這些不同的工廠方法,這些方法可以包含不同的業務邏輯,以滿足對不同的産品對象的需求。
  • 産品對象的重複使用:工廠對象将已經建立過的産品儲存到一個集合(如數組、List等)中,然後根據客戶對産品的請求,對集合進行查詢。如果有滿足要求的産品對象,就直接将該産品傳回用戶端;如果集合中沒有這樣的産品對象,那麼就建立一個新的滿足要求的産品對象,然後将這個對象在增加到集合中,再傳回給用戶端。
  • 多态性的喪失和模式的退化:如果工廠僅僅傳回一個具體産品對象,便違背了工廠方法的用意,發生退化,此時就不再是工廠方法模式了。一般來說,工廠對象應當有一個抽象的父類型,如果工廠等級結構中隻有一個具體工廠類的話,抽象工廠就可以省略,也将發生了退化。當隻有一個具體工廠,在具體工廠中可以建立所有的産品對象,并且工廠方法設計為靜态方法時,工廠方法模式就退化成簡單工廠模式。

(4)JDK中使用到的簡單工廠模式

  • java.util.Calendar,ResourceBundle and NumberFormat getInstance()
  • valueOf() 在包裝類中,如Boolean, Integer 也使用了工廠方法模式

四、抽象工廠模式

1、抽象工廠模式示例

設計一個案例,有Lenvon和Dell兩個工廠,分别生産Lenvon電腦和Lenvon手機、Dell電腦和Dell手機

(1)建立一個電腦接口Computer接口和一個手機接口Phone接口

Computer接口

public interface Computer {
    public void makeComputer();
}
           

Phone接口

public interface Phone {
    public void makePhone();
}
           

(2)建立具體的實體類分别實作Computer接口和Phone接口

LenvonComputer 實體類

public class LenvonComputer implements Computer {
    @Override
    public void makeComputer() {
        System.out.println("Lenvon生産一台聯想電腦");
    }
}
           

DellComputer 實體類

public class DellComputer implements Computer {
    @Override
    public void makeComputer() {
        System.out.println("Dell生産一台戴爾電腦");
    }
}
           

LenvonPhone實體類

public class LenvonPhone implements Phone {
    @Override
    public void makePhone() {
        System.out.println("Lenvon生産一部聯想手機");
    }
}
           

DellPhone 實體類

public class DellPhone implements Phone {
    @Override
    public void makePhone() {
        System.out.println("Dell生産一部戴爾手機");
    }
}
           

(3)建立一個工廠接口,并建立實體工廠實作工廠接口

工廠接口Factory

public interface Factory {
    public Computer getComputer();
    public Phone getPhone();
}
           

實體工廠LenvonFactory

public class LenvonFactory implements Factory {
    @Override
    public Computer getComputer() {
        return new LenvonComputer();
    }

    @Override
    public Phone getPhone() {
        return new LenvonPhone();
    }
}
           

實體工廠DellFactory

public class DellFactory implements Factory {
    @Override
    public Computer getComputer() {
        return new DellComputer();
    }

    @Override
    public Phone getPhone() {
        return new DellPhone();
    }
}
           

(4)main方法類測試,由不同的工廠生産不同的産品

public class Cxgcms {
    public static void main(String[] args) {
        LenvonFactory lenvonFactory = new LenvonFactory();
        lenvonFactory.getComputer().makeComputer();
        lenvonFactory.getPhone().makePhone();

        DellFactory dellFactory = new DellFactory();
        dellFactory.getComputer().makeComputer();
        dellFactory.getPhone().makePhone();
    }
}
           

(5)輸出結果

Lenvon生産一台聯想電腦
Lenvon生産一部聯想手機
Dell生産一台戴爾電腦
Dell生産一部戴爾手機
           

2、抽象工廠模式分析

(1)優點和缺點

優點

  • 抽象工廠模式隔離了具體類的生成,使得客戶并不需要知道什麼被建立。由于這種隔離,更換一個具體工廠就變得相對容易。所有的具體工廠都實作了抽象工廠中定義的那些公共接口,是以隻需改變具體工廠的執行個體,就可以在某種程度上改變整個軟體系統的行為。另外,應用抽象工廠模式可以實作高内聚低耦合的設計目的,是以抽象工廠模式得到了廣泛的應用。
  • 當一個産品族中的多個對象被設計成一起工作時,它能夠保證用戶端始終隻使用同一個産品族中的對象。這對一些需要根據目前環境來決定其行為的軟體系統來說,是一種非常實用的設計模式。

    增加新的具體工廠和産品族很友善,無須修改已有系統,符合“開閉原則”。

缺點

  • 在添加新的産品對象時,難以擴充抽象工廠來生産新種類的産品,這是因為在抽象工廠角色中規定了所有可能被建立的産品集合,要支援新種類的産品就意味着要對該接口進行擴充,而這将涉及到對抽象工廠角色及其所有子類的修改,顯然會帶來較大的不便。
  • 開閉原則的傾斜性

    (1) 增加産品族:對于增加新的産品族,抽象工廠模式很好地支援了“開閉原則”,隻需要增加具體産品并對應增加一個新的具體工廠,對已有代碼無須做任何修改。

    (2) 增加新的産品等級結構:對于增加新的産品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生産新産品的方法,違背了“開閉原則”。

  • 正因為抽象工廠模式存在“開閉原則”的傾斜性,它以一種傾斜的方式來滿足“開閉原則”,為增加新産品族提供友善,但不能為增加新産品結構提供這樣的友善,是以要求設計人員在設計之初就能夠全面考慮,不會在設計完成之後向系統中增加新的産品等級結構,也不會删除已有的産品等級結構,否則将會導緻系統出現較大的修改,為後續維護工作帶來諸多麻煩。

(2)适用環境

  • 一個系統不應當依賴于産品類執行個體如何被建立、組合和表達的細節,這對于所有類型的工廠模式都是很重要的,使用者無須關心對象的建立過程,将對象的建立和使用解耦。
  • 系統中有多于一個的産品族,而每次隻使用其中某一産品族。可以通過配置檔案等方式來使得使用者可以動态改變産品族,也可以很友善地增加新的産品族。
  • 屬于同一個産品族的産品将在一起使用,這一限制必須在系統的設計中展現出來。同一個産品族中的産品可以是沒有任何關系的對象,但是它們都具有一些共同的限制,如同一制作水果蛋糕用的水果–草莓和芒果,草莓和芒果之間沒有直接關系,但它們都是屬于水果。
  • 産品等級結構穩定,設計完成之後,不會向系統中增加新的産品等級結構或者删除已有的産品等級結構。

(3)模式擴充

“開閉原則”要求系統對擴充開放,對修改封閉,通過擴充達到增強其功能的目的。對于涉及到多個産品族與多個産品等級結構的系統,其功能增強包括兩方面:

  • 增加産品族:對于增加新的産品族,工廠方法模式很好的支援了“開閉原則”,對于新增加的産品族,隻需要對應增加一個新的具體工廠即可,對已有代碼無須做任何修改。
  • 增加新的産品等級結構:對于增加新的産品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生産新産品的方法,不能很好地支援“開閉原則”。

工廠模式的退化

  • 當抽象工廠模式中每一個具體工廠類隻建立一個産品對象,也就是隻存在一個産品等級結構時,抽象工廠模式退化成工廠方法模式;
  • 當工廠方法模式中抽象工廠與具體工廠合并,提供一個統一的工廠來建立産品對象,并将建立對象的工廠方法設計為靜态方法時,工廠方法模式退化成簡單工廠模式。

繼續閱讀