這段時間學習了一下軟件開發中的設計模式,這篇讀書筆記就談談我對設計模式的理解。java
設計模式不是一套api,而是一種可複用的、通常性的解決方式,相似於之前談過的MSF,其官方定義以下:設計模式(design pattern)是軟件開發人員在軟件開發過程當中面臨的通常問題的解決方案。設計模式的提出自己是基於面向對象的語言的,沒有了面向對象的繼承與多態,全部設計模式都玩不轉了。設計模式分爲三大類,建立型、結構型、行爲型,此次就主要談談建立型設計模式。程序員
首先是最多見的工廠模式,官方的說法是:定義一個建立對象的接口,讓其子類本身決定實例化哪個工廠類,工廠模式使其建立過程延遲到子類進行。這句話真的很難懂,因此不如舉個例子,到汽車廠提車,不管寶馬、奧迪仍是奧拓只要提就好了,沒必要管具體的製造細節。這樣的說法感受清晰了些,能夠看出工廠模式提供了必定程度上的封裝,但具體它要實現怎樣的目的呢?放一段Java源碼:設計模式
public interface Shape { void draw(); } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public class Square implements Shape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } } public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } public class ShapeFactory { 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; } }
這樣一切都清晰了,用我本身的話說,工廠模式實現的是對一類產品族的封裝。其實你們多少都用過工廠模式,當初學習C++虛基類以及Java接口的時候,確定嘗試過經過一個抽象類實現各類相應的實體類,這個過程其實即便工廠模式的核心,即基於接口與實現技術,對於C++也能夠說是虛基類與繼承技術。要說工廠模式比咱們原來寫的東西多了什麼,就是最後的 ShapeFactory 類以及其 getShape 方法,我相信當初大部分人都不會使用這種寫法,但仔細一下這種方式實現了對上層很是乾淨的封裝,對於用戶來講不再須要想着new一個什麼樣的對象,須要的只是產生一個 ShapeFactory 對象,而後從中 getShape 獲得想要的對象,即只要走進汽車工廠就能夠取到各類各樣的車。api
有句題外話,就是我在看工廠模式的過程當中忽然模糊了Java中虛基類與接口的區別,查了查纔想起來虛基類的部分函數是已經實現了的,子類只需實現那些沒有實現的類,而接口的函數所有都是虛函數,實現類必須實現其中所有的函數。安全
下面看看單例模式,這種模式要求該類本身負責建立本身,且需確保只有單個對象被建立。爲了確保該類不會被實例化,有種騷操做就是把構造函數設爲 private ,這樣若是該類被 new 則編譯器報錯,這種操做真的是長見識。具體的單例模式代碼還分爲懶漢模式和餓漢模式。多線程
懶漢模式:ide
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
其中 synchronized 關鍵字是爲了保證線程安全,即多線程訪問時給對象上鎖。從下能夠看到餓漢模式不存在這種問題。函數
餓漢模式:性能
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; }
這二者的區別就在於,懶漢模式的初始化在被調用時進行,而餓漢模式的初始化在類加載時就進行了,這帶來的結果就是懶漢模式更節省內存,可是其自己存在線程不安全的問題,必須使用 sychronized 關鍵字,因此性能會比較低,通常在單例用的次數少且消耗資源大時使用。學習
單例模式這樣就講清楚了,可是帶來的新的問題是類到底什麼時候被初始化?我查了相關資料發現,JVM並無規定類什麼時候被加載(加載到JVM方法區和堆區),但嚴格規定了什麼時候被初始化,其中最多見的兩種狀況就是 new 一個類或者調用其靜態方法(還包括讀取、設置靜態字段等狀況),一旦出現上述狀況類就必須被初始化,而餓漢模式中的static成員instance在類加載時被賦值,同時 new 了自身,因此該類在加載時就初始化,懶漢模式就能夠等到被調用再初始化。
可見,這麼短短兩段代碼中蘊藏着那麼多知識,充分體現了第一次提出規範單例模式的程序員的智慧。
最後再談談原型模式。原型模式的目的是建立重複的對象,好比說,若是類的初始化須要不少資源,不如只保存一份相應的對象。從這個角度看,原型模式很像是單例模式的擴展,能夠當作是一個產品族的單例集合。看看以下java代碼:
public abstract class Shape implements Cloneable { private String id; protected String type; abstract void draw(); public String getType(){ return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } } public class ShapeCache { private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>(); public static Shape getShape(String shapeId) { Shape cachedShape = shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } public static void loadCache() { Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); Square square = new Square(); square.setId("2"); shapeMap.put(square.getId(),square); Rectangle rectangle = new Rectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(),rectangle); } }
java中自帶的 Cloneable 類能夠很好地實現原型模式的目的。雖然上述代碼有些工廠模式的感受,但其實原型模式的目的與工廠模式徹底不一樣。可是,原型模式最大的用處之一就是和工廠模式結合,若是把上述代碼中的形狀改成工廠,具體的形狀改成不一樣的工廠,那麼最後的 Cache 提供的就是不一樣工廠的克隆,這很好地實現了工廠的單例性。
本文描述了工廠模式、單例模式、原型模式三種常見的建立型設計模式,下次再來講說結構型設計模式。