Java反射+簡單工廠模式總結

除了 new 以外的建立對象的方法

經過 new 建立對象,會使得程序面向實現編程,先舉個例子,某個果園裏如今有兩種水果,一種是蘋果,一種是香蕉,有客戶想採摘園子裏的水果,要求用get()方法表示便可程序員

通常狀況下,最直接的寫法爲:sql

public class Apple {
    public void get() {
        System.out.println("獲得蘋果");
    }
}
 
public class Banana {
    public void get() {
        System.out.println("獲得香蕉");
    }
}
 
// 客戶端
public static void one() {
        // 實例化一個apple
        Apple apple = new Apple();
        // 實例化一個banana
        Banana banana = new Banana();
        apple.get();
        banana.get();
}

如上代碼,一堆的水果類,必須等到運行時才能知道實例化哪個。一旦水果類有變化或者擴展,還要從新修改客戶端類,一旦代碼量多了,或者系統複雜了,修改的成本是很大的。數據庫

那麼能夠用一種方法替代,就是工廠模式——把實例化的具體代碼從應用中抽離,或者封裝。工廠模式的變形比較多,本文只引伸簡單工廠模式。編程

簡單工廠模式

教科書的定義:bootstrap

簡單工廠模式屬於類的建立型模式,也叫靜態工廠方法模式。它經過專門定義一個類來負責建立其餘類的實例,目的是爲了隱藏具體類的對象的建立過程,既不耽誤對象的建立,也隱藏了建立過程,被建立的實例一般都具備共同父類設計模式

繼續看水果的例子,後來果園有了新需求——用採摘到的水果作果汁,要求使用 doJuice(對應的水果)生產果汁。水果類的代碼能夠保持不變,客戶端新加的其餘代碼以下:app

// 客戶端
private static void doJuice(Apple apple) {
    apple.get();
    System.out.println("作成果汁");
}

private static void doJuice(Banana banana) {
    banana.get();
    System.out.println("作成果汁");
}
 
public static void one() {
        // 實例化一個apple
        Apple apple = new Apple();
        // 實例化一個banana
        Banana banana = new Banana();
        doJuice(apple);
        doJuice(banana);
}

隨着業務發展,後來果園又引進了大量新水果,好比橘子,西瓜,柿子,荔枝,葡萄,哈密瓜,火龍果等。若是繼續用以前的代碼,那麼除了必須新加水果類以外,在客戶端裏還要分別爲每一類水果添加對應的doJuice(水果)方法,然而水果那麼多……使得代碼的維護性,穩定性變差框架

面向接口編程

爲了增長程序的靈活性,能夠作一些抽象,即把各個具體的水果都抽象化,能夠選擇抽象類或者接口去實現,如今要建立不帶任何方法定義和成員變量的抽象化的類,首選的應該是接口ide

如圖1所示,接口有擴展能力,也就是舊的接口能 extends 新接口,從而使得代碼的擴展行爲是可行的性能

使用接口的另外一個緣由和抽象類同樣,都是爲了不某個類被實例化,能夠告訴編譯器,以及一塊兒協做開發的程序員,這個類不須要實例化,它只是爲了對某些行爲作出規範,誰想用,誰就去遵照這個規則便可。

public interface Fruit {
    void get();
}
 
public class AppleA implements Fruit {
    @Override
    public void get() {
        System.out.println("蘋果");
    }
}

public class BananaA implements Fruit {
    @Override
    public void get() {
        System.out.println("香蕉");
    }
}

// 客戶端 
private static void doJuiceB(Fruit fruit) { // Fruit 是接口,只須要一個方法 doJuiceB
    fruit.get();
    System.out.println("作成果汁");
}
 
private static void two() {
        // 使用接口的引用指向子類的對象,向上轉型過程,用到了多態
        Fruit apple = new AppleA();
        Fruit banana = new BananaA();
        Fruit orange = new OrangeA();

        doJuiceB(apple);
        doJuiceB(banana);
        doJuiceB(orange);
    }

綜上,接口的做用能夠歸納爲兩個:

一、避免客戶端去實例化某個類

二、向上轉型的使用(多態) 

