亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

1 認(rèn)識設(shè)計(jì)模式

1.1 設(shè)計(jì)模式簡介

軟件設(shè)計(jì)模式(Software Design Pattern),俗稱設(shè)計(jì)模式,設(shè)計(jì)模式是一套被反復(fù)使用的、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。它描述了在軟件設(shè)計(jì)過程中的一些不斷重復(fù)發(fā)生的問題,以及該問題的解決方案。也就是說,它是解決特定問題的一系列套路,是前輩們的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié),具有一定的普遍性,可以反復(fù)使用。使用設(shè)計(jì)模式的目的是為了代碼重用、讓代碼更容易被他人理解、保證代碼可靠性。

1.2 設(shè)計(jì)原則

優(yōu)良的系統(tǒng)設(shè)計(jì)具備特點(diǎn):

  1. 可擴(kuò)展性(Extensibility)
  2. 靈活性(Flexibility)
  3. 組件化可插拔式(Pluggability)

面向?qū)ο缶幊坛S玫脑O(shè)計(jì)原則包括7個,這些原則并不是孤立存在的,它們相互依賴,相互補(bǔ)充。

1.2.1 單一職責(zé)原則

定義:一個對象應(yīng)該只包含單一的職責(zé),并且該職責(zé)被完整地封裝在一個類中。

解說:一個類(或者大到模塊,小到方法)承擔(dān)的職責(zé)越多,它被復(fù)用的可能性越小,而且如果一個類承擔(dān)的職責(zé)過多,就相當(dāng)于將這些職責(zé)耦合在一起,當(dāng)其中一個職責(zé)變化時,可能會影響其他職責(zé)的運(yùn)作。

類的職責(zé)主要包括兩個方面:數(shù)據(jù)職責(zé)和行為職責(zé),數(shù)據(jù)職責(zé)通過其屬性來體現(xiàn),而行為職責(zé)通過其方法來體現(xiàn)。

單一職責(zé)原則是實(shí)現(xiàn)高內(nèi)聚、低耦合的指導(dǎo)方針,在很多代碼重構(gòu)方法中都能找到它的存在,它是最簡單但又最難運(yùn)用的原則,需要設(shè)計(jì)人員發(fā)現(xiàn)類的不同職責(zé)并將其分離,而發(fā)現(xiàn)類的多重職責(zé)需要設(shè)計(jì)人員具有較強(qiáng)的分析設(shè)計(jì)能力和相關(guān)重構(gòu)經(jīng)驗(yàn)。

實(shí)例:以登錄實(shí)現(xiàn)為例:

原始設(shè)計(jì)方案:

架構(gòu)中的設(shè)計(jì)模式

 

使用單一職責(zé)原則對其進(jìn)行重構(gòu):

架構(gòu)中的設(shè)計(jì)模式

 

1.2.2 開閉原則

定義:一個軟件實(shí)體應(yīng)當(dāng)對擴(kuò)展開放,對修改關(guān)閉。也就是說在設(shè)計(jì)一個模塊的時候,應(yīng)當(dāng)使這個模塊可以在不被修改的前提下被擴(kuò)展,即實(shí)現(xiàn)在不修改源代碼的情況下改變這個模塊的行為。

解說:開閉原則還可以通過一個更加具體的“對可變性封裝原則”來描述,對可變性封裝原則(EVP)要求找到系統(tǒng)的可變因素并將其封裝起來。

如果一個軟件設(shè)計(jì)符合開閉原則,那么可以非常方便地對系統(tǒng)進(jìn)行擴(kuò)展,而且在擴(kuò)展時無須修改現(xiàn)有代碼,使得軟件系統(tǒng)在擁有適應(yīng)性和靈活性的同時具備較好的穩(wěn)定性和延續(xù)性。為了滿足開閉原則,需要對系統(tǒng)進(jìn)行抽象化設(shè)計(jì),抽象化是開閉原則的關(guān)鍵。

實(shí)例:我們拿報表功能來說, BarChart 和 PieChart 為不同的報表功能,此時在 ChartDisplay 中使用報表功能,可以直接new對應(yīng)的功能,但如果增加新的報表功能,在 ChartDisplay 中使用,就需要改代碼了,這就違背了開閉原則。

原始設(shè)計(jì)方案:

架構(gòu)中的設(shè)計(jì)模式

 

基于開閉原則進(jìn)行重構(gòu):

架構(gòu)中的設(shè)計(jì)模式

 

1.2.3 里氏代換原則

定義:所有引用基類(父類)的地方必須能透明地使用其子類的對象。

解說:里氏代換原則可以通俗表述為:在軟件中將一個基類對象替換成它的子類對象,程序?qū)⒉粫a(chǎn)生任 何錯誤和異常,反過來則不成立,如果一個軟件實(shí)體使用的是一個子類對象的話,那么它不 一定能夠使用基類對象。

里氏代換原則是實(shí)現(xiàn)開閉原則的重要方式之一,由于使用基類對象的地方都可以使用子類對 象,因此在程序中盡量使用基類類型來對對象進(jìn)行定義,而在運(yùn)行時再確定其子類類型,用 子類對象來替換父類對象。

實(shí)例:我們以給客戶發(fā)消息為例,給VIP客戶(VipCustomer)和普通客戶(CommonCustomer)發(fā)消息,在SendMessage 中分別定義給普通會員和VIP發(fā)消息,如果以后有新的客戶分類,不僅要添加客戶分類,還要修改SendMessage ,違背了開閉原則。

原始設(shè)計(jì)方案:

架構(gòu)中的設(shè)計(jì)模式

 

基于里氏代換原則進(jìn)行重構(gòu):

