<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Dict.CN 在線詞典, 英語學習, 在線翻譯

    都市淘沙者

    荔枝FM Everyone can be host

    統計

    留言簿(23)

    積分與排名

    優秀學習網站

    友情連接

    閱讀排行榜

    評論排行榜

    敏捷開發的必要技巧:處理不合適的依賴

    source: http://www.matrix.org.cn/resource/article/2007-01-17/Agile+Tips_2d4b7190-a5ee-11db-8440-755941c7293d.html

    摘要:

    要判斷一個代碼是不是包含了“不合適的依賴”,共有四個方法:1.看代碼:有沒有互相依賴?2.認真想想:它真正需要的是什么?3.推測一下:它在以后的系統中可以重用嗎?4.到要重用的時候就知道了:現在我要重用這個類,能不能重用? 如果現在有一個類Parent,里面有個屬性的類型是Child,add的方法里面還有個參數的類型是Girl:
      class Parent{
    ????????Child child;
    ????????void add(Girl girl){
    ?????????? ...
    ????????}
    ???? }



    ???? 因為上面Parent里面用到了Child跟Girl這兩個類,我們就說,Parent引用了類Child跟類Girl。現在的問題是,如果Child這個類或者Girl這個類編譯不過的話,那么Parent這個類也編譯不了了。也就是說,Parent依賴于Child跟Girl。這章講述的,就是因為一些類的依賴造成的無法重用的問題。

    示例

      這是一個處理ZIP的程序。用戶可以在主窗口中先輸入要生成的目標zip的路徑,比如c:\f.zip ,然后輸入他想壓縮到這個zip的源文件的路徑,比如
    c:\f2.doc和c:\f2.doc 。然后這個程序就會開始壓縮f1.doc和f2.doc,生成f.zip文件。在壓縮各個源文件的時候,主窗口下的狀態欄都要顯示相關的信息。比如,在壓縮c:\f2.doc的時候,狀態欄就顯示"正在壓縮 c:\f2.zip"。??????????????????????????????????????????????????????????????????????????????

    目前的代碼就是????????????????????????????????????????????????????????????????????????????

    ????class ZipMainFrame extends Frame {???????????????????????????????????????????????????????????? 
    ?????? StatusBar sb;??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip() {????????????????????????????????????????????????????????????????????????????
    ?????????? String zipFilePath;????????????????????????????????????????????????????????????????????
    ?????????? String srcFilePaths[];??????????????????????????????????????????????????????????????????
    ?????????? //根據UI上給zipFilePath和srcFilePaths賦值??????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? ZipEngine ze = new ZipEngine();????????????????????????????????????????????????????????
    ?????????? ze.makeZip(zipFilePath, srcFilePaths, this);????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ?????? void setStatusBarText(String statusText) {??????????????????????????????????????????????????
    ?????????? sb.setText(statusText);????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}??
    ????
    ????class ZipEngine {??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip(String zipFilePath, String srcFilePaths[], ZipMainFrame f) {??????????????????
    ?????????? //在該路徑上創建zip文件????????????????????????????????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? for (int i = 0; i < srcFilePaths.length; i++) {????????????????????????????????????????
    ?????????????? //將srcFilePaths[i]的文件加到壓縮包中??????????????????????????????????
    ?????????????? ...????????????????????????????????????????????????????????????????????????????????
    ?????????????? f.setStatusBarText("Zipping "+srcFilePaths[i]);????????????????????????????????????
    ?????????? }??????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}
    ??????????????????????????????????????????????????????????????????????????????????????????????

      我們還有一個存貨管理系統,里面有一些程序的數據文件,經常需要壓縮起來備份。這些源數據文件都有固定的路徑,所以就不需要用戶特地去輸入路徑了。現在我們想直接把上面的這個ZipEngine類拿過來重用。這個存貨管理系統也有一個主窗口,同樣在壓縮待備份文件時,狀態欄上面也要顯示目前正在壓縮的文件名稱。

      現在,問題來了。我們希望可以在不用修改代碼的情況下直接重用ZipEngine這個類。但看了上面的代碼以后我們發現:在調用makeZip這個方法時,還需要一個傳遞一個ZipMainFrame類型的參數進來。可是很明顯我們現在的這個存貨管理系統里面并沒有ZipMainFrame這樣的類。也就是說,現在ZipEngine這個類,在我們的這個存貨管理系統中用不了了。

      再往遠一點想,好像其他的系統,一般也不會有ZipMainFrame這個類。即使類名一樣的,里面所做的功能也不一樣。那其他的系統也重用不了這個ZipEngine類了。

    “不合適的依賴”,讓代碼很難被重用

      因為ZipEngine引用了ZipMainFrame這個類,當我們想重用ZipEngine的時候,我們就需要將ZipMainFrame也加進來,調用ZipEngine的makeZip方法時,還要構造一個ZipMainFrame對象傳給它。而在新的環境中,我們不可能有一個同樣的ZipMainFrame,也不可能特地為了調用這個方法,隨便創建一個ZipMainFrame對象給它。

      一般來說,如果一個類A引用了一個類B,當我們想要重用A這個類時,我們就還得將B這個類也加進我們的系統。如果B引用了C,那么B又將C也一起拉了進來。而如果B或者C在一個新的系統中沒有意義,或者壓根兒不應該存在的情況下,真正我們想要用的A這個類也用不了了。

      因此,“不合適的依賴”讓代碼很難被重用。

      為了可以重用ZipEngine,首先,我們得讓ZipEngine不再引用ZipMainFrame。或者說,讓ZipEngine不用依賴于ZipMainFrame。
      那怎么做呢?回答這個問題之前,我們先回答另一個問題:給你一段代碼,你怎么判斷這段代碼是不是包含了“不合適的依賴”?“不合適”這個詞定義的標準又是什么?


    怎么判斷是“不合適的依賴”

      方法1:
      一個簡單的方法就是:我們先看一下這段代碼里面有沒有一些互相循環的引用。比如,ZipMainFrame引用了ZipEngine這個類,而ZipEngine又引用了ZipMainFrame。我們管這樣的類叫“互相依賴”。互相依賴也是一種代碼異味,我們就認定這樣的代碼,是“不合適的依賴”。
      這個方法很簡單。不過,這種方法并不能包含全部情況,并不是所有有“不合適的依賴”的代碼,都是這種互相依賴。

      方法2:
      另一個方法比較主觀:在檢查代碼的時候,我們問自己:對于它已經引用的這些類,是它真正需要引用的嗎?對于ZipEngine,它真的需要ZipMainFrame這個類嗎?ZipEngine只是改變ZipMainFrame的狀態欄上的信息。是不是只有引用了ZipMainFrame才能滿足這樣的需求,其他類行不行?有沒有一個類可以取代ZipMainFrame呢?而實際上,ZipEngine并不是一定要引用ZipMainFrame的。它想引用的,其實只是一個可以顯示信息的狀態欄而已。
    因此,我們就將代碼改為:

    ????class ZipEngine {??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip(String zipFilePath, String srcFilePaths[], StatusBar statusBar) {??????????????
    ?????????? //在該路徑上創建zip文件????????????????????????????????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? for (int i = 0; i < srcFilePaths.length; i++) {????????????????????????????????????????
    ?????????????? //將srcFilePaths[i]的文件加到壓縮包中??????????????????????????????????
    ?????????????? ...????????????????????????????????????????????????????????????????????????????????
    ?????????????? statusbar.setText("Zipping "+srcFilePaths[i]);??????????????????????????????????????
    ?????????? }??????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}????????
    ??????????????????????????????????????????????????????????????????????????????????????

      現在,ZipEngine只是引用了StatusBar,而不再是ZipMainFrame了。可是這樣好嗎?相對好一些!因為StatusBar比較通用(至少有StatusBar這個類的系統比ZipMainFrame多多了),這樣的話,ZipEngine這個類的可重用性就大幅改觀了。
      不過,這樣的方法還是太主觀了。沒有一個既定的標準,可以判斷ZipEngine到底需要的是什么樣的東西。比如,我們就說,ZipEngine其實想要的也不是一個狀態欄,它只是想調用一個可以顯示一些信息的接口而已(而不是一個狀態欄這么大的一個對象)。????????????????????????????????????????????????????????????????????

      方法3:

      第3種方法也很主觀:在設計類的時候,我們先預測一個以后可能會重用這個類的系統。然后再判斷,在那樣的系統中,這個類能不能被重用?如果你自己都覺得以后的系統不能重用這個類的話,你就斷定,這個類包含“不合適的依賴”了。
      比如,我們在設計完ZipEngine這個類時,我們就想一下,這個類能在別的系統重用嗎?可是好像別的系統,不會有ZipMainFrame這個類,至少一個沒有GUI的系統會有這樣的類!這樣的話,那它就不應該引用ZipMainFrame這個類了。這個方法其實也很主觀,不怎么實用。每個人預測的可能性都不一樣。

      方法4

      第4個方法比較簡單而且客觀了。當我們想在一個新系統中重用這個類,卻發現重用不了時,我們就判斷,這個類包含了“不合適的依賴”。比如,我們在存貨管理系統中,要重用ZipEngine的時候,我們才發現,這個類重用不了。這時我們就認定,這個類有“不合適的依賴”。

      后一種方法是個“懶惰而被動的”方法,因為我們真正想在具體的項目中重用的時候,才能判斷出來。不過這也是個很有效的方法。


    總結

      要判斷一個代碼是不是包含了“不合適的依賴”,共有四個方法:
    ?? 1.看代碼:有沒有互相依賴?

    ?? 2.認真想想:它真正需要的是什么?

    ?? 3.推測一下:它在以后的系統中可以重用嗎?

    ?? 4.到要重用的時候就知道了:現在我要重用這個類,能不能重用?


      方法1跟4是最簡單的方法,推薦初學者可以這樣來判斷。有更多的設計經驗了,再用方法2跟3會好一些。

    怎么讓ZipEngine不再引用(依賴于)ZipMainFrame

      現在我們來看看,怎么讓ZipEngine不再引用ZipMainFrame。其實,在介紹方法2的時候,我們就已經通過思考發現,ZipEngine這個類真正需要的是什么,也找出了解決辦法。不過因為方法2相對來講并不是那么簡單就可以用好的,所以我們先假裝不知道方法2的結果。

      我們用方法4。現在我們是在做一個文字模式的系統(沒有狀態欄了,我們只能直接在沒有圖形的屏幕上顯示這些信息),發現ZipEngine不能重用了。怎么辦?

      因為我們不能重用ZipEngine,我們只好先將它的代碼復制粘貼出來,然后再修改成下面的代碼:

    ????class TextModeApp {
    ?????? void makeZip() {
    ?????????? String zipFilePath;
    ?????????? String srcFilePaths[];
    ?????????? ...??
    ?????????? ZipEngine ze = new ZipEngine();
    ?????????? ze.makeZip(zipFilePath, srcFilePaths);
    ?????? }????????
    ????}??????????
    ????class ZipEngine {
    ?????? void makeZip(String zipFilePath, String srcFilePaths[]) {
    ?????????? //在該路徑上創建zip文件
    ?????????? ...??
    ?????????? for (int i = 0; i < srcFilePaths.length; i++) {
    ?????????????? //將srcFilePaths[i]的文件加到壓縮包中
    ?????????????? ...
    ?????????????? System.out.println("Zipping "+srcFilePaths[i]);????????????????????????????????????
    ?????????? }??????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}??
    ??????????????????????????????????????????????????????????????????????????????????????????

      再看一下原來的代碼是:
    ????class ZipEngine {??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip(String zipFilePath, String srcFilePaths[], ZipMainFrame f) {??????????????????
    ?????????? //在該路徑上創建zip文件????????????????????????????????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? for (int i = 0; i < srcFilePaths.length; i++) {????????????????????????????????????????
    ?????????????? //將srcFilePaths[i]的文件加到壓縮包中??????????????????????????????????
    ?????????????? ...????????????????????????????????????????????????????????????????????????????????
    ?????????????? f.setStatusBarText("Zipping "+srcFilePaths[i]);????????????????????????????????????
    ?????????? }??????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}??????????????????????????????????????????????????????????????????????????????????????????????


      很明顯,這里面有很多的重復代碼(代碼異味)。要消除這樣的代碼異味,我們就先用偽碼讓這兩段代碼看起來一樣。比如,改成:

    ????class ZipEngine {??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip(String zipFilePath, String srcFilePaths[]) {??????????????????????????????????
    ?????????? //在該路徑上創建zip文件????????????????????????????????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? for (int i = 0; i < srcFilePaths.length; i++) {????????????????????????????????????????
    ?????????????? //將srcFilePaths[i]的文件加到壓縮包中??????????????????????????????????
    ?????????????? ...????????????????????????????????????????????????????????????????????????????????
    ?????????????? 顯示信息。。。??????????????????????????????????????????????????????????????????
    ?????????? }??????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}????????
    ??????????????????????????????????????????????????????????????????????????????????????

      因為“顯示信息”具體出來,有兩種實現,所以我們現在創建一個接口,里面有一個方法用來顯示信息。這個方法可以直接取名為“showMessage”,而根據這個接口做的事,我們也可以直接將接口名取為“MessageDisplay”或者“MessageSink”之類的:

    ????interface MessageDisplay {????????????????????????????????????????????????????????????????????
    ?????? void showMessage(String msg);??????????????????????????????????????????????????????????????
    ????}??????????????????????????????????????????????????????????????????????????????????????????????

      將ZipEngine改為:

    ????class ZipEngine {??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip(String zipFilePath, String srcFilePaths[], MessageDisplay??????????????????????
    ????msgDisplay) {??????????????????????????????????????????????????????????????????????????????????
    ?????????? //在該路徑上創建zip文件????????????????????????????????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? for (int i = 0; i < srcFilePaths.length; i++) {????????????????????????????????????????
    ?????????????? //將srcFilePaths[i]的文件加到壓縮包中??????????????????????????????????
    ?????????????? ...????????????????????????????????????????????????????????????????????????????????
    ?????????????? msgDisplay.showMessage("Zipping "+srcFilePaths[i]);
    ?????????? }????
    ?????? }????????
    ????}??
    ????????

      而MessageDisplay這個接口的兩個實現類就是:

    ????class ZipMainFrameMessageDisplay implements MessageDisplay {
    ?????? ZipMainFrame f;
    ?????? ZipMainFrameMessageDisplay(ZipMainFrame f) {
    ?????????? this.f = f;
    ?????? }????????
    ?????? void showMessage(String msg) {
    ?????????? f.setStatusBarText(msg);
    ?????? }????????
    ????}??????????

    ????class SystemOutMessageDisplay implements MessageDisplay {
    ?????? void showMessage(String msg) {
    ?????????? System.out.println(msg);
    ?????? }????????
    ????}
    ??????????

      現在兩個系統也相應的做了修改:
    ????class ZipMainFrame extends Frame {
    ?????? StatusBar sb;
    ?????? void makeZip() {
    ?????????? String zipFilePath;
    ?????????? String srcFilePaths[];
    ?????????? //根據UI上給zipFilePath和srcFilePaths賦值
    ?????????? ...??
    ?????????? ZipEngine ze = new ZipEngine();
    ?????????? ze.makeZip(zipFilePath, srcFilePaths, new ZipMainFrameMessageDisplay(this));
    ?????? }????????
    ?????? void setStatusBarText(String statusText) {
    ?????????? sb.setText(statusText);
    ?????? }????????
    ????}????????
    ????
    ????class TextModeApp {
    ?????? void makeZip() {
    ?????????? String zipFilePath;
    ?????????? String srcFilePaths[];
    ?????????? ...??
    ?????????? ZipEngine ze = new ZipEngine();
    ?????????? ze.makeZip(zipFilePath, srcFilePaths, new SystemOutMessageDisplay());
    ?????? }????????
    ????}
    ??????????

    改進后的代碼

    下面就是改進完的代碼。為了讓代碼看起來清楚一些,我們用了Java的內類:
    ????interface MessageDisplay {
    ?????? void showMessage(String msg);??????????????????????????????????????????????????????????????
    ????}??????????????????????????????????????????????????????????????????????????????????????????????

    ????class ZipEngine {??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip(String zipFilePath, String srcFilePaths[], MessageDisplay??????????????????????
    ????msgDisplay) {??????????????????????????????????????????????????????????????????????????????????
    ?????????? //在該路徑上創建zip文件????????????????????????????????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? for (int i = 0; i < srcFilePaths.length; i++) {????????????????????????????????????????
    ?????????????? //將srcFilePaths[i]的文件加到壓縮包中??????????????????????????????????
    ?????????????? ...????????????????????????????????????????????????????????????????????????????????
    ?????????????? msgDisplay.showMessage("Zipping "+srcFilePaths[i]);????????????????????????????????
    ?????????? }??????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}????????????
    ????
    ????class ZipMainFrame extends Frame {????????????????????????????????????????????????????????????
    ?????? StatusBar sb;??????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip() {????????????????????????????????????????????????????????????????????????????
    ?????????? String zipFilePath;????????????????????????????????????????????????????????????????????
    ?????????? String srcFilePaths[];??????????????????????????????????????????????????????????????????
    ?????????? //根據UI上給zipFilePath和srcFilePaths賦值??????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? ZipEngine ze = new ZipEngine();????????????????????????????????????????????????????????
    ?????????? ze.makeZip(zipFilePath, srcFilePaths, new MessageDisplay() {????????????????????????????
    ?????????????? void showMessage(String msg) {??????????????????????????????????????????????????????
    ??????????????????setStatusBarText(msg);??????????????????????????????????????????????????????????
    ?????????????? }??????????????????????????????????????????????????????????????????????????????????
    ?????????? });????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ?????? void setStatusBarText(String statusText) {??????????????????????????????????????????????????
    ?????????? sb.setText(statusText);????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}????????????
    ????
    ????class TextModeApp {????????????????????????????????????????????????????????????????????????????
    ?????? void makeZip() {????????????????????????????????????????????????????????????????????????????
    ?????????? String zipFilePath;????????????????????????????????????????????????????????????????????
    ?????????? String srcFilePaths[];??????????????????????????????????????????????????????????????????
    ?????????? ...????????????????????????????????????????????????????????????????????????????????????
    ?????????? ZipEngine ze = new ZipEngine();????????????????????????????????????????????????????????
    ?????????? ze.makeZip(zipFilePath, srcFilePaths, new MessageDisplay() {????????????????????????????
    ?????????????? void showMessage(String msg) {??????????????????????????????????????????????????????
    ??????????????????System.out.println(msg);????????????????????????????????????????????????????????
    ?????????????? }??????????????????????????????????????????????????????????????????????????????????
    ?????????? });????????????????????????????????????????????????????????????????????????????????????
    ?????? }??????????????????????????????????????????????????????????????????????????????????????????
    ????}
    ??????????????????????????????????????????????????????????????????????????????????????????????

    引述????????????????????????????????????????????????????????????????????????????????????????

     依賴反轉原則(Dependency Inversion Principle )表述:抽象不應該依賴于具體,高層的比較抽象的類不應該依賴于低層的比較具體的類。當這種問題出現的時候,我們應該抽取出更抽象的一個概念,然后讓這兩個類依賴于這個抽取出來的概念。更多的信息,可以看:

    http://www.objectmentor.com/resources/articles/dip.pdf??????????????????????????????????????
    http://c2.com/cgi/wiki?DependencyInversionPrinciple??????????

    posted on 2007-01-25 10:41 都市淘沙者 閱讀(147) 評論(0)  編輯  收藏 所屬分類: Java Basic/Lucene/開源資料

    主站蜘蛛池模板: 色欲国产麻豆一精品一AV一免费| 免费看美女被靠到爽| 无人在线观看免费高清| 亚洲午夜日韩高清一区| 久久久久亚洲av无码专区喷水| 亚洲精品伦理熟女国产一区二区 | 激情无码亚洲一区二区三区| 成人无码精品1区2区3区免费看| 四虎成年永久免费网站| av在线亚洲欧洲日产一区二区| 一级毛片免费视频网站| 亚洲免费黄色网址| 亚洲色欲色欲www| 久久精品国产免费| 成人亚洲性情网站WWW在线观看 | 黄色一级免费网站| 成年女性特黄午夜视频免费看| 久久精品国产亚洲AV久| 老司机午夜精品视频在线观看免费| 大学生一级特黄的免费大片视频 | 国产无遮挡吃胸膜奶免费看| 亚洲综合视频在线观看| 你懂的网址免费国产| 国产成人毛片亚洲精品| a级大片免费观看| 亚洲精品在线播放视频| 日韩中文字幕在线免费观看| 亚洲av日韩av无码av| 日韩a在线观看免费观看| 成人A毛片免费观看网站| 亚洲码一区二区三区| 日韩免费视频在线观看| 中文字幕在线观看免费| 亚洲午夜精品国产电影在线观看| 嫩草影院在线播放www免费观看| 亚洲视频2020| 免费在线看v网址| 亚洲人xxx日本人18| 亚洲男人av香蕉爽爽爽爽| 亚洲中文字幕人成乱码| 一个人看的在线免费视频|