分離變的部分

繼續看這個例子,客戶只是想把果園採摘的水果作出果汁,客戶做爲調用者,只須要水果去作出果汁,而水果具體怎麼獲得的,其實客戶不須要也不必關心,調用者不必爲了喝果汁還花代價去親自採摘水果……

以前的設計,客戶端有一個傳入接口類型參數的 doJuiceB(Fruit fruit); 方法。客戶端調用該方法能夠動態的作出不一樣水果的果汁,如今把採集水果的代碼單獨放到一個類裏,隱藏起來,分離變化的部分,咱們叫它工廠類,下面是代碼實現。

// 工廠生產水果
// 對於客戶端來講,再也不直接簡單粗暴的 new 一個水果的實例,而是把生成水果的實例的過程放到一個單獨的類,把這個實例化的過程隱藏了起來……咱們叫它工廠類
public class FruitFactory {
    public static FruitC getApple() {
        return new AppleC();
    }

    public static FruitC getBanana() {
        return new BananaC();
    }
}
 
// 客戶端
private static void doJuice(FruitC fruit) {
    fruit.get();
    System.out.println("作成果汁");
}
 
private static void three() {
    FruitC apple = FruitFactory.getApple();
    FruitC banana = FruitFactory.getBanana();

    doJuice(apple);
    doJuice(banana);
}

簡單工廠模式解決的問題是如何去實例化一個合適的對象,簡單工廠模式的核心思想就是有一個專門的類來負責建立實例。具體來講,把產品看爲是一系列的類的集合,這些類由某個抽象類或者接口派生出一個對象樹,工廠類產生一個合適的對象來知足客戶的要求,從而把對象的建立過程進行封裝

若是簡單工廠模式所涉及到的具體產品之間沒有共同的邏輯,那麼就可使用接口來扮演抽象產品的角色,若是具體產品之間有邏輯上的聯繫,就把這些共同的東西提取出來,放在一個抽象類中,而後讓具體產品繼承抽象類,以實現代碼複用,如圖2所示。借用高斯林(Java之父)所說:

共同的東西老是應該抽象出來。在實際的的使用中,抽象產品和具體產品之間每每是多層次的產品結構

引入簡單工廠模式

上面的設計,對於客戶端來講,再也不直接簡單粗暴的 new 一個水果的實例,而是把生成水果的實例的過程放到一個單獨的類,把這個實例化的過程隱藏了起來……咱們叫它工廠類,這個設計也叫簡單工廠模式——它解決的問題是如何去實例化一個合適的對象。

簡單工廠模式的核心思想就是:有一個專門的類來負責建立實例。具體來講,把產品看着是一系列的類的集合,這些類是由某個抽象類或者接口派生出來的一個對象樹,而工廠類用來產生一個合適的對象來知足客戶的要求,從而把對象的建立過程進行封裝,若是簡單工廠模式所涉及到的具體產品之間沒有共同的邏輯,那麼咱們就可使用接口來扮演抽象產品的角色,若是具體產品之間有邏輯上的聯繫,咱們就把這些共同的東西提取出來,放在一個抽象類中,而後讓具體產品繼承抽象類,爲實現更好複用的目的,共同的東西老是應該抽象出來的。在實際的的使用中,抽象產品和具體產品之間每每是多層次的產品結構,如圖:

下面看看教科書的定義:簡單工廠模式屬於類的建立型模式,也叫靜態工廠方法模式,經過專門定義一個類來負責建立其餘類的實例,目的是爲了隱藏具體類的對象的建立過程,既不耽誤對象的建立,也隱藏了建立過程。被建立的實例一般都具備共同父類

本例子裏,蘋果和香蕉都有一個共同的父類——水果,此時咱們專門定義一個類,負責建立其餘類的實例,這個類叫簡單工廠類,它有三個角色:

一、工廠(Creator)角色 :簡單工廠模式的核心,它負責實現建立全部實例的內部邏輯。工廠類能夠被外界直接調用,建立所需的產品對象。

二、抽象產品(Product)角色: 簡單工廠模式所建立的全部對象的父類,它負責描述全部實例所共有的公共接口,或者抽象類。