架構(gòu)中的設(shè)計(jì)模式

 

1.2.4 依賴倒轉(zhuǎn)原則

定義:抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)當(dāng)依賴于抽象。換言之,要針對接口編程,而不是針對實(shí)現(xiàn)編程。

實(shí)例:我們可以把之前的開閉原則案例修改一下,利用Spring框架進(jìn)行修改,可讀性更強(qiáng),同時遵循了開閉原則、里氏代換原則和依賴倒轉(zhuǎn)原則,如下圖:

架構(gòu)中的設(shè)計(jì)模式

 

1.2.5 接口隔離原則

定義:使用多個專門的接口,而不使用單一的總接口,即客戶端不應(yīng)該依賴那些它不需要的接口。

講解:接口僅僅提供客戶端 需要的行為,客戶端不需要的行為則隱藏起來,應(yīng)當(dāng)為客戶端提供盡可能小的單獨(dú)的接口, 而不要提供大的總接口。在面向?qū)ο缶幊陶Z言中,實(shí)現(xiàn)一個接口就需要實(shí)現(xiàn)該接口中定義的 所有方法,因此大的總接口使用起來不一定很方便,為了使接口的職責(zé)單一,需要將大接口 中的方法根據(jù)其職責(zé)不同分別放在不同的小接口中,以確保每個接口使用起來都較為方便, 并都承擔(dān)某一單一角色。接口應(yīng)該盡量細(xì)化,同時接口中的方法應(yīng)該盡量少,每個接口中只 包含一個客戶端(如子模塊或業(yè)務(wù)邏輯類)所需的方法即可,這種機(jī)制也稱為“定制服務(wù)”,即 為不同的客戶端提供寬窄不同的接口

實(shí)例:下圖展示了一個擁有多個客戶類的系統(tǒng),在系統(tǒng)中定義了一個巨大的接口DataRead來服務(wù)所有的客戶類。

原始設(shè)計(jì)方案:

架構(gòu)中的設(shè)計(jì)模式

 

基于接口隔離原則進(jìn)行重構(gòu):

架構(gòu)中的設(shè)計(jì)模式

 

1.2.6 合成復(fù)用原則

定義:盡量使用對象組合,而不是繼承來達(dá)到復(fù)用的目的

講解:合成復(fù)用原則就是在一個新的對象里通過關(guān)聯(lián)關(guān)系(包括組合關(guān)系和聚合關(guān)系)來使用一些 已有的對象,使之成為新對象的一部分;新對象通過委派調(diào)用已有對象的方法達(dá)到復(fù)用功能 的目的。簡言之:復(fù)用時要盡量使用組合/聚合關(guān)系(關(guān)聯(lián)關(guān)系),少用繼承。

在面向?qū)ο笤O(shè)計(jì)中,可以通過兩種方法在不同的環(huán)境中復(fù)用已有的設(shè)計(jì)和實(shí)現(xiàn),即通過組合/ 聚合關(guān)系或通過繼承,但首先應(yīng)該考慮使用組合/聚合,組合/聚合可以使系統(tǒng)更加靈活,降低 類與類之間的耦合度,一個類的變化對其他類造成的影響相對較少;其次才考慮繼承,在使 用繼承時,需要嚴(yán)格遵循里氏代換原則,有效使用繼承會有助于對問題的理解,降低復(fù)雜 度,而濫用繼承反而會增加系統(tǒng)構(gòu)建和維護(hù)的難度以及系統(tǒng)的復(fù)雜度,因此需要慎重使用繼 承復(fù)用。

通過繼承來進(jìn)行復(fù)用的主要問題在于繼承復(fù)用會破壞系統(tǒng)的封裝性,因?yàn)槔^承會將基類的實(shí) 現(xiàn)細(xì)節(jié)暴露給子

類,由于基類的內(nèi)部細(xì)節(jié)通常對子類來說是可見的,所以這種復(fù)用又稱“白 箱”復(fù)用,如果基類發(fā)生改變,那么子類的實(shí)現(xiàn)也不得不發(fā)生改變;

由于組合或聚合關(guān)系可以將已有的對象(也可稱為成員對象)納入到新對象中,使之成為新對象的一部分,因此

新對象可以調(diào)用已有對象的功能,這樣做可以使得成員對象的內(nèi)部實(shí)現(xiàn) 細(xì)節(jié)對于新對象不可見。

實(shí)例:圖書管理系統(tǒng)中,如果數(shù)據(jù)在MySQL中,我們需要創(chuàng)建一個鏈接MySQL的工具類 MySQLUtil ,Dao只需要繼承該工具類即可操作數(shù)據(jù)庫,如果把數(shù)據(jù)庫換成Oracle,我們需要新建一個工具類 OracleUtil ,Dao需要修改繼承對象改為 OracleUtil ,這就違反了開閉原則。

原始設(shè)計(jì)方案

架構(gòu)中的設(shè)計(jì)模式

 

基于合成復(fù)用原則進(jìn)行重構(gòu):

架構(gòu)中的設(shè)計(jì)模式

 

我們把 OracleUtil 作為 MySQLUtil 的子類,BookDao中把 MySQLUtil 作為一個屬性組合進(jìn)來,每次需要變更數(shù)據(jù)庫鏈接的時候,只需要修改BookDao的依賴注入配置文件即可。這里符合里氏替換原則。

1.2.7 迪米特法則

定義:一個軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生相互作用。

講解:如果一個系統(tǒng)符合迪米特法則,那么當(dāng)其中某一個模塊發(fā)生修改時,就會盡量少地影響其他 模塊,擴(kuò)展會相對容易,這是對軟件實(shí)體之間通信的限制,迪米特法則要求限制軟件實(shí)體之 間通信的寬度和深度。迪米特法則可降低系統(tǒng)的耦合度,使類與類之間保持松散的耦合關(guān)系。

