前言java
前面介紹了OOAD的基礎知識,如今咱們來詳細的說明一下GOF設計模式中的23種模式,但願你們可以學到東西!算法
工廠方法模式屬於建立型模式,是簡單工廠模式(Simple Factory)的一個升級版。工廠方法模式分爲三種:普通工廠模式 多個工廠方法模式 靜態工廠方法模式。
編程
1)角色與職責設計模式
抽象產品: 工廠產物的抽象。工廠所要建立的實例的類都繼承於同一個抽象類或者接口。 安全
具體產品: 工廠的具體產品。全部的具體產品都是抽象產品的一個實現類。 多線程
抽象工廠: 工廠類的抽象。注意這個抽象工廠就是抽象的工廠的意思,並非抽象工廠模式(Abstract Factory)的那個抽象工廠。 ide
具體工廠: 建立產品實例的具體工廠類。函數
3)適用場景性能
建立產品對象的過程比較複雜,客戶端並不關心如何建立產品對象,只想使用產品。此外產品的類型比較多,編寫代碼的時候沒有可能去徹底枚舉全部的具體產品,將來還有可能會增長。測試
普通工廠模式:就是創建一個工廠類,對實現了同一接口的產品類進行實例的建立
//發送短信和郵件的接口 public interface Sender { public void Send(); } //發送郵件的實現類 public class MailSender implements Sender { public void Send() { System.out.println("發送郵件!"); } } //發送短信的實現類 public class SmsSender implements Sender { public void Send() { System.out.println("發送短信!"); } } //建立工廠類 public class SendFactory { //工廠方法 public Sender produce(String type) { if ("mail".equals(type)) { return new MailSender(); } else if ("sms".equals(type)) { return new SmsSender(); } else { System.out.println("請輸入正確的類型!"); return null; } } } //測試類 public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produce("sms"); sender.Send(); } }
多個工廠方法模式:是對普通工廠方法模式的改進,在普通工廠方法模式中,若是傳遞的字符串出錯,則不能正確建立對象,而多個工廠方法模式是提供多個工廠方法,分別建立對象。
//將上面的代碼作下修改,改動下SendFactory類就行 //這個就不用根據用戶傳的字符串類建立對象了 public class SendFactory { public Sender produceMail(){ return new MailSender(); } public Sender produceSms(){ return new SmsSender(); } } //測試類 public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produceMail(); sender.Send(); } }
靜態工廠方法模式:將上面的多個工廠方法模式裏的方法置爲靜態的,不須要建立實例,直接調用便可。
public class SendFactory { public static Sender produceMail(){ return new MailSender(); } public static Sender produceSms(){ return new SmsSender(); } } //測試類 public class FactoryTest { public static void main(String[] args) { Sender sender = SendFactory.produceMail(); sender.Send(); } }
抽象工廠模式(Abstract Factory)也屬於建立型設計模式,用於建立系列產品。思路與工廠方法模式(Factory Method)基本一致,只是工廠再也不用於建立單個的產品,而是每一個工廠負責建立一個系列的產品。
工廠方法模式有一個問題就是,類的建立依賴工廠類,也就是說,若是想要拓展程序,必須對工廠類進行修改,這違背了開閉原則,因此,從設計角度考慮,有必定的問題,如何解決?
就用到抽象工廠模式,建立多個工廠類,這樣一旦須要增長新的功能,直接增長新的工廠類就能夠了,不須要修改以前的代碼。
1)角色以及職責
抽象產品: 工廠產物的抽象。由工廠建立的全部系列產品都集成自這些抽象產品。
具體產品: 工廠的具體產品。全部的具體產品都是抽象產品的一個實現類。
抽象工廠: 工廠類的抽象。
具體工廠: 建立產品實例的具體工廠類。能夠理解爲產品的具體的「供應商」。
2)適用場景
客戶端用到的多個產品之間相互關聯,而且客戶端不但願與當前用到的系列產品直接「綁定」,但願能夠靈活的切換系列產品的「供應商」。
//發送短信和郵件的接口 public interface Sender { public void Send(); } //發送郵件的實現類 public class MailSender implements Sender { public void Send() { System.out.println("發送郵件!"); } } //發送短信的實現類 public class SmsSender implements Sender { public void Send() { System.out.println("發送短信!"); } } //給工廠類一個接口 public interface Provider { public Sender produce(); } //兩個工廠的實現類 public class SendMailFactory implements Provider { public Sender produce(){ return new MailSender(); } } public class SendSmsFactory implements Provider{ public Sender produce() { return new SmsSender(); } } //測試類 public class Test { public static void main(String[] args) { Provider provider = new SendMailFactory(); Sender sender = provider.produce(); sender.Send(); } }
注:這個模式的好處就是,若是你如今想增長一個功能:發送及時信息,則只需作一個實現類實現Sender接口,同時作一個工廠類,實現Provider接口就能夠了,
無需去改動現成的代碼。這樣作,拓展性較好,而且還能夠在Provider中加上泛型,這樣未來代碼的擴展性會更好。
單例模式(Singleton)屬於建立型設計模式,用於建立整個程序中只容許存在一個實例的類的對象。單例模式確保能夠在程序中任意地方訪問同一個對象,適合用於全局共享的對象。
單例對象(Singleton)是一種經常使用的設計模式。在Java應用中,單例對象能保證在一個JVM中,該對象只有一個實例存在。這樣的模式有幾個好處:
1)適用場景
單例模式用於建立整個程序中只容許存在一個實例的類的對象。單例模式確保能夠在程序中任意地方訪問同一個對象,適合用於全局共享的對象。
2)單例模式的優勢
某些類建立比較頻繁,對於一些大型的對象,這是一筆很大的系統開銷。
省去了new操做符,下降了系統內存的使用頻率,減輕GC壓力。
有些類如交易所的核心交易引擎,控制着交易流程,若是該類能夠建立多個的話,系統徹底亂了。
//簡單的單例類 餓漢模式 public class Singleton { /* 持有私有靜態實例,防止被引用*/ private static Singleton instance = new Singleton(); /* 私有構造方法,防止被實例化 */ private Singleton() { } /* 靜態工程方法,返回Singleton實例 */ public static Singleton getInstance() { return instance; } }
這個類是能夠實現單例模式的,可是存在很多問題,好比在類中無論用戶是否要使用該類的對象,就先建立好了一個實例放在內存中。
//簡單的單例類 懶漢模式 public class Singleton { /* 持有私有靜態實例,防止被引用,此處賦值爲null,目的是實現延遲加載 */ private static Singleton instance = null; /* 私有構造方法,防止被實例化 */ private Singleton() { } /* 靜態工程方法,建立實例 */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } /* 若是該對象被用於序列化,能夠保證對象在序列化先後保持一致 */ private Object readResolve() { return instance; } }
這個類能夠知足基本要求,可是,像這樣毫無線程安全保護的類,若是咱們把它放入多線程的環境下,確定就會出現問題了,
如何解決?咱們首先會想到對getInstance方法加synchronized關鍵字,以下:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
可是,synchronized做爲修飾符在方法上使用,在性能上會有所降低,由於每次調用getInstance(),都要對對象上鎖,
事實上,只有在第一次建立對象的時候須要加鎖,以後就不須要了,因此,這個地方須要改進。咱們改爲下面這個:
public static Singleton getInstance() { if (instance == null) { synchronized (instance) { if (instance == null) { instance = new Singleton(); } } } return instance; }
彷佛解決了以前提到的問題,將synchronized關鍵字加在了方法內部,也就是說當調用的時候是不須要加鎖的,只有在instance爲null,
並建立對象的時候才須要加鎖,性能有必定的提高。可是,這樣的狀況,仍是有可能有問題的:
看下面的狀況:
在Java指令中建立對象和賦值操做是分開進行的,也就是說instance = new Singleton();語句並不是是一個原子操做,在 JVM 中這句代碼大概作了下面 3 件事情:
1.給 new的對象 分配內存
2.調用 Singleton 的構造函數來初始化成員變量
3.將引用instance指向分配的內存空間(執行完這步 instance 就爲非 null 了)
可是在 JVM 的即時編譯器中存在指令重排序的優化。也就是說上面的第二步和第三步的順序是不能保證的,最終的執行順序多是 1-2-3 也多是 1-3-2。
若是是後者,則在 3 執行完畢、2 未執行以前,另一個線程B搶奪到了CPU的執行權,這時instance已是非null了(但卻沒有初始化),
因此線程B會直接返回 instance,而後使用,結果就會出現問題了(由於對象尚未初始化)。
還有另一種解決方案:
使用內部類來維護單例的實現,JVM內部的機制可以保證當一個類被加載的時候,這個類的加載過程是線程互斥的(就是加載完畢後別的線程才能使用)。
這樣當咱們第一次調用getInstance的時候,JVM可以幫咱們保證instance只被建立一次,而且會保證把賦值給instance的內存初始化完畢,這樣咱們就不用擔憂上面的問題。
同時該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了低性能問題。例如:
public class Singleton { /* 私有構造方法,防止被實例化 */ private Singleton() { } /* 此處使用一個內部類來維護單例 */ private static class SingletonFactory { private static Singleton instance = new Singleton(); } /* 獲取實例 */ public static Singleton getInstance() { return SingletonFactory.instance; } /* 若是該對象被用於序列化,能夠保證對象在序列化先後保持一致 */ private Object readResolve() { return getInstance(); } }
可是若是在構造函數中拋出異常,實例將永遠得不到建立,也會出錯。因此說,十分完美的東西是沒有的,咱們只能根據實際狀況,選擇最適合本身應用場景的實現方法。
同時,咱們還能夠使用反射去建立這個類的對象,即便它的構造器是私有的,咱們也是能夠調用到的。那麼這個時候咱們就須要再次修改代碼去訪問別人反射調用構造器。
//在構造器中控制一下,構造器只容許調用一次,以後在調用就拋出異常 public class Singleton { private volatile static boolean flag; /* 私有構造方法,防止被實例化 */ private Singleton() { if(!flag){ flag = true; }else{ throw new RuntimeException("不能屢次建立單例對象"); } } /* 此處使用一個內部類來維護單例 */ private static class SingletonFactory { private static Singleton instance = new Singleton(); } /* 獲取實例 */ public static Singleton getInstance() { return SingletonFactory.instance; } /* 若是該對象被用於序列化,能夠保證對象在序列化先後保持一致 */ private Object readResolve() { return getInstance(); } }
反射的問題處理完了以後,這裏還有一個問題,就是若是把單例對象進行序列化而後再反序列化,那麼內存中就會出現倆個同樣的單例對象,
只是內存地址不一樣。這種狀況咱們可使用readResolve方法來防止。
private Object readResolve(){.....}
ObjectInputStream 會檢查對象的class是否認義了readResolve方法。若是定義了,將由readResolve方法指定返回的對象。返回對象的類型必定要是兼容的,不然會拋出ClassCastException 。
public abstract class Singleton8 implements Serializable{ private static final long serialVersionUID = 7863921642928237696L; /* 此處使用一個內部類來維護單例 */ private static class SingletonFactory { @SuppressWarnings("serial") private static Singleton8 instance = new Singleton8(){}; } //測試方式,把單例對象序列化後再反序列化從而得到一個新的對象 就至關於複製了一個單例對象 public Singleton8 copy() throws Exception{ ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(Singleton8.getInstance()); InputStream is = new ByteArrayInputStream(os.toByteArray()); ObjectInputStream ois = new ObjectInputStream(is); Singleton8 obj = (Singleton8) ois.readObject(); return obj; } /* 獲取實例 */ public static Singleton8 getInstance() { return SingletonFactory.instance; } /* 若是該對象被用於序列化,能夠保證對象在序列化先後保持一致 */ /* 把這個方法註釋前和註釋後來運行測試代碼觀察結果 */ private Object readResolve() { return getInstance(); } }
建造者模式(Builder)屬於建立型設計模式,一樣用於建立複雜對象。意圖爲將複雜對象的建立算法與「裝配」過程和對象的各個組成部分的建立算法分離。
工廠類模式提供的是建立單個類的模式,而建造者模式則是將各類產品集中起來進行管理,用來建立複合對象,所謂複合對象就是指某個類具備不一樣的屬性。
建造者模式主要用於「分步驟構建一個複雜的對象」,在這其中「分步驟」是一個穩定的算法,而複雜對象的各個部分則常常變化。
所以, 建造者模式主要用來解決「對象部分」的需求變化。 這樣能夠對對象構造的過程進行更加精細的控制。
1)角色與職責
Product: 做爲建立型模式,最重要的確定是建立的產品。Product在這裏是產品。
Builder: 建立者,負責建立產品。
Concrete Builder: Builder的實現
Director: 負責調用Builder去建立具體的產品。這個類決定產品的各個組建裝配成一個完整產品的裝配過程。
2)適用場景
建立的對象很複雜,且這個複雜對象經過不一樣的方式去裝配會有不一樣的表示的狀況。Builder模式在平常編程工做中仍是很是常見的。好比netty的Bootstrap和ServerBootstrap,
其實這兩個「引導」類就是個Builder模式的實踐。還有ProtoBuf中的建立對象的方式,很明顯也是Builder模式的實踐。除此以外,java庫中也有StringBuilder這個比較特殊的Builder。
//CPU接口 public interface CPU { } //Inter的cup class IntelCPU implements CPU{ } //AMD的cpu class AMDCPU implements CPU{ } //內存接口 public interface Memory { } //金士頓內存 class KingstonMemory implements Memory{ } //三星內存 class SamsungMemory implements Memory{ } //主板內存 public interface Mainboard { } //華碩主板 class AsusMainboard implements Mainboard{ } //技嘉主板 class GaMainboard implements Mainboard{ } //計算機 public class Computer { private CPU cpu; private Memory memory; private Mainboard mainboard; get/set } //計算機的builder的接口 public interface ComputerBuilder { public void buildCPU(); public void buildMemory(); public void buildMainboard(); public Computer getComputer(); } //聯想電腦的builder public class LenoveComputerBuilder implements ComputerBuilder { private Computer lenoveComputer; public LenoveComputerBuilder(){ lenoveComputer = new Computer(); } public void buildCPU() { lenoveComputer.setCpu(new IntelCPU()); } public void buildMemory() { lenoveComputer.setMemory(new KingstonMemory()); } public void buildMainboard() { lenoveComputer.setMainboard(new AsusMainboard()); } public Computer getComputer() { return lenoveComputer; } } //惠普電腦的builder public class HPComputerBuilder implements ComputerBuilder { private Computer HPComputer; public HPComputerBuilder(){ HPComputer = new Computer(); } public void buildCPU() { HPComputer.setCpu(new AMDCPU()); } public void buildMemory() { HPComputer.setMemory(new SamsungMemory()); } public void buildMainboard() { HPComputer.setMainboard(new GaMainboard()); } public Computer getComputer() { return HPComputer; } } //Director類(導演) //指導如何具體的創造電腦 public class Director { private ComputerBuilder builder; public Director(ComputerBuilder builder) { this.builder = builder; } //用戶自定義的自造順序 具體指導各類builder如何建立電腦 public void construct() { builder.buildCPU(); builder.buildMemory(); builder.buildMainboard(); } } //測試類 public class Test { public static void main(String[] args) { Computer lenoveComputer = null; ComputerBuilder lenoveComputerBuilder = new LenoveComputerBuilder(); Director director = new Director(lenoveComputerBuilder); director.construct(); lenoveComputer = lenoveComputerBuilder.getComputer(); System.out.println(lenoveComputer); } }
從這點看出,建造者模式將不少功能集成到一個類裏,這個類能夠創造出比較複雜的東西。因此與工廠模式的區別就是:
工廠模式關注的是建立單個產品,而建造者模式則關注建立適合對象的多個部分。所以,是選擇工廠模式仍是建造者模式,依實際狀況而定。
例如一個Person類是由頭、身體、腳三個對象組成,那麼咱們在建造者模式中就要先分別創造出這三個部分而後再把他們組裝成一個Person對象。
原型模式(Builder)屬於建立型設計模式,是個人《GoF設計模式》系列文章中介紹的最後一個建立型模式。原型模式經過複製對象的方式建立對象,意圖爲複用對象既有的狀態以簡化對象建立過程。
1)角色以及職責
Prototype: 原型類的抽象,在這裏約定好克隆對象的操做方式。
Concrete Prototype: 原型類的具體實現。
原型模式雖然是建立型的模式,可是與工程模式沒有關係,從名字便可看出,該模式的思想就是將一個對象做爲原型,
對其進行復制、克隆,產生一個和原對象相似的新對象。在Java中,複製對象是經過clone()實現的,先建立一個原型類:
public class Prototype implements Cloneable { public Object clone() throws CloneNotSupportedException { Prototype proto = (Prototype) super.clone(); return proto; } }
很簡單,一個原型類,只須要實現Cloneable接口,覆寫clone方法,此處clone方法能夠改爲任意的名稱,由於Cloneable接口是個空接口,你能夠任意定義實現類的方法名,
如cloneA或者cloneB,由於此處的重點是super.clone()這句話,super.clone()調用的是Object的clone()方法,而在Object類中,clone()是native的,說明這個方法實現並非使用java語言。
在java.lang下
在這裏先認識倆個概念 : 淺複製 深複製
淺複製:將一個對象複製後,基本數據類型的變量都會從新建立,而引用類型,指向的仍是原對象所指向的。
深複製:將一個對象複製後,不管是基本數據類型還有引用類型,都是從新建立的。簡單來講,就是深複製進行了徹底完全的複製,而淺複製不完全。
public class Prototype implements Cloneable, Serializable { private static final long serialVersionUID = 1L; private String string; //這個是在下面聲明的一個類 private SerializableObject obj; /* 淺複製 */ public Object clone() throws CloneNotSupportedException { Prototype proto = (Prototype) super.clone(); return proto; } /* 深複製 */ public Object deepClone() throws IOException, ClassNotFoundException { /* 寫入當前對象的二進制流 */ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); /* 讀出二進制流產生的新對象 */ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } public String getString() { return string; } public void setString(String string) { this.string = string; } public SerializableObject getObj() { return obj; } public void setObj(SerializableObject obj) { this.obj = obj; } } class SerializableObject implements Serializable { private static final long serialVersionUID = 1L; }
總結:
建立型模式
共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
喜歡就點個「推薦」哦!