3.具體產品(Concrete Product)角色: 簡單工廠模式所建立的具體實例對象,這些對象去繼承或者實現抽象角色

不過,細細體味下,在工廠類裏針對每個水果都有一個對應的獲取水果的操做,這是一種很粗糙的設計,還能夠更好,就是把每一個get方法抽象爲一個公用的get方法,代碼以下:

public interface FruitD {
    void get();
}
//////////////////////////////
public class AppleD implements FruitD {
    @Override
    public void get() {
        System.out.println("蘋果");
    }
}
///////////////////////////////
public class BananaD implements FruitD {
    @Override
    public void get() {
        System.out.println("香蕉");
    }
}
//////////////////////////////
public class FruitFactoryFour {
    public static FruitD getFruit(String type) {
        if ("apple".equalsIgnoreCase(type)) {
            return new AppleD();
        } else if ("banana".equalsIgnoreCase(type)) {
            return new BananaD();
        } else {
            System.out.print("error!");
        }

        return null;
    }
}

這樣稍微好了點兒,把每一個水果對應的get方法抽象爲一個公用的get方法,工廠類里根據傳入的參數,去判斷應該生成哪一個水果的對象,並把這個對象返回(依然是向上轉型的使用),客戶端只需簡單的進行調用便可。

很是方便,也隱藏了具體產品的實例化過程,完美的完成了客戶和水果廠的需求。

能夠認爲簡單工廠模式的核心是工廠類,這個類含有必要的邏輯判斷(if-else),能夠決定在何時建立哪個類的實例,而調用者則能夠免除直接建立對象的責任。簡單工廠模式經過這種作法實現了對責任的分割,當系統引入新的產品的時候無需修改調用者。

解耦合的簡單工廠模式

雖然簡單工廠模式分離了產品的建立者和消費者,有利於軟件系統結構的優化,可是因爲一切產品建立的業務邏輯都集中在一個工廠類中,致使了沒有很高的內聚性,同時也違背了開閉原則。另外,簡單工廠模式的方法通常都是靜態的,而靜態工廠方法讓子類繼承是可能被隱藏的,所以,簡單工廠模式沒法造成基於基類的繼承樹結構。

到了這裏,其實又要想,不要過分的優化,不要爲了使用設計模式而使用設計模式,若是是業務比較簡單的場景,這樣的簡單工廠模式仍是很是好用的。但不管如何,繁瑣的if-else判斷仍是不太好,一旦判斷條件稍微多點兒,if-else寫起來就很是繁瑣。

觀察一些開源框架實現相似場景的代碼,發現它們使用了 Java 的反射機制省去了判斷的步驟,比以前的繁瑣的 if-else 判斷要好一些,以下代碼。

public interface FruitE {
    void get();
}

public class BananaE implements FruitE {
    @Override
    public void get() {
        System.out.println("香蕉");
    }
}

public class AppleE implements FruitE {
    @Override
    public void get() {
        System.out.println("蘋果");
    }
}
 
// 新的工廠類
public class FruitFactoryFive {
        public static FruitE getFruit(String type) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class fruit = Class.forName(type);
        return (FruitE) fruit.newInstance();
    }
}
 
// 客戶端
private static void five() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        FruitE apple = FruitFactoryFive.getFruit("simpleFactory.five.AppleE");
        FruitE banana = FruitFactoryFive.getFruit("simpleFactory.five.BananaE");

        apple.get();
        banana.get();
}

如此一來,使得工廠的擴展性變強了。

補充:forName 方法和 newInstance 方法

從 JVM 的角度看,使用 new 的時候,這個要 new 的類能夠沒有被 JVM 加載,可是使用 newInstance,就必須保證這個類已經加載且這個類已經連接,而完成這兩個步驟的正是 Class 的靜態方法 forName(......),該方法調用了啓動類加載器(bootstrap加載器)去加載類(不初始化)。

Class 類的對象方法 newInstance 與靜態方法 forName 其實是把 new 關鍵字作的事情分解爲了兩步:

一、加載某個類

二、初始化