迪米特法則要求我們在設(shè)計(jì)系統(tǒng)時,應(yīng)該盡量減少對象之間的交互,如果兩個對象之間不必 彼此直接通信,那么這兩個對象就不應(yīng)當(dāng)發(fā)生任何直接的相互作用,如果其中的一個對象需 要調(diào)用另一個對象的某一個方法的話,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用。簡言之,就是通過引 入一個合理的第三者來降低現(xiàn)有對象之間的耦合度。

作用:降低系統(tǒng)的耦合度

實(shí)例:我們在做增刪改查的時候,如果直接用控制層調(diào)用Dao,業(yè)務(wù)處理的關(guān)系會比較亂,我們需要合理增加一個中間對象(業(yè)務(wù)層)來解決個問題。

原始設(shè)計(jì)方案

架構(gòu)中的設(shè)計(jì)模式

 

基于迪米特法則進(jìn)行重構(gòu)

架構(gòu)中的設(shè)計(jì)模式

 

1.3 設(shè)計(jì)模式分類

GOF中共提到了23種設(shè)計(jì)模式不是孤立存在的,很多模式之間存在一定的關(guān)聯(lián)關(guān)系,在大的系統(tǒng)開發(fā)中常常同時使用多種設(shè)計(jì)模式。這23種設(shè)計(jì)模式根據(jù)功能作用來劃分,可以劃分為3類:

(1)創(chuàng)建型模式:用于描述“怎樣創(chuàng)建對象”,它的主要特點(diǎn)是“將對象的創(chuàng)建與使用分離”,單例、原型、工廠方法、抽象工廠、建造者5種設(shè)計(jì)模式屬于創(chuàng)建型模式。

(2)結(jié)構(gòu)型模式:用于描述如何將類或?qū)ο蟀茨撤N布局組成更大的結(jié)構(gòu),代理、適配器、橋接、裝飾、外觀、享元、組合7種設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。

(3)行為型模式:用于描述類或?qū)ο笾g怎樣相互協(xié)作共同完成單個對象都無法單獨(dú)完成的任務(wù),以及怎樣分配職責(zé)。模板方法、策略、命令、職責(zé)鏈、狀態(tài)、觀察者、中介者、迭代器、訪問者、備忘錄、解釋器11種設(shè)計(jì)模式屬于行為型模式。

GOF的23種設(shè)計(jì)模式

1、單例(Singleton)模式:某個類只能生成一個實(shí)例,該類提供了一個全局訪問點(diǎn)供外部獲取該實(shí)例,其拓展是有限多

例模式。

2、原型(Prototype)模式:將一個對象作為原型,通過對其進(jìn)行復(fù)制而克隆出多個和原型類似的新實(shí)例。

3、工廠方法(Factory Method)模式:定義一個用于創(chuàng)建產(chǎn)品的接口,由子類決定生產(chǎn)什么產(chǎn)品。

4、抽象工廠(AbstractFactory)模式:提供一個創(chuàng)建產(chǎn)品族的接口,其每個子類可以生產(chǎn)一系列相關(guān)的產(chǎn)品。

5、建造者(Builder)模式:將一個復(fù)雜對象分解成多個相對簡單的部分,然后根據(jù)不同需要分別創(chuàng)建它們,最后構(gòu)建成該

復(fù)雜對象。

6、代理(Proxy)模式:為某對象提供一種代理以控制對該對象的訪問。即客戶端通過代理間接地訪問該對象,從而限制、

增強(qiáng)或修改該對象的一些特性。

7、適配器(Adapter)模式:將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的

那些類能一起工作。

8、橋接(Bridge)模式:將抽象與實(shí)現(xiàn)分離,使它們可以獨(dú)立變化。它是用組合關(guān)系代替繼承關(guān)系來實(shí)現(xiàn),從而降低了抽

象和實(shí)現(xiàn)這兩個可變維度的耦合度。

9、裝飾(Decorator)模式:動態(tài)的給對象增加一些職責(zé),即增加其額外的功能。

10、外觀(Facade)模式:為多個復(fù)雜的子系統(tǒng)提供一個一致的接口,使這些子系統(tǒng)更加容易被訪問。

11、享元(Flyweight)模式:運(yùn)用共享技術(shù)來有效地支持大量細(xì)粒度對象的復(fù)用。

12、組合(Composite)模式:將對象組合成樹狀層次結(jié)構(gòu),使用戶對單個對象和組合對象具有一致的訪問性。

13、模板方法(TemplateMethod)模式:定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以

不改變該算法結(jié)構(gòu)的情況下重定義該算法的某些特定步驟。

14、策略(Strategy)模式:定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的改變不會影響

使用算法的客戶。

15、命令(Command)模式:將一個請求封裝為一個對象,使發(fā)出請求的責(zé)任和執(zhí)行請求的責(zé)任分割開。

16、職責(zé)鏈(Chain of Responsibility)模式:把請求從鏈中的一個對象傳到下一個對象,直到請求被響應(yīng)為止。通

過這種方式去除對象之間的耦合。

17、狀態(tài)(State)模式:允許一個對象在其內(nèi)部狀態(tài)發(fā)生改變時改變其行為能力。

18、觀察者(Observer)模式:多個對象間存在一對多關(guān)系,當(dāng)一個對象發(fā)生改變時,把這種改變通知給其他多個對象,

從而影響其他對象的行為。

19、中介者(Mediator)模式:定義一個中介對象來簡化原有對象之間的交互關(guān)系,降低系統(tǒng)中對象間的耦合度,使原有

