亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

過程擴展與放置鉤子

系統(tǒng) 1903 0
前面我們談到了功能擴展對維護一個軟件的巨大作用。實際上,正是因為功能在不斷地擴展,才使得我們的很多軟件質(zhì)量在下降。因此,如何進行功能擴展,我們不得不察。每當(dāng)新功能到來的時候,不用急急匆匆就開始編碼,我們應(yīng)當(dāng)仔細思考我們的設(shè)計,即使是時間非常緊張的項目。用更多的時間去思考與設(shè)計,才會用更少的時間去做更簡單的設(shè)計與編碼。在這里,我提倡的是設(shè)計應(yīng)當(dāng)簡單到發(fā)指,因為它體現(xiàn)的是一種精巧絕倫,它會使我們的思路更清晰,維護更簡單,變更更容易。只有經(jīng)過仔細的思考,才會做出精巧絕倫的設(shè)計,那是我們的目標。在這方面,“小步快跑”與“兩頂帽子”的方法可以大大降低我們的設(shè)計難度,因為我們不是神,而是人。

上一章,我們用“抽取接口”的方法,替代萬惡的if語句,從而實現(xiàn)了功能擴展。注意,不是所有的if語句都需要這樣調(diào)整,只有那些真正需要擴展的時候才應(yīng)該這樣調(diào)整。如果if語句只有2、3個條件分支,并且不太可能擴展,我們真的不必這樣調(diào)整,或者當(dāng)這樣的功能擴展到來的時候再進行調(diào)整。記住,過度設(shè)計與恰到好處的設(shè)計只有一線之隔。
除此之外,我們還有很多的辦法來擴展我們的功能,其中一種擴展叫過程的擴展,我們可以這樣設(shè)計:

前面代碼復(fù)用的部分我們提到,解決處理過程中相同或相似的代碼最好的辦法就是使用模板模式。首先將那些相同的代碼抽取出來形成函數(shù),將這些函數(shù)抽象并升級為抽象類及接口,然后將各自不同的代碼統(tǒng)一函數(shù)名,放在各個實現(xiàn)類中各自去實現(xiàn)。這樣,代碼復(fù)用的問題就解決了。

但是,毫無疑問我們都不是先知,永遠都無法預(yù)測未來系統(tǒng)會變成什么樣兒。比較常見的需求變更之一就是處理步驟的變更。現(xiàn)在我們的處理有1、2、3步,而今后可能還有5、6、7步,甚至某項步驟可能會插入到現(xiàn)有步驟的中間。當(dāng)日后這樣的變更發(fā)生的時候,我們又希望符合OCP原則而不改動現(xiàn)有代碼時,又應(yīng)當(dāng)怎樣設(shè)計呢?嗯,是個問題。

我過去就曾無數(shù)次遇到過這樣的問題,其中一個令我印象深刻的就是一次平臺控件的設(shè)計。在一次平臺開發(fā)中,我設(shè)計了許多的控件,如文本框、下拉框、單選框、復(fù)選框……開發(fā)這些控件的目的是使其它開發(fā)者在設(shè)計報表的過濾條件時不用再寫任何代碼,選擇控件就可以了。起初,我為所有控件都提供了draw()和beUsed()方法,用于繪制控件和判斷該條件在查詢時是否被使用。隨著控件品種的增加,一些控件需要在繪制前要執(zhí)行一個查詢,如那些多選框、下拉框等等。為此我準備設(shè)計了一個getItems()方法,只要這些控件定義了各自的查詢語句,就可以通過該方法查詢并返回結(jié)果。但問題是,前面已經(jīng)設(shè)計好的控件不用這個方法,我不希望因為這個功能的擴展影響了前面那些控件,這該怎么設(shè)計呢?

每次面對這樣的問題時,一種叫做“鉤子(hook)”的設(shè)計就可以派上用場了。什么叫“鉤子”?它是一個空函數(shù),調(diào)用它就如同什么都沒有調(diào)用一般。但鉤子如果被放在了抽象類中,作用就非常大了。如果抽象類的子類要使用它時,則重載這個函數(shù),為其編寫各自的代碼,完成相應(yīng)的操作;而其它的子類如果不使用它,則什么也不用做。當(dāng)系統(tǒng)在調(diào)用各個子類時,被重載的子類就會去調(diào)用子類中的函數(shù),而其它沒有被重載的子類則會去調(diào)用抽象類中的“鉤子”,就如同什么都沒有做一樣。

在該示例中,getItems()就是一個“鉤子”,它首先被定義在父類AbstractControl中。AbstractControl是一個抽象類,但getItems()在里面不是被定義成一個抽象方法,而是一個普通方法,因為它不需要每個子類都去實現(xiàn)它,不使用的子類就不用再實現(xiàn)它了。
    
	/* (non-Javadoc)
	 * @see com...control.Control#getItems(com...model.RptControl)
	 */
	public List getItems(RptControl control){
		//hook only
		return null;
	}

  


那些在繪制前不需要查詢的控件,如DefaultControl,在繼承父類的時候不用去重載getItems(),因此系統(tǒng)在繪制它們時,該函數(shù)就如同不存在一般。然而那些需要查詢的控件,如QueryControl,就需要重載這個函數(shù):
    
	/* (non-Javadoc)
	 * @see com...control.Control#getItems(com...model.RptControl)
	 */
	public List getItems(RptControl control) {
		if (control==null) {
			throw new IllegalArgumentException("參數(shù)為空");
		}
		String sql = control.getSql();
		if (sql==null||"".equals(sql)) {
			throw new RuntimeException("SQL為空");
		}
		BasicQuery query = new BasicQuery();
		query.setExpression(sql);
		return getJdbcSupport().find(query);
	}

  

