1. 程式人生 > >看透設計模式-建立型模式

看透設計模式-建立型模式

總結下自己關於設計模式的一些思考。(我終於看穿了愛情,不過就是一圈圈圈圈菸圈圈圈圈 用來解悶消遣。。)

 

設計模式是對一些 常用的類的行為規範的一個最佳實踐的提取。

設計模式不是架構模式,設計模式關注的是程式碼的可讀性,拓展性。而架構關注的是 效能、穩定性、可用性

開閉原則,並不是說,我們能完全的 能夠通過新增類、介面來拓展而做到不修改已有的任何元件, 而是說,把修改從某個區域性 轉移到 外部。

設計模式並不是說只有23種,GoF的那本書《Design Patterns》出版到現在都過去了幾十年了,當時的情況,現在已經發生了天翻地覆。很多新的場景、用法 可能都是GoF沒有考慮到的。所以說,我認為,如果把各種各樣的,常用的不常用的設計模式統計起來,恐怕230、2300 種都不止。 但是,這23種是最最常用的, 也是最 久負盛名的。

其實每一個複雜的類,和其相關的類之間,都會存在一定的模式,有已知的,也有未命名的,有好的,也有壞的,只不過是我們是否發現了而言。

當然,不同的行業,常用到的模式又不一樣。

我們看到了,這些模式,其實都很簡單,實際情況,可能複雜得多。

 

下面,我們從 主要功能、是否能簡化、適用範圍、示例 等方面進行探討。

 

單例模式:

 

適用於,全域性唯一,只有一個例項物件。

但是正是由於,它要求全域性唯一, 那麼可能出現多執行緒同時訪問,需要防範多執行緒問題。

如何對單例模式進行改造,使得系統中某個類的物件可以存在有限多個,例如兩例或三例?【注:改造之後的類可稱之為多例類。】 

簡化,無法再繼續簡化了,因為只有一個類啊! 方法也簡單,沒有複雜的呼叫。簡化反而可能引起錯誤。

 

簡單工廠模式:

(下面的factoryMethod應該為靜態方法)

 

 簡化:去掉了工廠類,抽象產品同時兼具工廠功能 (下面的factoryMethod可以為靜態方法):

 
拓展性:新增一個具體產品實現抽象產品類即可。簡單,但是新增產品的時候,必須修改工廠,從而破壞了開閉原則。

討論:Product可以為抽象類嗎? 當然可以的,Product 可以是任意的東西,pojo,controller,dao,service,複雜的物件。 可以有很多的各種屬性,也可以各種方法。所以,我們不限制它必須是介面。

 

工廠方法模式:

 

避免了新增產品的時候 修改工廠,新增一個產品類,則需要同時增加一個工廠類, 方便拓展(新增類,不是什麼缺點)。

拓展範圍:有一系列有統一規範的產品(繼承、實現了Product介面)

 討論:1 Product、Factory可以為介面類嗎? 應該可以的,主要看是否有公共的內容需要提取到上層,如果是,那麼則需要為 抽象類,否則,就無所謂了。

2 工廠方法模式中的工廠方法能否為靜態方法?為什麼? 不能,因為工廠方法實際上是抽象方法,要求由子類來動態地實現,而動態性與static所宣告的靜態性相沖突

 

抽象工廠模式:

概念說明:

產品等級:有繼承關係的一些列產品,通常不是一個通常生產的,AbstractProductA的子類:ConcreteProductA1、A2 就是產品等級。產品等級可以理解為  不同工廠的 brand

產品族:    同一個工廠生產的不同產品,上圖的AbstractProductA、AbstractProductB就是兩個產品族。顯然,產品族是一個更加 寬泛的概念。

當然實際情況可能更加複雜,比如有的工廠可以生產AbstractProductA,同時自己工廠的AbstractProductA下面有很多細分的產品,比如AbstractProductA1,AbstractProductA2,有很多型號,但都是屬於一個產品線。

新增具體工廠涉及到拓展已有的 產品等級(不會有新的產品族出現,這個有點難理解,可以這麼說, 這個工廠沒有增加新 種類的產品,而只是新增了不同brand的產品。),新增工廠方便,新增3個類即可; 但是新增產品族呢?  則破壞了開閉原則,需要修改各個工廠,同時需要新增3個類。這就是所謂的“ “開閉原則”的傾斜性  ”

簡化:Factory是否可以省去? 不能,否則各ConcreteFactory 沒有統一的規範,相互之間難以約束,就亂套了。然而實際情況也正是如此,所以它的適用範圍不多。如果我們又一個剛剛好的類似的產品體系, 則可以考慮此模式。

適用範圍:複雜的產品體系(同時包含 產品等級、產品族 兩個 維度)

 討論:AbstractProduct、Factory可以為抽象類嗎? 應該可以。不應做過多限制。但是Factory 作為一個高層級的 東西, 設定為 介面更好。

原型模式:

 

對已有物件克隆,建立一個一模一樣的物件。當建立新的物件例項較為複雜時,使用原型模式可以簡化物件的建立過程,通過複製一個已有例項可以提高新例項的建立效率。

適用範圍:對於某些複雜的,消耗資源多的物件,直接建立物件的代價大,克隆代價小。什麼情況下克隆的代價會比較小?比如一個物件有幾十個屬性,現在我需要新建一個一模一樣的物件,不想使用初始值,通常我們可以先new 一個物件,然後一個個的set Xxx( old.getXxx() ),這樣是完全沒問題的,但是顯然就影響了可讀性,而且顯得繁瑣而且笨重, 當然我們也可以使用 BeanUtils的那一套。

如果使用Cloneable,那麼只需要寫一個clone方法即可,由於clone方法是native,也就是c++實現的,那麼jdk底層做了優化,肯定是比一般的操作要快一些(估計是直接進行了記憶體拷貝操作等)。 當然, 具體有多快,是不是就比BeanUtils的那一套 還要快,我無法證明。

Prototype 就對應了java 的Cloneable。相對於BeanUtils或自己寫copyXxx方法,確實是多了一些麻煩,也就是我們需要實現它,雖然 實現clone 方法通常非常簡單,但是確實是破壞了開閉原則,然而,話也不能這麼說,使用設計模式,當然也是有一些些的代價的,但是我們要看到其方便的一面。任何事物都有兩面性,有利有弊。

原型管理器(Prototype Manager),可以把很多的Prototype 統一管理,相對於是把 Prototype  和 享元模式結合到了一起。

 拓展性:雖然外面可以 對Prototype 進行繼承實現拓展,但是 估計一般人也不會這麼做,需要多種clone 的方式嗎? 所以說Prototype 模式的重點不是拓展,而是 clone 方法的代價,是否有必要。

簡化: client 是不需要考慮的,顯然Prototype 還可以簡化, 我們可以把Prototype,ConcretePrototype 合併, 如上所述,我們幾乎不需要 多種 clone 的方式, 不需要擴充套件。

 

建造者模式:

 

適用範圍:同一過程、標準 構建 很多很複雜產品。

簡化:省去Director,可以將Director和抽象建造者Builder進行合併。當一個產品非常複雜的時候,我們需要一個Director來指揮 建造過程。當然,如果這樣的產品不多,比如只有一個ConcreteBuilder,那麼也沒必要專門的Director,我們完全可以省去Director,甚至Builder。所以它適用於 很多很複雜產品,同時,這些產品有 很相似的 建造過程。 —— 話說回來,這樣的 場景 多嗎? 也不多,所以,這個模式用得少。