對象之間不必相互了解。

20、迭代器(Iterator)模式:提供一種方法來順序訪問聚合對象中的一系列數(shù)據(jù),而不暴露聚合對象的內(nèi)部表示。

21、訪問者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種訪問方式,即每個元素有多

個訪問者對象訪問。

22、備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取并保存一個對象的內(nèi)部狀態(tài),以便以后恢復(fù)它。

23、解釋器(Interpreter)模式:提供如何定義語言的放法,以及對語言句子的解釋方法,即解釋器。

2 設(shè)計(jì)模式常用案例

2.1 單例模式

單例模式(Singleton Pattern)是 JAVA 中最常見的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。

單例模式涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。該類還提供了一種訪問它唯一對象的方式,其他類可以直接訪問該方法獲取該對象實(shí)例,而不需要實(shí)例化該類的對象。

單例模式特點(diǎn):

1、單例類只能有一個實(shí)例。

2、單例類必須自己創(chuàng)建自己的唯一實(shí)例。

3、單例類必須給所有其他對象提供這一實(shí)例。

單例模式優(yōu)點(diǎn)

1、在內(nèi)存里只有一個實(shí)例,減少了內(nèi)存的開銷,尤其是頻繁的創(chuàng)建和銷毀實(shí)例。

2、避免對資源的多重占用(比如寫文件操作)

單例模式真實(shí)應(yīng)用場景

1、網(wǎng)站的計(jì)數(shù)器

2、應(yīng)用程序的日志應(yīng)用

3、數(shù)據(jù)庫連接池設(shè)計(jì)

4、多線程的線程池設(shè)計(jì)

2.1.1 單例模式-餓漢式

創(chuàng)建一個單例對象 SingleModel , SingleModel 類有它的私有構(gòu)造函數(shù)和本身的一個靜態(tài)實(shí)例。SingleModel 類提供了一個靜態(tài)方法,供外界獲取它的靜態(tài)實(shí)例。 DesignTest 我們的演示類使用 SingleModel 類來獲取 SingleModel 對象。

架構(gòu)中的設(shè)計(jì)模式

 

2.1.2 多種單例模式講解

單例模式有多種創(chuàng)建方式,剛才創(chuàng)建方式?jīng)]有特別的問題,但是程序啟動就需要創(chuàng)建對象,不管你用不用到對象,都會創(chuàng)建對象,都會消耗一定內(nèi)存。因此在單例的創(chuàng)建上出現(xiàn)了多種方式。

懶漢式:

懶漢式有這些特點(diǎn):

1、延遲加載創(chuàng)建,也就是用到對象的時候,才會創(chuàng)建

2、線程安全問題需要手動處理(不添加同步方法,線程不安全,添加了同步方法,效率低)

3、實(shí)現(xiàn)容易

案例如下: SingleModel1

架構(gòu)中的設(shè)計(jì)模式

 

如果在創(chuàng)建對象實(shí)例的方法上添加同步 synchronized ,但是這種方案效率低,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

雙重校驗(yàn)鎖: SingleModel2

這種方式采用雙鎖機(jī)制,安全且在多線程情況下能保持高性能。

架構(gòu)中的設(shè)計(jì)模式

 

3 Spring設(shè)計(jì)模式剖析

Spring是一個分層的JavaSE/EE full-stack(一站式) 輕量級開源框架,非常受企業(yè)歡迎,他解決了業(yè)務(wù)邏輯層和其他各層的松耦合問題,它將面向接口的編程思想貫穿整個系統(tǒng)應(yīng)用。在Spring源碼中擁有多個優(yōu)秀的設(shè)計(jì)模式使用場景,有非常高的學(xué)習(xí)價值。

3.1 觀察者模式

定義: 對象之間存在一對多或者一對一依賴,當(dāng)一個對象改變狀態(tài),依賴它的對象會收到通知并自動更新。

MQ其實(shí)就屬于一種觀察者模式,發(fā)布者發(fā)布信息,訂閱者獲取信息,訂閱了就能收到信息,沒訂閱就收不到信息。

優(yōu)點(diǎn):

1、觀察者和被觀察者是抽象耦合的。

2、建立一套觸發(fā)機(jī)制

缺點(diǎn):

1、如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費(fèi)很多時間。

2、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。

3.1.1 Spring觀察者模式

ApplicationContext 事件機(jī)制是觀察者設(shè)計(jì)模式的實(shí)現(xiàn),通過 ApplicationEvent 類和 ApplicationListener 接口,可以實(shí)現(xiàn) ApplicationContext 事件處理。

如果容器中有一個 ApplicationListener Bean ,每當(dāng) ApplicationContext 發(fā)布 ApplicationEvent 時,ApplicationListener Bean 將自動被觸發(fā)。這種事件機(jī)制都必須需要程序顯示的觸發(fā)。

其中spring有一些內(nèi)置的事件,當(dāng)完成某種操作時會發(fā)出某些事件動作。比如監(jiān)聽 ContextRefreshedEvent 事件,當(dāng)所有的bean都初始化完成并被成功裝載后會觸發(fā)該事件,實(shí)現(xiàn)ApplicationListener<ContextRefreshedEvent> 接口可以收到監(jiān)聽動作,然后可以寫自己的邏輯。同樣事件可以自定義、監(jiān)聽也可以自定義,完全根據(jù)自己的業(yè)務(wù)邏輯來處理。

對象說明:

1、ApplicationContext容器對象

2、ApplicationEvent事件對象(ContextRefreshedEvent容器刷新事件)

3、ApplicationListener事件監(jiān)聽對象

3.1.2 ApplicationContext事件監(jiān)聽

當(dāng)ApplicationContext內(nèi)的Bean對象初始化完成時,此時可以通過監(jiān)聽 ContextRefreshedEvent 得到通知!我們來

模擬一次。

創(chuàng)建監(jiān)聽對象:
ApplicationContextListener

架構(gòu)中的設(shè)計(jì)模式

 

將對象添加到容器中:

架構(gòu)中的設(shè)計(jì)模式

 

測試

架構(gòu)中的設(shè)計(jì)模式

 

此時會打印如下信息:

架構(gòu)中的設(shè)計(jì)模式

 

應(yīng)用場景:

程序啟動,初始化過程中,需要確保所有對象全部初始化完成,此時在從容器中獲取指定對象做相關(guān)初始化操作。例如:將省、市、區(qū)信息初始化到緩存中。

3.1.3 自定義監(jiān)聽事件

自定義監(jiān)聽事件可以監(jiān)聽容器變化,同時也能精確定位指定事件對象,我們編寫一個案例演示自定義監(jiān)聽事件實(shí)現(xiàn)流

程。

定義事件監(jiān)聽對象: MessageNotifier

架構(gòu)中的設(shè)計(jì)模式

 

定義事件對象: MessageEvent

架構(gòu)中的設(shè)計(jì)模式

 


架構(gòu)中的設(shè)計(jì)模式

 

將對象添加到容器中:

架構(gòu)中的設(shè)計(jì)模式

 

添加事件測試

架構(gòu)中的設(shè)計(jì)模式

 

測試打印結(jié)果如下:

架構(gòu)中的設(shè)計(jì)模式

 

3.2 代理模式

定義:給某對象提供一個代理對象,通過代理對象可以訪問該對象的功能。主要解決通過代理去訪問[不能直接訪問的對象,例如租房中介,你可以直接通過中介去了解房東的房源信息,此時中介就可以稱為代理。

優(yōu)點(diǎn)

1、職責(zé)清晰。

2、高擴(kuò)展性。

3、智能化。

缺點(diǎn)

1、由于在客戶端和真實(shí)主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。

2、實(shí)現(xiàn)代理模式需要額外的工作,有些代理模式的實(shí)現(xiàn)非常復(fù)雜。

代理實(shí)現(xiàn)方式:

基于接口的動態(tài)代理

提供者:JDK官方的Proxy類。

要求:被代理類最少實(shí)現(xiàn)一個接口。

基于子類的動態(tài)代理

提供者:第三方的CGLib,如果報asmxxxx異常,需要導(dǎo)入asm.jar。

要求:被代理類不能用final修飾的類(最終類)。

3.2.1 JDK動態(tài)代理

JDK動態(tài)代理要點(diǎn):

1、被代理的類必須實(shí)現(xiàn)一個接口

2、創(chuàng)建代理對象的時候,用JDK代理需要實(shí)現(xiàn)InvocationHandler

3、代理過程在invoke中實(shí)現(xiàn)

 

我們以王五租房為例,王五通過中介直接租用戶主房屋,中介在這里充當(dāng)代理角色,戶主充當(dāng)被代理角色。

創(chuàng)建房東接口對象: LandlordService

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建房東對象: Landlord

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建代理處理過程對象: QFangProxy

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建代理,并通過代理調(diào)用房東方法: JdkProxyTest

架構(gòu)中的設(shè)計(jì)模式

 

運(yùn)行結(jié)果如下:

架構(gòu)中的設(shè)計(jì)模式

 

3.2.2 CGLib動態(tài)代理

CGLib動態(tài)代理要點(diǎn):

1、代理過程可以實(shí)現(xiàn)MethodInterceptor(Callback)接口中的invoke來實(shí)現(xiàn)

2、通過Enhancer來創(chuàng)建代理對象

 

在上面的案例基礎(chǔ)上,把 QFangProxy 換成 SFangProxy ,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建測試類: CGLibProxyTest ,代碼如下

架構(gòu)中的設(shè)計(jì)模式

 

3.2.3 Spring AOP-動態(tài)代理

基于SpringAOP可以實(shí)現(xiàn)非常強(qiáng)大的功能,例如聲明式事務(wù)、基于AOP的日志管理、基于AOP的權(quán)限管理等功能,利用AOP可以將重復(fù)的代碼抽取,重復(fù)利用,節(jié)省開發(fā)時間,提升開發(fā)效率。Spring的AOP其實(shí)底層就是基于動態(tài)代理而來,并且支持JDK動態(tài)代理和CGLib動態(tài)代理,動態(tài)代理的集中體現(xiàn)在 DefaultAopProxyFactory 類中,我們來解析下 DefaultAopProxyFactory 類。

架構(gòu)中的設(shè)計(jì)模式

 

如果我們在spring的配置文件中不配置 <aop:config proxy-target-class="true"> ,此時默認(rèn)使用的將是JDK動態(tài)代理,如果配置了,則會使用CGLib動態(tài)代理。

JDK動態(tài)代理的創(chuàng)建 JdkDynamicAopProxy 如下:

架構(gòu)中的設(shè)計(jì)模式

 

CGLib動態(tài)代理的創(chuàng)建 ObjenesisCglibAopProxy 如下:

架構(gòu)中的設(shè)計(jì)模式

 

3.3 工廠設(shè)計(jì)模式

定義:工廠模式(Factory Pattern)是 Java 中最常用的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。它負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有實(shí)例的內(nèi)部邏輯。工廠類的創(chuàng)建產(chǎn)品類的方法可以被外界直接調(diào)用,創(chuàng)建所需的產(chǎn)品對象。

優(yōu)點(diǎn):

1、一個調(diào)用者想創(chuàng)建一個對象,只要知道其名稱就可以了。

2、屏蔽產(chǎn)品的具體實(shí)現(xiàn),調(diào)用者只關(guān)心產(chǎn)品的接口。

3、降低了耦合度

3.3.1 工廠模式案例

架構(gòu)中的設(shè)計(jì)模式

 

我們來做這么一個案例,創(chuàng)建一個接口Product和Product的實(shí)現(xiàn)類Mobile以及Car,再定義一個具體的工廠對象ProcutFactory,并通過ProductFactory來獲取指定的Product。

Product接口:

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建接口實(shí)現(xiàn)類Mobile:

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建接口實(shí)現(xiàn)類Car:

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建工廠ProductFactory,根據(jù)參數(shù)創(chuàng)建指定產(chǎn)品對象:

架構(gòu)中的設(shè)計(jì)模式

 

使用工廠ProductFactory創(chuàng)建指定對象:

架構(gòu)中的設(shè)計(jì)模式

 

運(yùn)行結(jié)果如下

架構(gòu)中的設(shè)計(jì)模式

 

3.3.2 BeanFactory工廠模式

Spring內(nèi)部源碼也有工廠模式的實(shí)現(xiàn),并且解決了上面我們提到的工廠模式的缺陷問題。

Spring中的BeanFactory就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個唯一的標(biāo)識來獲得Bean對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個要根據(jù)具體情況來定

BeanFactory源碼:

架構(gòu)中的設(shè)計(jì)模式

 

在BeanFactory接口中,有多個getBean方法,該方法其實(shí)就是典型的工廠設(shè)計(jì)模式特征,在接口中定義了創(chuàng)建對象的方法,而對象如何創(chuàng)建其實(shí)在接口的實(shí)現(xiàn)類中實(shí)現(xiàn)
DefaultListableBeanFactory 。

我們用Spring的工廠對象BeanFactory來解決上面工廠模式案例所帶來的問題

案例:

架構(gòu)中的設(shè)計(jì)模式

 

測試:

架構(gòu)中的設(shè)計(jì)模式

 

結(jié)果:

架構(gòu)中的設(shè)計(jì)模式

 

使用Spring的BeanFactory,以后要新增一個產(chǎn)品,只需要創(chuàng)建產(chǎn)品對應(yīng)的xml配置即可,而不需要像ProductFactory 那樣硬編碼存在。

3.4 適配器模式

定義

將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作

優(yōu)點(diǎn):

1、可以讓任何兩個沒有關(guān)聯(lián)的類一起運(yùn)行。

2、提高了類的復(fù)用。

3、靈活性好

缺點(diǎn):

過多地使用適配器,會讓系統(tǒng)非常零亂,不易整體進(jìn)行把握。比如,明明看到調(diào)用的是 A 接口,其實(shí)內(nèi)部被適配成了 B 接口的實(shí)現(xiàn),一個系統(tǒng)如果太多出現(xiàn)這種情況,無異于一場災(zāi)難。

3.4.1 Spring Aop適配器+代理模式案例

Spring架構(gòu)中涉及了很多設(shè)計(jì)模式,本文來介紹下Spring中在AOP實(shí)現(xiàn)時Adapter模式的使用。AOP本質(zhì)上是Java動態(tài)代理模式的實(shí)現(xiàn)和適配器模式的使用。

我們基于Spring的前置通知來實(shí)現(xiàn)一個打卡案例,再基于前置通知講解前置適配模式。

創(chuàng)建打卡接口: PunchCard 定義打卡方法,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

定義打卡實(shí)現(xiàn): PunchCardImpl 實(shí)現(xiàn)打卡操作,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

前置通知創(chuàng)建: PunchCardBefore 實(shí)現(xiàn)在打卡之前識別用戶身份,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

spring.xml配置前置通知:

架構(gòu)中的設(shè)計(jì)模式

 

測試效果如下:

架構(gòu)中的設(shè)計(jì)模式

 

3.4.2 Spring AOP適配器體系

架構(gòu)中的設(shè)計(jì)模式

 

前置通知其實(shí)就是適配器模式之一,剛才我們編寫的前置通知實(shí)現(xiàn)了接口 MethodBeforeAdvice 。Spring容器將每個具體的advice封裝成對應(yīng)的攔截器,返回給容器,這里對advice轉(zhuǎn)換就需要用到適配器模式。我們來分析下適配器的實(shí)現(xiàn):

如下代碼實(shí)現(xiàn)了接口 BeforeAdvice ,而 BeforeAdvice 繼承了 Advice 接口,在適配器接口 AdvisorAdapter 里面定義了方法攔截。

架構(gòu)中的設(shè)計(jì)模式

 

AdvisorAdapter :定義了2個方法,分別是判斷通知類型是否匹配,如果匹配就會獲取對應(yīng)的方法攔截。

架構(gòu)中的設(shè)計(jì)模式

 

MethodBeforeAdviceAdapter :實(shí)現(xiàn)了 AdvisorAdapter ,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

剛才我們在spring.xml中配置了代理類,代理類通過
DefaultAdvisorAdapterRegistry類來注冊相應(yīng)的適配器,我們可以在

架構(gòu)中的設(shè)計(jì)模式

 

4 架構(gòu)中的設(shè)計(jì)模式

架構(gòu)中的設(shè)計(jì)模式

 

我們結(jié)合上面所學(xué)的設(shè)計(jì)模式,開發(fā)一款框架,該框架具備Spring的功能和SpringMVC功能,這里我們會提供部分工具,直接供大家使用。開發(fā)的框架流程圖如上,整體基于Servlet實(shí)現(xiàn)。

準(zhǔn)備工作:

搭建一個工程,引入相關(guān)依賴包,以及工具包,pom.xml代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

擴(kuò)展

架構(gòu)中的設(shè)計(jì)模式

 

在 AccountController 類上有幾個@RequestMapping注解,這個注解是我們自定義的,這個注解上有對應(yīng)的值,我們可以通過 ParseAnnotation 工具類解析該注解,并將解析值存儲到Map中,修改 BaseInit ,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

解析這個注解的作用,可以通過用于請求的路徑來判斷該路徑歸哪個對象處理;

此時不要忘了配置web.xml

架構(gòu)中的設(shè)計(jì)模式

 

4.1 自定義框架-適配器視圖渲染

4.1.1 流程分析

架構(gòu)中的設(shè)計(jì)模式

 

在我們做視圖解析的時候,有不同的解析方式,比如有直接輸入json數(shù)據(jù)、重定向、轉(zhuǎn)發(fā)、輸出文件流等多種方式。通常采用哪種解析方式由執(zhí)行方法的返回值決定,例如返回一個字符串,我們可以把返回的字符串當(dāng)做響應(yīng)的頁面,這時候可以采用轉(zhuǎn)發(fā)的方式,如果返回的是一個javabean,這時候我們可以采用輸出json字符串的方式解析。像這一塊的實(shí)現(xiàn),我們可以采用適配器模式實(shí)現(xiàn)。

實(shí)現(xiàn)步驟如下:

1、定義一個視圖解析接口ViewHandler,提供2種解析方式,分別為json輸出和forward

2、為接口實(shí)現(xiàn)每種解析方式。分別創(chuàng)建PrintViewHandler和ForwardViewHandler

3、創(chuàng)建一個視圖渲染接口View,View中提供渲染方法render

4、創(chuàng)建View的渲染實(shí)現(xiàn)ViewAdapter,通過提供的相應(yīng)結(jié)果,來創(chuàng)建對應(yīng)的試圖解析器,并調(diào)用解析方式

 

4.1.2 適配器模式實(shí)現(xiàn)視圖解析

1)創(chuàng)建視圖解析器接口

