1. 程式人生 > >阿里P7大牛細說架構——設計模式專欄

阿里P7大牛細說架構——設計模式專欄

設計模式介紹

對於有經驗的開發人員,學習設計模式有助於我們找到在軟體開發過程中所面臨的問題的最佳解決方案。一直以來軟體都是為了用來解決現實生活中遇到的複雜問題而存在,設計模式(Design pattern)就像一套基礎武功心法,每一式都代表了一類問題的最佳實踐,且可根據實際情況組合使用。本系列文章筆者將帶大家一起從零開始學習設計模式,後面會逐個剖析23種設計模式在Java中的具體實現,讀者需具備基本的Java程式設計概念。

文末有福利放送,感謝讀者的閱讀。

設計模式(Design pattern)代表了最佳的實踐,通常被有經驗的面向物件的軟體開發人員所採用。設計模式是軟體開發人員在軟體開發過程中面臨的一般問題的解決方案。這些解決方案是眾多軟體開發人員經過相當長的一段時間的試驗和錯誤總結出來的

設計模式可以通過提供經過測試和驗證的開發範例來加快開發過程

重用設計模式有助於防止可能導致重大問題的微妙問題,同時也提高了熟悉模式的程式設計師和架構師的程式碼可讀性

什麼是GOF(四人幫,全拼Gang of Four)

在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名為 Design Patterns - Elements of Reusable Object-Oriented Software(中文譯名:設計模式 - 可複用的面向物件軟體元素) 的書,該書首次提到了軟體開發中設計模式的概念。

四位作者合稱 GOF(四人幫,全拼 Gang of Four)。他們所提出的設計模式主要是基於以下的面向物件設計原則。

  • 對介面程式設計而不是對實現程式設計。
  • 優先使用物件組合而不是繼承。

用途

設計模式的主要用途有兩個,一是提供了一個標準的術語系統,且具體到特定的場景。如單例設計模式意味著使用單個物件,這樣所有熟悉單例設計模式的開發人員都能使用單個物件,並且可以通過這種方式告訴對方,程式使用的是單例模式。二是提供了最佳的實踐,設計模式已經經歷了很長一段時間的發展,它們提供了軟體開發過程中面臨的一般問題的最佳解決方案。學習這些模式有助於經驗不足的開發人員通過一種簡單快捷的方式來學習軟體設計

設計模式的型別

根據設計模式的參考書 Design Patterns - Elements of Reusable Object-Oriented Software(中文譯名:設計模式 - 可複用的面向物件軟體元素) 中所提到的,總共有 23 種設計模式。這些模式可以分為三大類:建立型模式(Creational Patterns)、結構型模式(Structural Patterns)、行為型模式(Behavioral Patterns)。還有另一類設計模式:

J2EE設計模式

。這些設計模式從易到難可以分為三個等級:Difficulty-Beginner(難度-初學者), Difficulty-Intermediate(難度-中級) & Difficulty-Expert(難度-專家),後續文章我將從易到難的為大家介紹不同型別的設計模式。

阿里P7大牛細說架構——設計模式專欄

 

阿里P7大牛細說架構——設計模式專欄

 

阿里P7大牛細說架構——設計模式專欄

 

下面一張圖片整體描述了一下設計模式之間的關係:

阿里P7大牛細說架構——設計模式專欄

 

設計模式的六大原則

1、開閉原則(Open Close Principle)

開閉原則的意思是:對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。簡言之,是為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。

2、里氏代換原則(Liskov Substitution Principle)

里氏代換原則是面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。

3、依賴倒轉原則(Dependence Inversion Principle)

這個原則是開閉原則的基礎,具體內容:針對介面程式設計,依賴於抽象而不依賴於具體。

4、介面隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟體架構出發、便於升級和維護的軟體設計思想,它強調降低依賴,降低耦合。

5、迪米特法則,又稱最少知道原則(Demeter Principle)

最少知道原則是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。

6、合成複用原則(Composite Reuse Principle)

合成複用原則是指:儘量使用合成/聚合的方式,而不是使用繼承。

設計模式的思考

在學習和理解設計模式之前,你應該熟悉一些程式設計/軟體設計原則。 所有的設計都應該儘可能簡單,從最簡單的事情開始,慢慢延伸,這可能是工作學習的原理。

只有在實際要求可擴充套件性,需要複雜性和套路模式時,才應該引入它們(設計模式)。 一旦你熟悉了這些設計模式的概念,你就可以根據實際工程情況選擇最適合的模式來優雅的實現工程目標。

工廠模式

工廠模式也被稱之為虛擬建構函式(Virtual Constructor),是Java中最常用的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。

在工廠模式中,我們在建立物件時不會對客戶端暴露建立邏輯,並且是通過使用一個共同的介面來指向新建立的物件

意圖

定義用於建立物件的介面,但是讓子類決定例項化哪個類。factory方法允許類將例項化推遲到子類

主要解決:介面選擇的問題。

何時使用:我們明確地計劃不同條件下建立不同例項時。

如何解決:讓其子類實現工廠介面,返回的也是一個抽象的產品。

關鍵程式碼:建立過程在其子類執行。

解釋

現實世界的例子

鐵匠製造武器。精靈需要精靈武器,獸人需要獸人武器。根據手頭的顧客,召集合適型別的鐵匠

簡而言之

