考慮一個(gè)日志記錄工具。目前需要提供一個(gè)方便的日志API,使得客戶可以輕松地完成日志的記錄。該日志要求被記錄到指定的文件中,記錄的內(nèi)容屬于字符串類型,其值由客戶提供。我們可以非常容易地定義一個(gè)日志對(duì)象:
public class Log { public void Write(string target, string log) { //實(shí)現(xiàn)內(nèi)容; } } |
Log log = new Log(); log.Write(“error.log”, “l(fā)og”); |
然而隨著日志記錄的頻繁使用,有關(guān)日志的文件越來越多,日志的查詢與也變得越不方便。此時(shí),客戶提出,需要改變?nèi)罩镜挠涗浄绞,將日志?nèi)容寫入到指定的數(shù)據(jù)表中。顯然,如果仍然按照前面的設(shè)計(jì),具有較大的局限性。 現(xiàn)在我們回到設(shè)計(jì)之初,想象一下對(duì)于日志API的設(shè)計(jì),需要考慮到這樣的變化嗎?這里存在兩種設(shè)計(jì)理念,即漸進(jìn)的設(shè)計(jì)和計(jì)劃的設(shè)計(jì)。從本例來分析,要求設(shè)計(jì)者在設(shè)計(jì)初就考慮到日志記錄方式在未來的可能變化,并不容易。再者,如果在最開始就考慮全面的設(shè)計(jì),會(huì)產(chǎn)生設(shè)計(jì)上的冗余。因此,采用計(jì)劃的設(shè)計(jì)固然具有一定的前瞻性,但一方面對(duì)設(shè)計(jì)者的要求過高,同時(shí)也會(huì)產(chǎn)生一些缺陷。那么,采用漸進(jìn)的設(shè)計(jì)時(shí),遇到需求變化時(shí),利用重構(gòu)的方法,改進(jìn)現(xiàn)有的設(shè)計(jì),又需要考慮未來的再一次變化嗎?這是一個(gè)見仁見智的問題。對(duì)于本例而言,我們完全可以直接修改Write()方法,接受一個(gè)類型判斷的參數(shù),從而解決此問題。但這樣的設(shè)計(jì),自然要擔(dān)負(fù)因?yàn)槲磥砜赡艿脑僖淮巫兓,而?dǎo)致代碼大量修改的危險(xiǎn),例如,我們要求日志記錄到指定的Xml文件中。
所以,變化是完全可能的。在時(shí)間和技術(shù)能力允許的情況下,我更傾向于將變化對(duì)設(shè)計(jì)帶來的影響降低到最低。此時(shí),我們需要封裝變化。
在封裝變化之前,我們需要弄清楚究竟是什么發(fā)生了變化?從需求看,是日志記錄的方式發(fā)生了變化。從這個(gè)概念分析,可能會(huì)導(dǎo)致兩種不同的結(jié)果。一種情形是,我們將日志記錄的方式視為一種行為,確切的說,是用戶的一種請(qǐng)求。另一種情形則從對(duì)象的角度來分析,我們將各種方式的日志看作不同的對(duì)象,它們調(diào)用接口相同的行為,區(qū)別僅在于創(chuàng)建的是不同的對(duì)象。前者需要我們封裝“用戶請(qǐng)求的變化”,而后者需要我們封裝“日志對(duì)象創(chuàng)建的變化”。
封裝“用戶請(qǐng)求的變化”,在這里就是封裝日志記錄可能的變化。也就是說,我們需要把日志記錄行為抽象為一個(gè)單獨(dú)的接口,然后才分別定義不同的實(shí)現(xiàn)。如圖一所示: