設計模式是對特定問題經過無數次經驗總結後提出的能夠解決它的優雅的方案
但是
如果想要真正使設計模式發揮最大作用
僅僅知道設計模式是什麼
以及它是如何實現的是很不夠的
因為那樣就不能使你對於設計模式有真正的理解
也就不能夠在自己的設計中正確
恰當的使用設計模式
本文試圖從另一個角度(設計模式的意圖
動機)來看待設計模式
通過這種新的思路
設計模式會變得非常貼近你的設計過程
並且能夠指導
簡化你的設計
最終將會導出一個優秀的解決方案
介紹
在進行項目的開發活動中
有一些設計在項目剛剛開始工作的很好
但是隨著項目的進展
發現需要對已有的代碼進行修改或者擴展
導致這樣做的原因主要有
新的功能需求的需要以及對系統進一步理解
在這個時候
我們往往會發現進行這項工作比較困難
即使能完成也要付出很大的代價
此時
一個必須要做的工作就是要對現有的代碼進行重構(refactoring)
通過重構使得我們接下來的工作變得相對容易
重構就是在不改變軟件系統代碼的外部行為的前提下
改善它的內部結構
重構的目標就是使代碼結構更加合理
富有彈性
能夠適應新的需求
新的變化
對於特定問題給出優美解決方案的設計模式往往會成為重構的目標
而且一旦我們能夠識別出能夠解決我們問題的設計模式
將會大大簡化我們的工作
因為我們可以重用別人已經做過的工作
但是在我們的原始設計和最終可能會適用於我們的設計模式間的過渡並不是平滑的
而是有一個間隙
這樣的結果就是
即使我們已經知道了很多的設計模式
面對我們的實際問題
我們也沒有一個有效的方法去判斷哪一個設計模式適用於我們的系統
我們應該去怎樣應用它
造成上述問題的原因往往是由於過於注重設計模式所給出的解決方案這個結果
而對於設計模式的意圖
以及它產生的動機卻忽略了
然而
正是設計模式的意圖
動機促使人們給出了一個解決一類問題的方案這個結果
設計模式的動機
意圖體現了該模式的形成思路
所以更加貼近我們的實際問題
從而會有效的指導我們的重構歷程
本文將通過一個實例來展示這個過程
在本文中對例子進行了簡化
這樣做是為了突出問題的實質並且會使我們的思路更加清晰
思路本身才是最重要
最根本的
簡化了的例子不會降低我們所展示的思路
方法的適用性
問題描述
一個完善的軟件系統
必須要對出現的錯誤進行相應的處理
只有這樣才能使系統足夠的健壯
我准備以軟件系統中對於錯誤的處理為例
來展示我所使用的思路
方法
在一個分布式的網管系統中
一個操作往往不會一定成功
常常會因為這樣或者那樣的原因失敗
此時我們就要根據失敗的原因相應的處理
使錯誤的影響局限在最小的范圍內
最好能夠恢復而不影響系統的正常運行
還有一點很重要
那就是在對錯誤進行處理的同時
一定不要忘記通知系統的管理者
因為只有管理者才有能力對錯誤進行進一步的分析
從而查找出錯誤的根源
從根本上解決錯誤
下面我就從錯誤處理的通告管理者部分入手
開始我們的旅程
假定一個在一個分布式環境中訪問數據庫的操作
那麼就有可能因為通信的原因或者數據庫本身的原因失敗
此時我們要通過用戶界面來通知管理者發生的錯誤
簡化了的代碼示例如下
/* 錯誤碼定義 */
class ErrorConstant
{
public static final int ERROR_DBACCESS =
;
public static final int ERROR_COMMUNICATION =
;
}
/* 省略了用戶界面中的其他的功能 */
class GUISys
{
public void announceError(int errCode) {
switch(errCode) {
case ErrorConstant
ERROR_DBACCESS:
/* 通告管理者數據庫訪問錯誤的發生*/
break;
case ErrorConstant
ERROR_COMMUNICATION:
/* 通告管理者通信錯誤的發生*/
break;
}
}
}
開始
這段代碼工作的很好
能夠完成我們需要的功能
但是這段代碼缺少相應的彈性
很難適應需求的變化
問題分析
熟悉面向對象的讀者很快就會發現上面的代碼是典型的結構化的方法
結構化的方法是以具體的功能為核心來組織程序的結構
它的封裝度僅為
級
即僅有對於特定的功能的封裝(函數)
這使得結構化的方法很難適應需求的變化
面向對象的方法正是在這一點上優於結構化的方法
在面向對象領域
是以對象來組成程序結構的
一個對象有自己的職責
通過對象間的交互來完成系統的功能
這使得它的封裝度至少為
級
即封裝了為完成自己職責的方法和數據
另外面向對象的方法還支持更高層次的封裝
比如
通過對於不同的具體對象的共同的概念行為進行描述
我們可以達到
級的封裝度- 抽象的類(在Java中就是接口)
封裝的層次越高
抽象的層次就越高
使得設計
代碼有越高的彈性
越容易適應變化
考慮對上一節中的代碼
如果在系統的開發過程中發現需要對一種新的錯誤進行處理
比如
用戶認證錯誤
我們該如何做使得我們的系統能夠增加對於此項功能的需求呢?一種比較簡單
直接的做法就是在增加一條用來處理此項錯誤的case語句
是的
這種方法的確能夠工作
但是這樣做是要付出代價的
首先
隨著系統的進一步開發
可能會出現更多的錯誤類型
那麼就會導致對於錯誤的處理部分代碼冗長
不利於維護
其次
也是最根本的一點
修改已經能夠工作的代碼
很容易引入錯誤
並且在很多的情況下
錯誤都是在不經意下引入的
對於這種類型的錯誤很難定位
有調查表明
我們在開發過程中
用於修正錯誤的時間並不多
大部分的時間是在調試
發現錯誤
在面向對象領域
有一個很著名的原則
OCP(Open
Closed Principle)
它的核心含意是
一個好的設計應該能夠容納新的功能需求的增加
但是增加的方式不是通過修改又有的模塊(類)
而是通過增加新的模塊(類)來完成的
如果一個設計能夠遵循OCP
那麼就能夠有效的避免上述的問題
要是一個設計能夠符合OCP原則
就要求我們在進行設計時不能簡單的以功能為核心
要實現OCP的關鍵是抽象
抽象表征了一個固定的行為
但是對於這個行為可以有很多個不同的具體實現方法
通過抽象
我們就可以用一個固定的抽象的概念來代替哪些容易變化的數量眾多的具體的概念
並且使得原來依賴於哪些容易變化的概念的模塊
依賴於這個固定的抽象的概念
這樣的結果就是
系統新的需求的增加
僅僅會引起具體的概念的增加
而不會影響依賴於具體概念的抽象體的其他模塊
在實現的層面上
抽象體是通過抽象類來描述的
在Java中是接口(interface)
關於OCP的更詳細描述
請參見參考文獻[
]
既然知道了問題的本質以及相應的解決方法
下面就來改善我們的代碼結構
初步方案
讓我們重新審視代碼
看看該如何進行抽象
在錯誤處理中
需要處理不同類型的錯誤
每個具體的錯誤具有特定於自己本身的一些信息
但是它們在概念層面上又是一致的
比如
都可以通過特定的方法接口獲取自已內部的錯誤信息
每一個錯誤都有自己的處理方法
由此可以得到一個初步的方案
可以定義一個抽象的錯誤基類
在這個基類裡面定義一些在概念上適用於所有不同的具體錯誤的方法
每個具體的錯誤可以有自己的不同的對於這些方法的實現
代碼示例如下
interface ErrorBase
{
public void handle()
public String getInfo();
}
class DBAccessError implements ErrorBase
{
public void handle() {
/* 進行關於數據庫訪問錯誤的處理 */
}
public String getInfo() {
/* 構造返回關於數據庫訪問錯誤的信息 */
}
}
class CommunicationError implements ErrorBase
{
public void handle() {
/* 進行關於通信錯誤的處理 */
}
public String getInfo() {
/* 構造返回關於通信錯誤的信息 */
}
}
這樣
我們就可以在錯誤發生處
構造一個實際的錯誤對象
並以ErrorBase引用它
然後
交給給錯誤處理模塊
此時錯誤處理模塊就僅僅知道一個類型ErrorBase
而無需知道每一個具體的錯誤類型
這樣就可以使用統一的方式來處理錯誤了
代碼示例如下
class GUISys
{
public void announceError(ErrorBase error) {
/* 使用一致的方式處理錯誤 */
er
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27432.html