它提供了一種將例項化邏輯委託給子類的方法

維基百科說

In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.(在基於類的程式設計中,factory方法模式是一種建立模式,它使用factory方法來處理建立物件的問題,而不必指定將要建立的物件的確切類。這是通過呼叫factory方法(在介面中指定並由子類實現,或者在基類中實現並可選地由派生類重寫)來實現的,而不是通過呼叫建構函式來實現的。)

程式程式碼示例

以現實世界的例子鐵匠製造武器為例子,需要什麼樣的武器型別我們就召喚對應型別的鐵匠。程式類圖如下:

阿里P7大牛細說架構——設計模式專欄

 

鐵匠介面實現類圖.png

首先我們有一個鐵匠介面(定義了一個製造武器的方法)和一些精靈鐵匠、獸人鐵匠實現類:

public interface Blacksmith {
 Weapon manufactureWeapon(WeaponType weaponType);
}
public class ElfBlacksmith implements Blacksmith {
 public Weapon manufactureWeapon(WeaponType weaponType) {
 return new ElfWeapon(weaponType);
 }
}
public class OrcBlacksmith implements Blacksmith {
 public Weapon manufactureWeapon(WeaponType weaponType) {
 return new OrcWeapon(weaponType);
 }
}
複製程式碼

其次我們有一個武器介面(定義了一個獲取武器型別的方法)和一些精靈武器、獸人武器實現類:

/**
* Weapon interface.
*/
public interface Weapon {
 WeaponType getWeaponType();
}
/**
* ElfWeapon.
*/
public class ElfWeapon implements Weapon {
 private WeaponType weaponType;
 public ElfWeapon(WeaponType weaponType) {
 this.weaponType = weaponType;
 }
 @Override
 public String toString() {
 return "Elven " + weaponType;
 }
 @Override
 public WeaponType getWeaponType() {
 return weaponType;
 }
}
/**
* OrcWeapon.
*/
public class OrcWeapon implements Weapon {
 private WeaponType weaponType;
 public OrcWeapon(WeaponType weaponType) {
 this.weaponType = weaponType;
 }
 @Override
 public String toString() {
 return "Orcish " + weaponType;
 }
 @Override
 public WeaponType getWeaponType() {
 return weaponType;
 }
}
複製程式碼

最後,隨著顧客的到來,正確型別的鐵匠被召喚出來,要求製造武器

public class App {
 private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
 private final Blacksmith blacksmith;
 
 /**
 * Creates an instance of <code>App</code> which will use <code>blacksmith</code> to manufacture 
 * the weapons for war.
 * <code>App</code> is unaware which concrete implementation of {@link Blacksmith} it is using.
 * The decision of which blacksmith implementation to use may depend on configuration, or
 * the type of rival in war.
 * @param blacksmith a non-null implementation of blacksmith
 */
 public App(Blacksmith blacksmith) {
 this.blacksmith = blacksmith;
 }
 
 /**
 * Program entry point
 * 
 * @param args command line args
 */
 public static void main(String[] args) {
 // Lets go to war with Orc weapons
 App app = new App(new OrcBlacksmith());
 app.manufactureWeapons();
 
 // Lets go to war with Elf weapons
 app = new App(new ElfBlacksmith());
 app.manufactureWeapons();
 }
 
 private void manufactureWeapons() {
 Weapon weapon;
 weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR);
 LOGGER.info(weapon.toString());
 weapon = blacksmith.manufactureWeapon(WeaponType.AXE);
 LOGGER.info(weapon.toString());
 }
}
複製程式碼

執行App程式結果:

阿里P7大牛細說架構——設計模式專欄

 

app類執行結果輸出.png

適用場景

當遇到如下三種情況時,應使用工廠模式:

  1. 一個類不能預測它必須建立的物件的類
  2. 一個類希望它的子類指定它建立的物件
  3. 類將責任委託給幾個助手子類中的一個,並且你希望本地化哪個助手子類是委託的責任

Java中的現例項子

  • java.util.Calendar
  • java.util.ResourceBundle
  • java.text.NumberFormat
  • java.nio.charset.Charset
  • java.net.URLStreamHandlerFactory
  • java.util.EnumSet
  • javax.xml.bind.JAXBContext

優缺點

優點:

  1. 一個呼叫者想建立一個物件,只要知道其名稱就可以了。
  2. 擴充套件性高,如果想增加一個產品,只要擴充套件一個工廠類就可以。
  3. 遮蔽產品的具體實現,呼叫者只關心產品的介面

缺點:

每次增加一個產品時,都需要增加一個具體實現類和修改物件實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事

最後

工廠模式作為一種建立類模式,在任何需要生成複雜物件的地方,都可以使用工廠模式,比如設計一個連線伺服器的框架,需要三個協議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產品類,共同實現一個通訊介面。

對於簡單物件,特別是只需要通過new就可以完成建立的物件,無需使用工廠模式,因為使用工廠模式必然要引入一個工廠類,這會增加系統的複雜度,切不可為了設計模式而模式。

之後我們將在工廠模式的基礎上繼續延伸介紹抽象工廠模式,難度系統為中級

推薦一個交流學習圈子群:697-57-9751 裡面會分享一些資深架構師錄製的視訊錄影:有Spring,MyBatis,Netty原始碼分析,高併發、高效能、分散式、微服務架構的原理,JVM效能優化這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多: