青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Impossible is nothing  
  愛過知情重醉過知酒濃   花開花謝終是空   緣份不停留像春風來又走   女人如花花似夢
公告
日歷
<2025年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011
統計
  • 隨筆 - 8
  • 文章 - 91
  • 評論 - 16
  • 引用 - 0

導航

常用鏈接

留言簿(4)

隨筆分類(4)

隨筆檔案(8)

文章分類(77)

文章檔案(91)

相冊

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

 

軟件設計最大的敵人,就是應付需求不斷的變化。變化有時候是無窮盡的,于是項目開發就在反復的修改、更新中無限期地延遲交付的日期。變化如懸在頭頂的達摩克斯之劍,令許多軟件工程專家一籌莫展。正如無法找到解決軟件開發的“銀彈”,要徹底將變化扼殺在搖籃之中,看來也是不可能完成的任務。那么,積極地面對“變化”,方才是可取的態度。于是,極限編程(XP)的倡導者與布道者Kent Beck提出要“擁抱變化”,從軟件工程方法的角度,提出了應對“變化”的解決方案。而本文則試圖從軟件設計方法的角度,來探討如何在軟件設計過程中,解決未來可能的變化,其方法就是——封裝變化。

設計模式是“封裝變化”方法的最佳闡釋。無論是創建型模式、結構型模式還是行為型模式,歸根結底都是尋找軟件中可能存在的“變化”,然后利用抽象的方式對這些變化進行封裝。由于抽象沒有具體的實現,就代表了一種無限的可能性,使得其擴展成為了可能。所以,我們在設計之初,除了要實現需求所設定的用例之外,還需要標定可能或已經存在的“變化”之處。封裝變化,最重要的一點就是發現變化,或者說是尋找變化。

GOF對設計模式的分類,已經彰顯了“封裝變化”的內涵與精髓。創建型模式的目的就是封裝對象創建的變化。例如Factory Method模式和Abstract Factory模式,建立了專門的抽象的工廠類,以此來封裝未來對象的創建所引起的可能變化。而Builder模式則是對對象內部的創建進行封裝,由于細節對抽象的可替換性,使得將來面對對象內部創建方式的變化,可以靈活的進行擴展或替換。

至于結構型模式,它關注的是對象之間組合的方式。本質上說,如果對象結構可能存在變化,主要在于其依賴關系的改變。當然對于結構型模式來說,處理變化的方式不僅僅是封裝與抽象那么簡單,還要合理地利用繼承與聚合的方法,靈活地表達對象之間的依賴關系。例如Decorator模式,描述的就是對象間可能存在的多種組合方式,這種組合方式是一種裝飾者與被裝飾者之間的關系,因此封裝這種組合方式,抽象出專門的裝飾對象顯然正是“封裝變化”的體現。同樣地,Bridge模式封裝的則是對象實現的依賴關系,而Composite模式所要解決的則是對象間存在的遞歸關系。

行為型模式關注的是對象的行為。行為型模式需要做的是對變化的行為進行抽象,通過封裝以達到整個架構的可擴展性。例如策略模式,就是將可能存在變化的策略或算法抽象為一個獨立的接口或抽象類,以實現策略擴展的目的。Command模式、State模式、Vistor模式、Iterator模式概莫如是。或者封裝一個請求(Command模式),或者封裝一種狀態(State模式),或者封裝“訪問”的方式(Visitor模式),或者封裝“遍歷”算法(Iterator模式)。而這些所要封裝的行為,恰恰是軟件架構中最不穩定的部分,其擴展的可能性也最大。將這些行為封裝起來,利用抽象的特性,就提供了擴展的可能。

利用設計模式,通過封裝變化的方法,可以最大限度的保證軟件的可擴展性。面對紛繁復雜的需求變化,雖然不可能完全解決因為變化帶來的可怕夢魘,然而,如能在設計之初預見某些變化,仍有可能在一定程度上避免未來存在的變化為軟件架構帶來的災難性傷害。從此點看,雖然沒有“銀彈”,但從軟件設計方法的角度來看,設計模式也是一枚不錯的“銅彈”了。

