Java - 核心設計模式和原則

(本次設計模式摘自大話設計模式書本知識,用 C# 講解的)

簡單工廠模式

  • 簡單工廠模式是屬於創建型模式,簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解爲是不同工廠模式的一個特殊實現。
    在這裏插入圖片描述

策略模式

  • 前言:面向對象的編程,並不是類越多越好,類的劃分是爲了封裝,但分類的基礎是抽象,具有相同屬性和功能的對象的抽象集合纔是類。
  • 策略模式: 它定義了算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化,不會影響到使用算法的客戶。
  • 策略模式結構圖:
    在這裏插入圖片描述
    對於這個 Context 存在的意義:
    在這裏插入圖片描述
    客戶端可通過 Context 來實現想要實現的方法。
  • 策略模式解析:
  1. 策略模式是一種定義一系列算法的方法,從概念上來看,所有這些算法完成的都是相同的工作,只是實現不同,它可以以相同的方式調用所有的算法,減少各種算法類使用之間的耦合。
  2. 該 Strategy 類層次爲 Context 定義了一系列的可供重用的算法或行爲,繼承有助於析取出這些算法的公共功能。
  3. 策略模式的優點是簡化了單元測試,因爲每個算法都有自己的類,可以通過自己的接口單獨測試。
  4. 策略模式就是用來封裝算法的,但在實踐中,我們發現可以用它來封裝幾乎任何類型的規則,只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。

單一職責原則

  • 單一職責原則就一個類而言,應該僅有一個引起它變化的原因。
  • **注意:**如果一個類承擔的指責過多,就等於把這些職責耦合在一起,一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力,這種耦合會導致脆弱的設計,當變化發生時,設計會遭受意想不到的破壞。
  • 軟件設計真正要做的許多內容,就是發現職責並把這些職責相互分離。

開放-封閉原則

  • 開放封閉原則是說軟件實體(類,模塊,函數等等)應該可以擴展,但是不可以修改。
  • 及:對於擴展是開放的,對於更改是封閉的。
  • 無論模塊多麼的“封閉”,都會存在一些無法對之封閉的變化。既然不可能完全封閉,設計人員必須對於他設計的模塊應該對哪種變化封閉做出選擇。他必須先猜測出最優可能發生的變化種類,然後構造抽象來隔離這些變化。
  • 當我們最初編寫代碼時,假設變化不會發生。當變化發生時。我們就創建抽象來隔離以後發生的同類變化。
  • 面對需求,對程序的改動是通過增加新代碼進行的,而不是更改現有的代碼。
    在這裏插入圖片描述
  • 開放封閉原則是面向對象設計的核心所在。遵循這個原則可以帶來面向對象技術所聲稱的巨大好處,也就是可維護、可擴展、可複用、靈活性好。開發人員應該僅對程序中呈現出頻繁變化的那些部分做出抽象,然而,對於應用程序中的每個部分都刻意地進行抽象同樣不是一個好主意。拒絕不成熟的抽象和抽象本身一樣重要。切記,切記。

依賴倒置原則

  • 依賴倒轉原則
    A.高層模塊不應該依賴低層模塊。兩個都應該依賴抽象
    B.抽象不應該依賴細節。細節應該依賴抽象。
  • 說白了,就是針對接口編程,不要對實現編程。

里氏替換原則

  • 里氏代換原則(LSP):子類型必須能夠替換掉它們的父類型。
  • 也正因爲有了這個原則,使得繼承複用成爲了可能,只有當子類可以替換掉父類,軟件單位的功能不受到影響時,父類才能真正被複用,而子類也能夠在父類的基礎上增加新的行爲。
  • 依賴倒轉其實就是誰也不要依靠誰,除了約定的接口,大家都可以靈活自如。
    在這裏插入圖片描述

迪米特法則

  • “迪米特法則(LoD)’ 也叫最少知識原則。
  • 迪米特法則(LoD),如果兩個類不必彼此直接通信,那麼這兩個類就不應當發生直接的相互作用。如果其中一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用。
  • 迪米特法則首先強調的前提是在類的結構設計上,每一個類都應當儘量降低成員的訪問權限。
  • 迪米特法則其根本思想,是強調了類之間的松耦合
  • 類之間的耦合越弱,越有利於複用,一個處在弱耦合的類被修改,不會對有關係的類造成波及。

裝飾模式

  • 裝飾模式(Decorator), 動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更爲靈活。

  • 裝飾模式結構圖:
    在這裏插入圖片描述
    Component是定義一個對象接口,可以給這些對象動態地添加職責。ConcreteComponent 是定義了一個具體的對象,也可以給這個對象添加一些職責。Decorator, 裝飾抽象類,繼承了Component,從外類來擴展Component類的功能,但對於Component來說,是無需知道Decorator的存在的。至於ConcreteDecorator就是具體的裝飾對象,起到給Component添加職責的功能。”

  • 總結:裝飾模式卻提供了一個非常好的解決方案,它把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它所要裝飾的對象,因此,當需要執行特殊行爲時,客戶代碼就可以在運行時根據需要有選擇地、按順序地使用裝飾功能包裝對象了。這樣做更大的好處就是有效地把類的核心職責和裝飾功能區分開了。而且可以去除相關類中重複的裝飾邏輯。


代理模式

  • 代理模式(Proxy),爲其他對象提供一種代理以控制對這個對象的訪問
  • 代理模式結構圖:
    在這裏插入圖片描述
  • 代理模式應用
  1. 遠程代理,也就是爲一個對象在不同的地址空間提供局部代表。這樣可以隱藏一個對象存在於不同地址空間的事實。
  2. 虛擬代理,是根據需要創建開銷很大的對象。通過它來存放實例化需要很長時間的真實對象。這樣就可以達到性能的最優化。
  3. 安全代理,用來控制真實對象訪問時的權限。
  4. 智能指引,是指當調用真實的對象時,代理處理另外一些事。

觀察者模式

  • 觀察者模式又叫做發佈-訂閱(Publish/Subscribe) 模式。
  • 觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,
    會通知所有觀察者對象,使它們能夠自動更新自己。
  • 簡單樣例圖:
    在這裏插入圖片描述
  • 觀察者模式結構圖:
    在這裏插入圖片描述
    解析:
  1. Subject類,可翻譯爲主題或抽象通知者, 一般用一個抽象類或者一個接口實現。它把所有對觀察者對象的引用保存在一一個聚集裏,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。
  2. Observer類,抽象觀察者,爲所有的具體觀察者定義一個接口,在得到主題的通知時更新自己。這個接口叫做更新接口。抽象觀察者一般用一個抽象類或者一個接口實現。更新接口通常包含一個Update()方法,這個方法叫做更新方法。
  3. ConcreteSubject類,叫做具體主題或具體通知者,將有關狀態存入具體現察者對象;.在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個具體子類實現。
  4. ConcreteObserver類,具體觀察者,實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。具體觀察者角色可以保存一個指向具體主題對象的引用。具體觀察者角色通常用一個具體子類實現。
  • 觀察者模式特點
  1. 將一個系統分割成一系列相互協作的類有一個很不好的副作用,那就是需要維護相關對象間的一致性。 我們不希望爲了維持一致性而使各類緊密耦合, 這樣會給維護、擴展和重用都帶來不便。
  2. 當一個對象的改變需要同時改變其他對象的時候 ,而且它不知道具體有多少對象有待改變,應該考慮使用觀察者模式
  3. 當一個抽象模型有兩個方面,其中一方面依賴於另一方面,這時用觀察者模式可以將這兩者封裝在獨立的對象中使它們各自獨立地改變和複用。總的來講,觀察者模式所做的工作其實就是在解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使得各自的變化都不會影響另一邊的變化。
  • 觀察者模式的不足
    儘管已經用了依賴倒轉原則,但是‘抽象通知者’還是依賴‘抽象 觀察者’,也就是說,萬一沒有 了抽象觀察者這樣的接口,我這通知的功能就完不成了。另外就是每個具體觀察者,它不一定是‘更新’的方法要調用呀,就像我希望的是‘工具箱’是隱藏,‘自動窗口’
    是打開,這根本就不是同名的方法。這應該就是不足的地方吧。”

解決方案 -->> 事件委託實現

  • “抽象通知者”由於不希望依賴“抽象觀察者”,所以“增加”和“減少”的方法也就沒有必要了(抽象觀察者已經不存在了)。
  • 事件委託說明
  1. 委託就是一種引用方法的類型。一旦爲委託分配了方法,委託將與該方法具有完全相同的行爲。委託方法的使用可以像其他任何方法一樣,具有參數和返回值。委託可以看作是對函數的抽象,是函數的‘類’,委託的實例將代表一個具體的函數。
  2. 一個委託可以搭載多個方法,所有方法被依次喚起。更重要的是,它可以使得委託對象所搭載的方法並不需要屬於同一個類。
  3. 委託也是有前提的,那就是委託對象所搭載的所有方法必須具有相同的原形和形式,也就是擁有相同的參數列表和返回值類型。

抽象工廠模式

前言:

  • 用了工廠方法模式的數據訪問程序
    在這裏插入圖片描述
  • 用了抽象工廠模式的數據訪問程序
    在這裏插入圖片描述
  • 抽象工廠模式(Abstract Factory),提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
  • 抽象工廠模式結構圖:
    在這裏插入圖片描述
  • “AbstractProductA 和 AbstractProductB是兩個抽象產品,之所以爲抽象,是因爲它們都有可能有兩種不同的實現,就剛纔的例子來說就是User和Department,而 ProductA1、ProductA2 和ProductB1,ProductB2就是對兩個抽象產品的具體分類的實現,比如ProductA1可以理解爲是SqlserverUser,而ProductB I是AccessUser。
  • 這麼說,IFactory 是一個抽象工廠接口,它裏面應該包含所有的產品創建的抽象方法。而ConcreteFactory1和ConcreteFactory2就是具體的工廠了。就像SqlserverFactory和AccessFactory一樣。通常是在運行時刻再創建一個 ConcreteFactory類的實例,這個具體的工廠再創建具有特定實現的產品對象,也就是說,爲創建不同的產品對象,客戶端應使用不同的具體工廠。”
  • 抽象工廠模式的優點與缺點
  1. 最大的好處便是易於交換產品系列,由於具體工廠類,例如 IFactory factory = new AccessFactory(),在一個應用中只需要在初始化的時候出現一次,這就使得改變一個應用的具體工廠變得非常容易,它只需要改變具體工廠即可使用不同的產品配置。
  2. 我們的設計不能去防止需求的更改,那麼我們的理想便是讓改動變得最小,現在如果你要更改數據庫訪問,我們只需要更改具體工廠就可以做到。第二大好處是,它讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操縱實例,產品的具體類名也被具體工廠的實現分離,不會出現在客戶代碼中。
  3. 是個模式都是會有缺點的,都有不適用的時候,要辨證地看待問題哦。抽象工廠模式可以很方便地切換兩個數據庫訪問的代碼,但是如果你的需求來自增加功能,比如我們現在要增加項目表Project,那就至少要增加三個類,IProject、 SqIserverProject、 AccessProject, 還需要更改 IFactory、SqlserverFactory和 AccessFactory纔可以完全實現。啊,要改三個類,這太糟糕了。
  • 編程是門藝術,大批量的改動,顯然是非常醜陋的做法。

用簡單工廠來改進抽象工廠

  • 結構圖:
    在這裏插入圖片描述
  • 拋棄了IFactory、 SqlserverFactory 和AccessFactory三個工廠類,取而代之的是DataAccess 類,由於事先設置了db的值(Sqlserver或Access),所以簡單工廠的方法都不需要輸入參數,這樣在客戶端就只需要 DatAccess.CreateUser和DataAccess.CreateDepartment()來生成具體的數據庫訪問類實例,客戶端沒有出現任何一個SQL Server或Access 的字樣,達到了解耦的目的。

用反射+抽象工廠的數據訪問程序

  • 結構圖:
    在這裏插入圖片描述

用反射+配置文件實現數據訪問程序

  • 添加一個 config 文件:
    在這裏插入圖片描述
  • 現在我們應用了反射+抽象工廠模式解決了數據庫訪問時的可維護、可擴展的問題。從這個角度上說,所有在用簡單工廠的地方,都可以考慮用反射技術來去除 switch 或 if,解除分支判斷帶來的耦合

適配器模式

  • 適配器模式(Adapter),將一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由於接口不兼容而不能一
    起工作的那些類可以一起工作。
  • 在軟件開發中,也就是系統的數據和行爲都正確,但接口不符時,我們應該考慮用適配器,目的是使控制範圍之外的一個原有對象與某個接口匹配。適配器模式主要應用於希望複用一些現存的類,但是接口又與複用環境要求不一致的情況,比如在需要對早期代碼複用一些功能等應用上很有實際價值。
  • 適配器模式(Adapter)結構圖:
    在這裏插入圖片描述
何時使用適配器模式?
  • 兩個類所做的事情相同或相似,但是具有不同的接口時要使用它。客戶代碼可以統一調用同一接口就行了,這樣可以更簡單、更直接、更緊湊。
  • 並且,要在雙方都不太容易修改的時候再使用適配器模式適配。
  • 樣例結構圖:
    在這裏插入圖片描述
    儘管外籍球員曾經是不太懂英文,儘管教練和球員也不會學中文,但因爲有了翻譯者, 團隊溝通合作成爲了可能。
  • 需要注意的是
    如果能事先預防接口不同的問題,不匹配問題就不會發生;在有小的接口不統一問題發生時,及時重構,問題不至於擴大;只有碰到無法改變原有設計和代碼的情況時,才考慮適配。事後控制不如事中控制,事中控制不如事前控制

單例模式

  • 單例模式(Singleton),保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
  • 通常我們可以讓一個全局變量使得一個對象被訪問,但它不能防止你實例化多個對象。一個最好的辦法就是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法。
  • 單例模式(Singleton)結構圖:
    在這裏插入圖片描述
  • 好處還有:比如單例模式因爲Singleton 類封裝它的唯一實例, 這樣它可以嚴格地控制客戶怎樣訪問它以及何時訪問它。簡單地說就是對唯一實例的受控訪問。
  • 多線程訪問單例的時候,可以用 lock 解決可能造成創建多個實例的問題。或者,利用靜態初始化方法:
    在這裏插入圖片描述
  • 由於這種靜態初始化的方式是在自己被加載時就將自己實例化,所以被形象地稱之爲餓漢式單例類,原先的單例模式處理方式是要在第一次被引用時, 纔會將自己實例化,所以就被稱爲懶漢式單例類
  • 單例模式的實現方式還有很多很多種, 餓漢式單例和懶漢式單例只是兩種比較主流和常用的單例模式方法。,懶漢式的單例模式是線程可能會有安全隱患。

……未完待續,持續更新

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