摘要:抽象工廠模式多個抽象產品類,個抽象產品類可以派生出多個具體產品類。用了工廠方法模式,你替換生成鍵盤的工廠方法,就可以把鍵盤從羅技換到微軟。好處避免頻繁創建對象,節省系統開銷,減輕壓力。
總體分為3大類:
創建型模式 (5種):工廠方法、抽象工廠、單例、建造者、原型
結構型模式(7種):適配器、裝飾器、代理、外觀、橋接、組合、享元
行為型模式(11種):策略、模板方法、觀察者、迭代子、責任鏈、命令、備忘錄、狀態、訪問者、中介者、解釋器
其它(2種):并發型、線程池
開閉原則(總原則):對擴展開放,對修改關閉,需要使用接口和抽象類
單一職責:每個類應該實現單一的職責
里氏替換:基類可以出現的地方,子類一定可以出現。類似于向上轉型,子類對父類的方法盡量不要重寫和重載,會破壞父類定義好的與外界交互規范。是對開閉原則的補充,實現開閉原則的關鍵是抽象化,里氏替換原則是對實現抽象化規范
依賴倒轉:面向接口編程,依賴于抽象,不依賴于具體。代碼中:不與具體類交互,與接口交互。開閉原則的基礎。
接口隔離:接口中的方法在子類中一定能用到,否則拆分成多個接口
迪米特法則(最少知道):對外暴露越少越好
合成復用:盡量使用合成/聚合的方式,而不是使用繼承
創建型模式(5種):工廠方法、抽象工廠、單例、建造者、原型。 一、工廠方法模式
創建1個接口、2個實現類
public interface Sender { void send(); }
public class MailSender implements Sender { @Override public void send() { System.out.println("this is mailsender!"); } }
public class SmsSender implements Sender { @Override public void send() { System.out.println("this is smssender!"); } }
創建1個工廠接口、2個工廠實現類
public interface Provider { Sender produce(); }
public class SendMailFactory implements Provider { @Override public Sender produce() { return new MailSender(); } }
public class SendSmsFactory implements Provider { @Override 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 接口,就 OK 了,無需去改動現成的代碼。這樣做,拓展性較好!
二、抽象工廠模式工廠方法模式和抽象工廠模式的區別如下:
工廠方法模式:
1個抽象產品類可以派生出多個具體產品類。
1個抽象工廠類可以派生出多個具體工廠類。1個具體工廠類只能創建1個具體產品類的實例。
抽象工廠模式:
多個抽象產品類,1個抽象產品類可以派生出多個具體產品類。
1個抽象工廠類可以派生出多個具體工廠類。1個具體工廠類可以創建多個具體產品類的實例,也就是創建的是一個產品線下的多個產品。
對于 java 來說,你能見到的大部分抽象工廠模式都是這樣的:
---它的里面是一堆工廠方法,每個工廠方法返回某種類型的對象。
比如說工廠可以生產鼠標和鍵盤。那么抽象工廠的實現類(它的某個具體子類)的對象都可以生產鼠標和鍵盤,但可能工廠 A 生產的是羅技的鍵盤和鼠標,工廠 B 是微軟的。
用了工廠方法模式,你替換生成鍵盤的工廠方法,就可以把鍵盤從羅技換到微軟。但是用了抽象工廠模式,你只要換家工廠,就可以同時替換鼠標和鍵盤一套。如果你要的產品有幾十個,當然用抽象工廠模式一次替換全部最方便(這個工廠會替你用相應的工廠方法)所以說抽象工廠就像工廠,而工廠方法則像是工廠的一種產品生產線
單例模式能保證在一個JVM中該對象只有一個實例存在。好處:
1、 避免頻繁創建對象,節省系統開銷,減輕 GC 壓力。
2、在系統中某些對象只能有一個(比如一個軍隊出現了多個司令員同時指揮,肯定會亂成一團)
簡單的單例類(單線程):
只能在單線程中用,不能用于多線程。
public class Singleton { /* 持有私有靜態實例,防止被引用,此處賦值為 null,目的是實現延遲加載 */ private static Singleton instance = null; /* 私有構造方法,防止被實例化 */ private Singleton() { } /* 靜態工程方法,創建實例 */ public static Singleton getInstance() { if (instance == null){ instance = new Singleton(); } return instance; } /* 如果該對象被用于序列化,可以保證對象在序列化前后保持一致 */ public Object readResolve(){ return instance; } }
同步代碼塊:
/* 靜態工程方法,創建實例 */ public static Singleton getInstance() { if (instance == null){ synchronized (instance){ if(instance == null){ instance = new Singleton(); } } } return instance; }
在 Java 指令中創建對象和賦值操作是分開進行的,也就是說 instance = new Singleton();語句是分兩步執行的,可能會先為Singleton實例分配空間,再賦值給instance,最后初始化Singleton實例。
A、B 兩個線程為例:
A、B 線程同時進入了第一個 if 判斷
A首先進入 synchronized 塊,由于 instance 為 null,所以它執行 instance = new Singleton();
由于 JVM 內部的優化機制,JVM 先畫出了一些分配給 Singleton 實例的空白內存,并賦值給 instance 成員(注意此時 JVM 沒有開始初始化這個實例),然后 A 離開了 synchronized塊。
B進入 synchronized 塊,由于 instance 此時不是 null,因此它馬上離開了 synchronized 塊并將結果返回給調用該方法的程序。
此時 B 線程打算使用 Singleton 實例,卻發現它沒有被初始化,于是錯誤發生了。
上面這些話可以理解為A線程從同步代碼塊出來后,JVM沒有初始化Singleton實例,B線程調用instance時發現Singleton沒有初始化。
多線程單例
使用內部類來維護單例的實現,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; } /* 如果該對象被用于序列化,可以保證對象在序列化前后保持一致 */ public Object readResolve(){ return getInstance(); } }
將創建和賦值分開,多帶帶為創建類加靜態同步方法
因為我們只需要在創建類的時候進行同步,所以只要將創建和getInstance()分開,多帶帶為創建加 synchronized 關鍵字,也是可以的
public class SingletonTest { private static SingletonTest instance = null; public SingletonTest() { } private static synchronized void syncInit(){ if(instance == null){ instance = new SingletonTest(); } } public static SingletonTest getInstance() { if (instance == null){ syncInit(); } return instance; } }
補充:用 采用" 影子實例"的辦法為單例對象的屬性同步更新
public class SingletonTest { private static SingletonTest instance = null; private Vector properties = null; public Vector getProperties() { return properties; } public SingletonTest() { } private static synchronized void syncInit(){ if(instance == null){ instance = new SingletonTest(); } } public static SingletonTest getInstance() { if (instance == null){ syncInit(); } return instance; } public void updateProperties(){ SingletonTest shadow = new SingletonTest(); properties = shadow.getProperties(); } }
類和靜態方法與靜態類區別:
靜態類不能實現接口。(從類的角度說是可以的,但是那樣就破壞了靜態了。因為接口中不允許有 static 修飾的方法,所以即使實現了也是非靜態的)
單例可以被延遲初始化,靜態類一般在第一次加載是初始化。之所以延遲加載,是因為有些類比較龐大,所以延遲加載有助于提升性能。
單例類可以被繼承,他的方法可以被覆寫。但是靜態類內部方法都是 static,無法被覆寫。
四、建造者模式(Builder) 五、原型模式(Prototype)將一個對象作為原型,對其進行復制、克隆后產生一個和原對象類似的新對象
淺復制:將一個對象復制后,基本數據類型的變量都會重新創建,而引用類型,指向的還是原對象所指向的。
深復制:將一個對象復制后,不論是基本數據類型還有引用類型,都是重新創建的。
一個原型類,只需要實現 Cloneable 接口,覆寫 clone 方法,此處 clone 方法可以改成任意的名稱,因為 Cloneable 接口是個空接口,你可以任意定義實現類的方法名,如 cloneA或者cloneB,因為此處的重點是super.clone()這句話,super.clone()調用的是Object的clone()方法,而在 Object 類中,clone()是 native 的。這里寫一個深淺復制的例子
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; }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71052.html
摘要:數據源內嵌的數據庫,通過命令直接啟動即可,無需額外安裝。參考資料部署手冊集群部署說明推薦閱讀系列歡迎來到的世界系列基于的注冊中心系列基于的配置中心系列的使用 三種部署模式 Nacos支持三種部署模式 1、單機模式:可用于測試和單機使用,生產環境切忌使用單機模式(滿足不了高可用) 2、集群模式:可用于生產環境,確保高可用 3、多集群模式:可用于多數據中心場景 單機模式 啟動 Nacos ...
摘要:前言設計模式是面向對象的最佳實踐實戰實戰創建型模式單例模式工廠模式抽象工廠模式原型模式建造者模式實戰結構型模式橋接模式享元模式外觀模式適配器模式裝飾器模式組合模式代理模式過濾器模式實戰行為型模式模板模式策略模式狀態模式觀察者模式責任鏈模式訪 前言 設計模式是面向對象的最佳實踐 實戰 PHP實戰創建型模式 單例模式 工廠模式 抽象工廠模式 原型模式 建造者模式 PHP實戰結構型模式 ...
摘要:策略模式介紹策略模式定義了一系列的算法,并將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法獨立于使用它的客戶而獨立變化。使用策略模式的好處策略模式提供了管理相關的算法族的辦法。使用策略模式可以避免使用多重條件轉移語句。 你好,是我琉憶,PHP程序員面試筆試系列圖書的作者。 本周(2019.3.11至3.15)的一三五更新的文章如下: 周一:PHP面試常考之設計模式——工...
摘要:設計一個好的并非易事,本文先從設計時最容易犯的兩個錯誤開始介紹,然后引出如何合理地設計。錯誤以為設計的依據以為設計的依據,往往是一個對應一個子,的結構同返回的數據結構保持一致或接近一致。至此,的結構設計完成。 Redux是一個非常流行的狀態管理解決方案,Redux應用執行過程中的任何一個時刻,都是一個狀態的反映。可以說,State 驅動了Redux邏輯的運轉。設計一個好的State并非...
摘要:一懶漢式線程不安全懶漢式線程不安全私有構造方法只允許在內部進行實例的創建創建實例二懶漢式線程安全懶漢式線程安全私有構造方法只允許在內部進行實例的創建創建實例線程安全三餓漢式線程安全餓漢式私有構造方法只允許在內部進行實例的創建靜態初始化由保證 一、懶漢式(線程不安全) package com.java.singleton; //懶漢式 線程不安全 public class LazySi...
閱讀 2484·2023-04-25 19:24
閱讀 1700·2021-11-11 16:54
閱讀 2833·2021-11-08 13:19
閱讀 3547·2021-10-25 09:45
閱讀 2552·2021-09-13 10:24
閱讀 3276·2021-09-07 10:15
閱讀 4014·2021-09-07 10:14
閱讀 2950·2019-08-30 15:56