架構(gòu)中的設(shè)計(jì)模式

 

2)創(chuàng)建json解析和轉(zhuǎn)發(fā)

JSON解析對象: PrintViewHandler

架構(gòu)中的設(shè)計(jì)模式

 

轉(zhuǎn)發(fā)解析對象: ForwardViewHandler

架構(gòu)中的設(shè)計(jì)模式

 

3)視圖渲染接口和實(shí)現(xiàn)

視圖渲染接口: View

架構(gòu)中的設(shè)計(jì)模式

 

視圖渲染實(shí)現(xiàn): ViewAdapter

架構(gòu)中的設(shè)計(jì)模式

 

我們創(chuàng)建一個類 DispacherServlet ,繼承BaseInit,同時在 web.xml 中把 BaseInit 換成 DispacherServlet ,并重寫 service 方法,實(shí)現(xiàn)攔截所有用戶請求,在 service 中使用執(zhí)行反射調(diào)用,然后調(diào)用剛才寫好的適配器查找對應(yīng)的渲染方式執(zhí)行渲染,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 


架構(gòu)中的設(shè)計(jì)模式

 

我們來測試一下,請求 <
http://localhost:18081/account/info>

架構(gòu)中的設(shè)計(jì)模式

 

請求 <
http://localhost:18081/account/one>

架構(gòu)中的設(shè)計(jì)模式

 

4.2 自定義框架-觀察者模式

上面雖然已經(jīng)實(shí)現(xiàn)了MVC模型對應(yīng)功能,但是每次調(diào)用對象都是創(chuàng)建了新對象,我們可以對這里進(jìn)行優(yōu)化,讓每次調(diào)用的對象是單例對象,這時候我們就需要初始化的時候把對象創(chuàng)建好了,但是對象和對象之間又存在依賴關(guān)系,我們可以在配置文件中配置這種關(guān)系。這里我們可以使用觀察者模式和單例模式。

4.2.1 流程分析

架構(gòu)中的設(shè)計(jì)模式

 

我們編寫一個類似Spring的MVC框架,這里采用觀察者模式實(shí)現(xiàn)監(jiān)聽文件加載,實(shí)現(xiàn)步驟如下:

1、編寫B(tài)aseInit類,并繼承HttpServlet

2、重寫HttpServlet中的init(ServletConfig config)方法

3、編寫一個抽象類ParseFile,在該類中編寫監(jiān)聽的對象baseInit,同時編寫一個通知方法load(InputStream is)

4、編寫ParseXml繼承ParseFile,實(shí)現(xiàn)load(InputStream is)方法,當(dāng)BaseInit中的init方法加載到變更文件

時,該方法將得到變更通知。

4.2.2 監(jiān)聽文件加載并解析文件

要創(chuàng)建實(shí)例的對象信息以及依賴關(guān)系信息,我們可以配置到配置文件中,配置如下:

架構(gòu)中的設(shè)計(jì)模式

 

講解

1、配置文件中一個bean表示要創(chuàng)建一個對象的實(shí)例

2、id表示創(chuàng)建對象后的唯一id

3、class表示要創(chuàng)建哪個對象的全限定名

4、property表示給創(chuàng)建的對象指定屬性賦值

5、property.name表示給指定對象的指定屬性賦值

6、property.ref表示將指定對象賦值給property.name指定的屬性

 

我們需要加載哪個配置文件進(jìn)行解析,需要在web.xml中配置,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