考慮一個日志記錄工具。目前需要提供一個方便的日志API,使得客戶可以輕松地完成日志的記錄。該日志要求被記錄到指定的文本文件中,記錄的內容屬于字符串類型,其值由客戶提供。我們可以非常容易地定義一個日志對象:

public class Log

{

       public void Write(string target, string log)

{

       //實現內容;

}

}

當客戶需要調用日志的功能時,可以創建日志對象,完成日志的記錄:

Log log = new Log();

log.Write(“error.log”, “log”);

然而隨著日志記錄的頻繁使用,有關日志的文件越來越多,日志的查詢與管理也變得越不方便。此時,客戶提出,需要改變日志的記錄方式,將日志內容寫入到指定的數據表中。顯然,如果仍然按照前面的設計,具有較大的局限性。

現在我們回到設計之初,想象一下對于日志API的設計,需要考慮到這樣的變化嗎?這里存在兩種設計理念,即漸進的設計和計劃的設計。從本例來分析,要求設計者在設計初就考慮到日志記錄方式在未來的可能變化,并不容易。再者,如果在最開始就考慮全面的設計,會產生設計上的冗余。因此,采用計劃的設計固然具有一定的前瞻性,但一方面對設計者的要求過高,同時也會產生一些缺陷。那么,采用漸進的設計時,遇到需求變化時,利用重構的方法,改進現有的設計,又需要考慮未來的再一次變化嗎?這是一個見仁見智的問題。對于本例而言,我們完全可以直接修改Write()方法,接受一個類型判斷的參數,從而解決此問題。但這樣的設計,自然要擔負因為未來可能的再一次變化,而導致代碼大量修改的危險,例如,我們要求日志記錄到指定的Xml文件中。

所以,變化是完全可能的。在時間和技術能力允許的情況下,我更傾向于將變化對設計帶來的影響降低到最低。此時,我們需要封裝變化。

在封裝變化之前,我們需要弄清楚究竟是什么發生了變化?從需求看,是日志記錄的方式發生了變化。從這個概念分析,可能會導致兩種不同的結果。一種情形是,我們將日志記錄的方式視為一種行為,確切的說,是用戶的一種請求。另一種情形則從對象的角度來分析,我們將各種方式的日志看作不同的對象,它們調用接口相同的行為,區別僅在于創建的是不同的對象。前者需要我們封裝“用戶請求的變化”,而后者需要我們封裝“日志對象創建的變化”。

封裝“用戶請求的變化”,在這里就是封裝日志記錄可能的變化。也就是說,我們需要把日志記錄行為抽象為一個單獨的接口,然后才分別定義不同的實現。如圖一所示:


change01.jpg
圖一:封裝日志記錄行為的變化

如果熟悉設計模式,可以看到圖一所表示的結構正是Command模式的體現。由于我們對日志記錄行為進行了接口抽象,用戶就可以自由地擴展日志記錄的方式,只需要實現ILog接口即可。至于Log對象,則存在與ILog接口的弱依賴關系:

public class Log

{

       private ILog log;

       public Log(ILog log)

       {

              this.log = log;

       }

       public void Write(string target, string logValue)

       {

              log.Execute(target, logValue);

       }

}

我們也可以通過封裝“日志對象創建的變化”實現日志API的可擴展性。在這種情況下,日志會根據記錄方式的不同,被定義為不同的對象。當我們需要記錄日志時,就創建相應的日志對象,然后調用該對象的Write()方法,實現日志的記錄。此時,可能會發生變化的是需要創建的日志對象,那么要封裝這種變化,就可以定義一個抽象的工廠類,專門負責日志對象的創建,如圖二所示:


change02.jpg
圖二:封裝日志對象創建的變化

圖二是Factory Method模式的體現,由抽象類LogFactory專門負責Log對象的創建。如果用戶需要記錄相應的日志,例如要求日志記錄到數據庫,需要先創建具體的LogFactory對象:

LogFactory factory = new DBLogFactory();

當在應用程序中,需要記錄日志,那么再通過LogFactory對象來獲取新的Log對象:

Log log = factory.Create();

log.Write(“ErrorLog”, “log”);

如果用戶需要改變日志記錄的方式為文本文件時,僅需要修改LogFactory對象的創建即可:

LogFactory factory = new TxtFileLogFactory();

為了更好地理解“封裝對象創建的變化”,我們再來看一個例子。假如,我們需要設計一個數據庫組件,它能夠訪問微軟的Sql Server數據庫。根據ADO.Net的知識,我們需要使用如下的對象:

SqlConnection, SqlCommand, SqlDataAdapter等。

如果僅就Sql Server而言,在訪問數據庫時,我們可以直接創建這些對象:

SqlConnection connection = new SqlConnection(strConnection);

SqlCommand command = new SqlCommand(connection);

SqlDataAdapter adapter = new SqlDataAdapter();

如果在一個數據庫組件中,充斥著如上的語句,顯然是不合理的。它充滿了僵化的壞味道,一旦要求支持其他數據庫時,原有的設計就需要徹底的修改,這為擴展帶來了困難。

那么我們來思考一下,以上的設計應該做怎樣的修改?假定該數據庫組件要求或者將來要求支持多種數據庫,那么對于Connection,Command,DataAdapter等對象而言,就不能具體化為Sql Server的對象。也就是說,我們需要為這些對象建立一個繼承的層次結構,為他們分別建立抽象的父類,或者接口。然后針對不同的數據庫,定義不同的具體類,這些具體類又都繼承或實現各自的父類,例如Connection對象:


change03.jpg
圖三:
Connection對象的層次結構

我為Connection對象抽象了一個統一的IConnection接口,而支持各種數據庫的Connection對象都實現了IConnection接口。同樣的,Command對象和DataAdapter對象也采用了相似的結構。現在,我們要創建對象的時候,可以利用多態的原理創建:

IConnection connection = new SqlConnection(strConnection);

從這個結構可以看到,根據訪問的數據庫的不同,對象的創建可能會發生變化。也就是說,我們需要設計的數據庫組件,以現在的結構來看,仍然存在無法應對對象創建發生變化的問題。利用“封裝變化”的原理,我們有必要把創建對象的責任單獨抽象出來,以進行有效地封裝。例如,如上的創建對象的代碼,就應該由專門的對象來負責。我們仍然可以建立一個專門的抽象工廠類DBFactory,并由它負責創建ConnectionCommand,DataAdapter對象。至于實現該抽象類的具體類,則與目標對象的結構相同,根據數據庫類型的不同,定義不同的工廠類,類圖如圖四所示:


change04.jpg
圖四:
DBFactory的類圖

圖四是一個典型的Abstract Factory模式的體現。類DBFactory中的各個方法均為abstract方法,所以我們也可以用接口來代替該類的定義。繼承DBFactory類的各個具體類,則創建相對應的數據庫類型的對象。以SqlDBFactory類為例,創建各自對象的代碼如下:

public class SqlDBFactory: DBFactory

{

       public override IConnection CreateConnection(string strConnection)

       {

              return new SqlConnection(strConnection);

       }

       public override ICommand CreateCommand(IConnection connection)

       {

              return new SqlCommand(connection);

       }

       public override IDataAdapter CreateDataAdapter()

       {

              return new SqlDataAdapter();

       }

}

現在要創建訪問Sql Server數據庫的相關對象,就可以利用工廠類來獲得。首先,我們可以在程序的初始化部分創建工廠對象:

DBFactory factory = new SqlDBFactory();

然后利用該工廠對象創建相應的Connection,Command等對象:

IConnection connection = factory.CreateConnection(strConnection);

ICommand command = factory.CreateCommand(connection);

由于我們利用了封裝變化的原理,建立了專門的工廠類,以封裝對象創建的變化??梢钥吹剑斘覀円牍S類后,Connection,Command等對象的創建語句中,已經成功地消除了其與具體的數據庫類型相依賴的關系。在如上的代碼中,并未出現Sql之類的具體類型,如SqlConnectionSqlCommand等。也就是說,現在創建對象的方式是完全抽象的,是與具體實現無關的。無論是訪問何種數據庫,都與這幾行代碼無關。至于涉及到的數據庫類型的變化,則全部抽象到DBFactory抽象類中了。需要更改訪問數據庫的類型,我們也只需要修改創建工廠對象的那一行代碼,例如將Sql Server類型修改為Oracle類型:

DBFactory factory = new OracleDBFactory();

很顯然,這樣的方式提高了數據庫組件的可擴展性。我們將可能發生變化的部分封裝起來,放到程序固定的部分,例如初始化部分,或者作為全局變量,更可以將這些可能發生變化的地方,放到配置文件中,通過讀取配置文件的值,創建相對應的對象。如此一來,不需要修改代碼,也不需要重新編譯,僅僅是修改xml文件,就能實現數據庫類型的改變。例如,我們創建如下的配置文件:

<appSettings>

       <add key=”db” value=”SqlDBFactory”/>

</appSettings>

創建工廠對象的代碼則相應修改如下:

string factoryName = ConfigurationSettings.AppSettings[“db”].ToString();

//DBLib為數據庫組件的程序集:

DBFactory factory = (DBFactory)Activator.CreateInstance(“DBLib”,factoryName).Unwrap();

       當我們需要將訪問的數據庫類型修改為Oracle數據庫時,只需要將配置文件中的Value值修改為“OracleDBFactory”即可。這種結構具有很好的可擴展性,較好地解決了未來可能發生的需求變化所帶來的問題。

posted on 2006-02-23 14:52 笑笑生 閱讀(391) 評論(0)  編輯 收藏 引用 所屬分類: 軟件工程
 