這樣分步調用構造器的好處是顯而易見的,由於它的粒度更細,因此程序能夠在實例化類的時候得到更好的靈活性,催生一些降耦手段。

事實上,Class 類的 newinstance 方法常常被各類框架使用,它是解耦合的利器之一,好比設計模式中最最多見的工廠模式。

固然,一些知名的開源框架使用了更高級的asm等字節碼框架,能使反射操做的性能很是高效,而且還能修改已經編譯的字節碼,使得程序的靈活性變得很強。

依託配置文件(註解)徹底解耦

可是依然不完美—客戶端缺乏調用的靈活性,客戶端必須傳入嚴格對應類名的字符串,甚至還要包含完整的包名,才能實例化對應的類,稍微差一點兒,都會失敗。故仍是前面說的,簡單的業務通常使用if-else的方式傳入字符串便可,而稍微複雜的,能夠變爲反射的方式實現,而反射實現工廠類,對於客戶端又顯得調用上不方便。一些開源框架使用了配置文件或者註解解決了該問題,如今Java世界的主流是約定優於配置,註解是主流。

String className = readConfig(); // 從配置文件中得到類的句柄
Class c = Class.forName(className);
factory = (FruitE)c.newInstance();

利用配置文件消滅了寫死的產品類名稱,不管產品怎麼變化,代碼不會再修改,甚至能夠更換類的子類,只要他們繼承該類(實現接口)就能夠。

固然進一步就是自定義註解處理器,實現本身系統的註解

簡單工廠模式的經典案例——JDBC

JDBC是SUN公司提供的一套數據庫編程接口。它能提供簡單、一致的方式訪問各類關係型數據庫。Java經過JDBC能夠執行SQL語句,並能對獲取的數據進行處理,將變化了的數據存回數據庫。用JDBC進行數據庫訪問時,要使用數據庫廠商提供的驅動程序接口與DBMS進行交互。客戶端要使用使用數據時,只須要和工廠交互便可,這就是典型的簡單工廠模式的應用。使得程序員的代碼量獲得極大的簡化。

操做步驟按照順序依次爲:

一、註冊並加載數據庫驅動,通常使用Class.forName();

二、建立與數據庫的連接Connection對象

三、建立SQL語句對象preparedStatement(sql);

四、提交SQL語句,根據實際狀況使用executeQuery()或者executeUpdate();

五、顯示相應的結果

六、關閉數據庫

簡單工廠模式的優缺點

優勢

工廠類是整個模式的關鍵所在,它包含必要的判斷邏輯,可以根據外界給定的信息,決定究竟應該建立哪一個具體類的對象。

用戶在使用時能夠直接根據工廠類去建立所需的實例,而無需瞭解這些對象是如何建立以及如何組織的,有利於整個軟件體系結構的優化

缺點

因爲工廠類集中了全部實例的建立邏輯,這就直接致使一旦這個工廠出了問題,全部的客戶端都會受到牽連;

因爲簡單工廠模式的產品基於一個共同的抽象類或者接口,這樣一來,產品的種類增長的時候,即有不一樣的產品接口或者抽象類的時候,工廠類就須要判斷什麼時候建立何種種類的產品,這就和建立何種種類產品的產品相互混淆在了一塊兒,違背了單一職責,致使系統喪失靈活性和可維護性。

簡單工廠模式違背了「開放封閉原則」,由於當新增長一個產品的時候必須修改工廠類,相應的工廠類就須要從新編譯一遍。

一句話:雖然簡單工廠模式分離產品的建立者和消費者,有利於軟件系統結構的優化,但因爲一切邏輯都集中在一個工廠類中,致使了沒有很高的內聚性,同時也違背了「開放封閉原則」。另外,簡單工廠模式的方法通常都是靜態的,而靜態工廠方法是沒法讓子類繼承的,所以,簡單工廠模式沒法造成基於基類的繼承樹結構

引伸: Java 生成對象的方法都有哪些?

Java中有5類建立對象的方式

一、new

二、反射,Class.newInstance()或Contructor.newInstance(),其本質是同樣的,都採用了反射機制

三、clone方法

四、反序列化

五、JNI 

相關文章
相關標籤/搜索