欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

過程擴展與放置鉤子

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

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

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

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

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

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

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

  


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

  

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



這個組合控件由四個下拉框組成,分別代表2個年度與2個月份,因此它在執行查詢時,會提交到后臺這4個數據。然而,我們希望這個控件在提交給查詢模塊時,應當是2個數據:某年某月的1日,和某年某月的最后一天。也就是說,該控件在提交參數給查詢模塊的時候需要進行一個參數轉換,而不是直接傳遞給查詢模塊。
先看看我們現有的設計吧:當控件將參數提交給后臺以后,控件會直接將參數傳遞給查詢模塊。但為了實現這樣一個新需求,我們需要所有控件在這個地方硬生生插入一個數據轉換的功能。如果真的這樣修改了,整個系統就因小失大了。幸運的是,我們在這個地方有可擴展設計。
首先,我們在抽象的父類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
	}

  

然后我們創建新控件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);
}

  

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



(續)

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

特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!

過程擴展與放置鉤子


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

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

【本文對您有幫助就好】

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

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 天天碰天天操 | 色噜噜狠狠狠狠色综合久不 | 亚洲精品国偷拍自产在线观看 | 性欧美激情在线观看 | 国产精品在线 | 国产午夜大片 | 国产一区二 | 天天综合久久 | 亚洲精品久久久久久蜜臀 | 亚洲三级在线 | 久草视频免费播放 | 草久网| 毛片在线视频观看 | 国产999精品久久久影片官网 | 好吊妞gao988在线播放 | 欧美视频三区 | 91免费在线看 | 色视频免费版高清在线观看 | 250pp久久新 全黄性性激高免费视频 | 黄工厂精品视频在线观看 | 免费黄色欧美视频 | 一区二区日韩 | 久福利| 一级国产黄色片 | 奇米影视四色中文字幕 | 精品久久国产 | 免费啪视频在线观看免费的 | 国产秋霞| 久久伊99综合婷婷久久伊 | 欧美日韩亚洲综合另类ac | 成人a视频 | 色综合网址 | 久久艹免费视频 | 福利国产 | 久久中文字幕久久久久91 | 狠狠色狠狠色 | 国产人A片777777久久 | 免费看一级欧美毛片视频 | 天天摸天天碰天天碰 | 日韩精品一二区 | 一区二区av在线 |