Copyright © 笑笑生 Powered by: 博客園 模板提供:滬江博客
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久久国产成人精品亚洲午夜| 欧美在线一级va免费观看| 欧美在线播放一区二区| 国产情侣久久| 久久九九国产| 亚洲欧美制服另类日韩| 国产精品一区免费在线观看| 久久久精彩视频| 久久先锋资源| 一二三四社区欧美黄| 999亚洲国产精| 欧美成人亚洲成人| 亚洲欧美日韩国产| 久久久91精品| 亚洲久久在线| 亚洲欧美在线x视频| 在线观看欧美视频| 亚洲精品一区二区三| 蜜臀av国产精品久久久久| 亚洲欧洲日本一区二区三区| 9色精品在线| 国产一区二区三区四区五区美女| 男男成人高潮片免费网站| 欧美精品一区二区三区很污很色的 | 欧美成人国产一区二区| 亚洲一区二区三区高清不卡| 午夜视频一区在线观看| 亚洲人成人一区二区三区| 亚洲三级视频| 欧美国产日韩一二三区| 亚洲欧美网站| 欧美大片在线看| 欧美综合国产精品久久丁香| 裸体女人亚洲精品一区| 欧美性大战久久久久| 久久久久国产精品一区| 欧美日韩国产一区二区| 免费在线看成人av| 国产精品精品视频| 亚洲国产精品精华液网站| 国产一区再线| 99综合在线| 亚洲美女av在线播放| 久久国产天堂福利天堂| 小嫩嫩精品导航| 欧美精品性视频| 免费国产一区二区| 国产精品视频一二三| 日韩视频在线播放| 亚洲黄页视频免费观看| 久久国产日本精品| 久久精品国产第一区二区三区| 国产亚洲欧美一区在线观看| 久久精品一区二区三区中文字幕 | 一本色道久久综合狠狠躁篇怎么玩 | 欧美在线观看日本一区| 午夜精品偷拍| 国产精品任我爽爆在线播放| 亚洲美女诱惑| 一本色道久久综合亚洲精品高清| 久久成人精品| 久久久久9999亚洲精品| 国产精品一区一区| 亚洲欧美日韩国产一区二区三区| 亚洲一区二区三区久久| 欧美日韩日日夜夜| 一区二区激情视频| 国产精品99久久99久久久二8| 欧美激情亚洲| 亚洲黄色大片| 亚洲精品国产精品国自产在线 | 亚洲一区三区电影在线观看| 亚洲欧美在线网| 国产精品毛片大码女人| 亚洲欧美综合国产精品一区| 亚洲在线免费视频| 国产精品日产欧美久久久久| 亚洲欧美国产精品桃花| 欧美一区影院| 狠狠网亚洲精品| 免费亚洲视频| 日韩视频专区| 欧美在线免费观看| 在线观看一区视频| 欧美激情一二区| 中文一区二区在线观看| 久久av一区二区三区亚洲| 国语自产精品视频在线看8查询8 | 国产精品99久久久久久宅男 | 欧美一区二区三区另类| 国模吧视频一区| 欧美多人爱爱视频网站| 一区二区三区国产在线| 黄网站色欧美视频| 欧美波霸影院| 亚洲一区国产| 看欧美日韩国产| 一区二区三区高清在线观看| 国产美女精品视频免费观看| 久久久精品五月天| 亚洲精选在线观看| 久久嫩草精品久久久久| 亚洲精品一区二区三区婷婷月 | 9i看片成人免费高清| 国产美女扒开尿口久久久| 老色批av在线精品| 亚洲在线一区二区| 亚洲高清久久网| 午夜精品久久久99热福利| 国户精品久久久久久久久久久不卡| 免费在线成人av| 亚洲欧美综合网| 91久久久在线| 麻豆av福利av久久av| 亚洲欧美一区二区三区极速播放| 影音先锋亚洲一区| 国产欧美精品日韩| 欧美精品在线播放| 久久米奇亚洲| 久久免费视频网| 一本一本久久a久久精品综合妖精| 久久久亚洲国产天美传媒修理工 | 一二三四社区欧美黄| 亚洲丰满在线| 国模 一区 二区 三区| 国产伦精品一区二区| 欧美日韩三级在线| 欧美高清在线视频观看不卡| 欧美一区二区三区免费视频 | 久久亚洲综合| 制服丝袜激情欧洲亚洲| 亚洲国产一区二区三区高清| 黑人极品videos精品欧美裸| 国产欧美精品日韩| 国产女主播在线一区二区| 欧美日韩久久精品| 欧美日韩国产成人| 欧美日韩一卡二卡| 欧美日韩不卡| 欧美另类综合| 欧美日韩精品免费| 欧美视频网址| 国产精品国内视频| 欧美三日本三级少妇三2023| 欧美日本亚洲| 欧美日韩国产精品一卡| 欧美日韩国产专区| 欧美日韩国产区| 欧美天堂亚洲电影院在线播放| 欧美日韩亚洲一区二区三区在线| 欧美乱妇高清无乱码| 欧美日韩色综合| 欧美日韩中文字幕在线| 国产精品国产馆在线真实露脸| 国产精品久久九九| 国产免费成人| 国产精品视频精品视频| 欧美日韩亚洲综合在线| 国产精品久久久久久久午夜| 国产精品视频久久| 激情av一区| 亚洲三级国产| 欧美色欧美亚洲高清在线视频| 欧美日韩国产在线播放| 国产精品亚洲片夜色在线| 国产日韩欧美成人| 亚洲福利av| 亚洲一区二区三区在线观看视频| 新狼窝色av性久久久久久| 久久人人爽人人爽爽久久| 亚洲国产婷婷综合在线精品 | 亚洲精品一区二区三区不| 亚洲午夜伦理| 久久这里有精品15一区二区三区| 欧美国产日韩一区| 国产免费观看久久| 影音先锋亚洲视频| 亚洲在线不卡| 免费看精品久久片| 99ri日韩精品视频| 久久er精品视频| 欧美日韩国产精品一卡| 精品不卡在线| 亚洲一区二区视频在线观看| 久久亚洲精选| 91久久精品一区二区别| 午夜亚洲伦理| 欧美激情一区二区三区四区| 国产精品丝袜xxxxxxx| 亚洲日本在线观看| 久久精品一区二区三区不卡| 亚洲免费播放| 久久久精品性| 国产精品久久久久久影院8一贰佰| 国内精品久久久| 中国亚洲黄色| 欧美福利视频| 羞羞色国产精品| 国产精品www994| 亚洲美女91|