這樣,當(dāng)系統(tǒng)在繪制DefaultControl的時候不會去查詢數(shù)據(jù)庫,而繪制QueryControl的時候則先去進行一個數(shù)據(jù)庫查詢。現(xiàn)在我們來檢測一下該可擴展點的設(shè)計能否滿足OCP原則的要求。現(xiàn)在新需求來了,要繪制這么一個組合控件:



這個組合控件由四個下拉框組成,分別代表2個年度與2個月份,因此它在執(zhí)行查詢時,會提交到后臺這4個數(shù)據(jù)。然而,我們希望這個控件在提交給查詢模塊時,應(yīng)當(dāng)是2個數(shù)據(jù):某年某月的1日,和某年某月的最后一天。也就是說,該控件在提交參數(shù)給查詢模塊的時候需要進行一個參數(shù)轉(zhuǎn)換,而不是直接傳遞給查詢模塊。
先看看我們現(xiàn)有的設(shè)計吧:當(dāng)控件將參數(shù)提交給后臺以后,控件會直接將參數(shù)傳遞給查詢模塊。但為了實現(xiàn)這樣一個新需求,我們需要所有控件在這個地方硬生生插入一個數(shù)據(jù)轉(zhuǎn)換的功能。如果真的這樣修改了,整個系統(tǒng)就因小失大了。幸運的是,我們在這個地方有可擴展設(shè)計。
首先,我們在抽象的父類AbstractControl中加入一個非抽象方法transform():
    
	/* (non-Javadoc)
	 * @see com...control.Control#transform(java.lang.String, java.util.Map)
	 */
	public void transform(String ctrlName, Map<String, Object> params){
		//hook only
	}

  

然后我們創(chuàng)建新控件MonthRangeControl,重載transform()方法:
    
	/* (non-Javadoc)
	 * @see com...control.Control#transform(java.lang.String, java.util.Map)
	 */
	public void transform(String ctrlName, Map<String, Object> params){
		int yearLower = getValue(ctrlName, params.get(“yearLower”));
		int monthLower = getValue(ctrlName, params.get(“monthLower”));
		int yearUpper = getValue(ctrlName, params.get(“yearUpper”));
		int monthUpper = getValue(ctrlName, params.get(“monthUpper”));
		Date lower = DateUtil.getDate(yearLower, monthLower, 1);
		Date upper = 
			DateUtil.getLastDayOfMonth(DateUtil.getDate(yearUpper, monthUpper, 1));
		params.put(ctrlName, lower);
		params.put(ctrlName, upper);
}

  

整個設(shè)計修改了父類AbstractControl,增加了transform()方法,然后創(chuàng)建了新的控件類MonthRangeControl,不能說完全沒有修改原程序,但已經(jīng)在最大限度上滿足了OCP原則。整個設(shè)計如圖:



(續(xù))

相關(guān)文檔
遺留系統(tǒng):IT攻城獅永遠的痛
需求變更是罪惡之源嗎?
系統(tǒng)重構(gòu)是個什么玩意兒
我們應(yīng)當(dāng)改變我們的設(shè)計習(xí)慣
小步快跑是這樣玩的(上)
小步快跑是這樣玩的(下)
代碼復(fù)用應(yīng)該這樣做(1)
代碼復(fù)用應(yīng)該這樣做(2)
代碼復(fù)用應(yīng)該這樣做(3)
做好代碼復(fù)用不簡單
軟件可以這樣功能擴展
過程擴展與放置鉤子

特別說明:希望網(wǎng)友們在轉(zhuǎn)載本文時,應(yīng)當(dāng)注明作者或出處,以示對作者的尊重,謝謝!

過程擴展與放置鉤子


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 色一情一欲一爱一乱 | 男女啪视频大全1000 | 国产一区二区三区不卡观 | 在线观看 一区二区 麻豆 | 免费看成人播放毛片 | 中文字幕伦视频 | 爱爱视频天天看 | 日本欧美一区二区三区在线观看 | 国产亚洲精品看片在线观看 | 国产区二区 | 91精品免费高清在线 | 私人影院在线 | 啪啪网站免费 | aa毛片免费全部播放完整 | 七月婷婷精品视频在线观看 | 九九热精品视频在线 | 色妞bbbb女女女女 | 九一视频在线免费观看 | 精品一区二区三区在线播放 | 深夜精品影院18以下勿进 | 中文字幕久久亚洲一区 | 亚洲精品免费观看 | 69午夜| 久久夜色精品国产尤物 | 综合网色 | 久久国产精品岛国搬运工 | 久久久久久久91精品免费观看 | 九九久久国产精品免费热6 九九久久精品 | 亚洲国产精品久久久天堂麻豆 | 亚洲视频一二 | 亚洲欧美中文字幕专区 | 久久亚洲精品国产精品婷婷 | 在线羞羞视频 | 91精品国产免费久久久久久青草 | 亚洲国产成人最新精品资源 | 成人a毛片高清视频 | 尤物福利视频 | 久久久一级 | 国产精品福利视频一区二区三区 | 欧美一区二区三区精品国产 | 国产精品二 |