當(dāng) BaseInit 獲取到要解析的文件內(nèi)容時,通知 ParseFile 解析對應(yīng)的配置文件,在這里我們可以使用觀察者模式,讓當(dāng)前 BaseInit 和 ParseFile 關(guān)聯(lián),這里 ParseFile 是一個抽象對象,給它編寫一個實(shí)現(xiàn) ParseXml ,以后也許還會寫 ParseYaml 等。這里用到的設(shè)計(jì)模式是觀察者模式,觀察到要解析配置文件,通知指定對象進(jìn)行解析。

創(chuàng)建 ParseFile ,代碼如下

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建 ParseXml ,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

修改 BaseInit ,代碼如下

架構(gòu)中的設(shè)計(jì)模式

 

4.3 自定義框架-工廠模式

此時我們通知 ParseXml 加載需要解析的配置文件,這時候我們需要創(chuàng)建一個工廠對象來實(shí)現(xiàn)對象創(chuàng)建和對象獲取,這塊我們可以采用工廠模式實(shí)現(xiàn)

4.3.1 流程分析

架構(gòu)中的設(shè)計(jì)模式

 

工廠的流程如上圖,工廠需要先解析 spring.xml ,將解析的實(shí)體bean存儲起來,用戶每次獲取對應(yīng)的實(shí)例時,可以通過請求uri獲取,也可以通過 spring.xml 中唯一的id獲取

 

4.3.2 工廠模式實(shí)現(xiàn)獲取對象實(shí)例

1)創(chuàng)建工廠接口

創(chuàng)建工廠接口 BeanFactory ,因?yàn)橐院罂赡軙凶⒔鈱?shí)現(xiàn)方式,所以這里為工廠創(chuàng)建一個接口,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

2)工廠實(shí)現(xiàn)

創(chuàng)建工廠實(shí)現(xiàn)類 XmlBeanFactory ,同時實(shí)現(xiàn)根據(jù)url和根據(jù)id獲取對應(yīng)實(shí)例方法,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 


架構(gòu)中的設(shè)計(jì)模式

 

工廠測試:

架構(gòu)中的設(shè)計(jì)模式

 

3)重寫service反射調(diào)用

架構(gòu)中的設(shè)計(jì)模式

 

為了每次能使用單例對象處理用戶請求從而更節(jié)省資源,我們需要根據(jù)用戶的uri找到對應(yīng)的實(shí)例。

我們可以按照這個步驟實(shí)現(xiàn)通過uri找到對應(yīng)的實(shí)例:

1、循環(huán)所有methods,獲取每個method對應(yīng)的uri和method對應(yīng)的Class

2、匹配beans中實(shí)例的Class和method的Class相同的對象,獲取對應(yīng)的key(也就是id)

3、把uri和id重新組合成一個新的Map,叫urlIdMaps

4、用戶每次請求的時候,直接通過uri從urlIdMaps中獲取對應(yīng)實(shí)例

 

這個操作,該
ParseAnnotation.parseUrlMappingInstance(methods,beans) 方法已經(jīng)幫我們實(shí)現(xiàn)了,所以我們只需要把實(shí)現(xiàn)的整體流程搬到 XmlBeanFactory 中就可以了。

 

修改 XmlBeanFactory ,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 


架構(gòu)中的設(shè)計(jì)模式

 

4.4 自定義框架-代理模式增強(qiáng)

4.4.1 流程分析

架構(gòu)中的設(shè)計(jì)模式

 

我們模擬Spring的聲明式事務(wù)控制,在業(yè)務(wù)層進(jìn)行增強(qiáng),我們可以按照如下步驟實(shí)現(xiàn):

1、創(chuàng)建增強(qiáng)類TransactionManager,在里面編寫一個增強(qiáng)方法begin

2、創(chuàng)建一個BeanProxy對象,用于給指定包下的對象創(chuàng)建代理

3、每次ParseXml加載解析之后,調(diào)用BeanProxy給指定包下的對象創(chuàng)建代理

4.4.2 業(yè)務(wù)層代理模式增強(qiáng)

1)指定增強(qiáng)位置

我們首先在spring.xml配置文件中配置一下增強(qiáng)的位置,before表示前置增強(qiáng), package 表示指定包下的對象進(jìn)行前

置增強(qiáng), ref 表示指定增強(qiáng)的類, method 表示增強(qiáng)的類中指定的方法。

架構(gòu)中的設(shè)計(jì)模式

 

我們需要創(chuàng)建一個增強(qiáng)類 TransactionManager :

架構(gòu)中的設(shè)計(jì)模式

 

2)增強(qiáng)實(shí)現(xiàn)

我們接著需要對增強(qiáng)進(jìn)行實(shí)現(xiàn),我們可以按照如下步驟實(shí)現(xiàn):

1、解析xml,需要獲取增強(qiáng)的包,實(shí)現(xiàn)增強(qiáng)的類的方法

2、獲取所有解析的實(shí)例,并判斷每個實(shí)例是否是該包下的對象

3、如果是該包下的對象,給它創(chuàng)建代理,執(zhí)行增強(qiáng)

4、將當(dāng)前對象替換成代理對象

5、將當(dāng)前對象下的引用屬性替換成代理

 

我們創(chuàng)建該代理增強(qiáng)類 BeforeProxyBean ,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 

創(chuàng)建代理類 ProxyBeanFactory ,代碼如下:

架構(gòu)中的設(shè)計(jì)模式

 


架構(gòu)中的設(shè)計(jì)模式

 

在工廠 XmlBeanFactory 實(shí)現(xiàn)調(diào)用即可:

架構(gòu)中的設(shè)計(jì)模式

 

運(yùn)行測試結(jié)果如下:

架構(gòu)中的設(shè)計(jì)模式

 

 

若有收獲,就點(diǎn)個贊吧

分享到:
標(biāo)簽:架構(gòu)
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定