阿里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性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章