經過 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(); }
如此一來,使得工廠的擴展性變強了。
從 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是SUN公司提供的一套數據庫編程接口。它能提供簡單、一致的方式訪問各類關係型數據庫。Java經過JDBC能夠執行SQL語句,並能對獲取的數據進行處理,將變化了的數據存回數據庫。用JDBC進行數據庫訪問時,要使用數據庫廠商提供的驅動程序接口與DBMS進行交互。客戶端要使用使用數據時,只須要和工廠交互便可,這就是典型的簡單工廠模式的應用。使得程序員的代碼量獲得極大的簡化。
操做步驟按照順序依次爲:
一、註冊並加載數據庫驅動,通常使用Class.forName();
二、建立與數據庫的連接Connection對象
三、建立SQL語句對象preparedStatement(sql);
四、提交SQL語句,根據實際狀況使用executeQuery()或者executeUpdate();
五、顯示相應的結果
六、關閉數據庫
工廠類是整個模式的關鍵所在,它包含必要的判斷邏輯,可以根據外界給定的信息,決定究竟應該建立哪一個具體類的對象。
用戶在使用時能夠直接根據工廠類去建立所需的實例,而無需瞭解這些對象是如何建立以及如何組織的,有利於整個軟件體系結構的優化
因爲工廠類集中了全部實例的建立邏輯,這就直接致使一旦這個工廠出了問題,全部的客戶端都會受到牽連;
因爲簡單工廠模式的產品基於一個共同的抽象類或者接口,這樣一來,產品的種類增長的時候,即有不一樣的產品接口或者抽象類的時候,工廠類就須要判斷什麼時候建立何種種類的產品,這就和建立何種種類產品的產品相互混淆在了一塊兒,違背了單一職責,致使系統喪失靈活性和可維護性。
簡單工廠模式違背了「開放封閉原則」,由於當新增長一個產品的時候必須修改工廠類,相應的工廠類就須要從新編譯一遍。
一句話:雖然簡單工廠模式分離產品的建立者和消費者,有利於軟件系統結構的優化,但因爲一切邏輯都集中在一個工廠類中,致使了沒有很高的內聚性,同時也違背了「開放封閉原則」。另外,簡單工廠模式的方法通常都是靜態的,而靜態工廠方法是沒法讓子類繼承的,所以,簡單工廠模式沒法造成基於基類的繼承樹結構
Java中有5類建立對象的方式
一、new
二、反射,Class.newInstance()或Contructor.newInstance(),其本質是同樣的,都採用了反射機制
三、clone方法
四、反序列化
五、JNI