面向?qū)ο罅笤O(shè)計(jì)原則

目錄

最新在閱讀《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》一書(shū)桌吃,我覺(jué)得寫(xiě)的很清晰,每一個(gè)知識(shí)點(diǎn)都有示例苞轿,通過(guò)示例更加容易理解茅诱。書(shū)中的知識(shí)點(diǎn)有些都接觸過(guò),有的沒(méi)有接觸過(guò)搬卒,總之瑟俭,通過(guò)閱讀這本書(shū)來(lái)梳理一下知識(shí)點(diǎn),可能有些東西在項(xiàng)目中一直在使用秀睛,然并不能籠統(tǒng)尔当,清理的說(shuō)明理解它。本文主要是記錄閱讀這本書(shū)的知識(shí)點(diǎn)和自己的一些理解蹂安。一來(lái)整理知識(shí)點(diǎn),二來(lái)方便以后查看锐帜,快速定位田盈。

單一職責(zé)原則 :優(yōu)化代碼第一步

單一職責(zé)原則(英文簡(jiǎn)稱:SRP):對(duì)于一個(gè)類而言,應(yīng)該僅有一個(gè)引起它變化的原因缴阎。這個(gè)有點(diǎn)抽象允瞧,因?yàn)樵撛瓌t的劃分界面并不是那么清晰,很多時(shí)候靠個(gè)人經(jīng)驗(yàn)來(lái)區(qū)分蛮拔。簡(jiǎn)單來(lái)說(shuō)就是一個(gè)類只負(fù)責(zé)一個(gè)功能述暂,比如加減乘除應(yīng)分別對(duì)應(yīng)一個(gè)類,而不是把四個(gè)功能放在一個(gè)類中建炫,這樣在只要有一個(gè)功能變化都需要更改這個(gè)類畦韭。

下面以實(shí)現(xiàn)一個(gè)圖片加載器(ImageLoader)來(lái)說(shuō)明:

public class ImageLoader {

    //圖片內(nèi)存緩存
    private LruCache<String,Bitmap> mImageCache;
    //線程池,線程數(shù)量為CPU的數(shù)量
    private ExecutorService mExecutorService = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors()
    );

    public ImageLoader(){
        initImageLoader();
    }

    //初始化
    private void initImageLoader() {
        //計(jì)算最大的可使用內(nèi)存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        //取四分之一作為最大緩存內(nèi)存
        final int cacheSize = maxMemory / 4;
        mImageCache = new LruCache<String,Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }

    public void displayImage(final String url, final ImageView imageView){
        Bitmap bitmap = mImageCache.get(url);
        if(bitmap != null){
            imageView.setImageBitmap(bitmap);
            return;
        }
        imageView.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(url);
                if(bitmap == null){
                    return;
                }
                if (imageView.getTag().equals(url)) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(url,bitmap);
            }
        });

    }
}

我們一般都會(huì)這樣這樣簡(jiǎn)單的實(shí)現(xiàn)一個(gè)圖片加載工具類肛跌,這樣寫(xiě)功能雖然實(shí)現(xiàn)了艺配,但是代碼是有問(wèn)題的,代碼耦合嚴(yán)重衍慎,隨著ImageLoader功能越來(lái)越多转唉,這個(gè)類會(huì)越來(lái)越大,代碼越來(lái)越復(fù)雜稳捆。按照單一職責(zé)原則赠法,我們應(yīng)該把ImageLoader拆分一下,把各個(gè)功能獨(dú)立出來(lái)乔夯。

ImageLoader修改代碼如下:

public class ImageLoader {
    //圖片緩存
    ImageCache mImageCache = new ImageCache();
    //線程池砖织,線程數(shù)量為CPU的數(shù)量
    private ExecutorService mExecutorService = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors()
    );
    

    public void displayImage(final String url, final ImageView imageView){
         .............
    }
}
public class ImageCache {

    //圖片內(nèi)存緩存
    private LruCache<String,Bitmap> mImageCache;

    public ImageCache(){
        initImageCache();
    }

    //初始化
    private void initImageCache() {
        //計(jì)算最大的可使用內(nèi)存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        //取四分之一作為最大緩存內(nèi)存
        final int cacheSize = maxMemory / 4;
        mImageCache = new LruCache<String,Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }

    public Bitmap get(String url){
        return mImageCache.get(url);
    }

    public void put(String url,Bitmap bitmap){
        mImageCache.put(url,bitmap);
    }
}

上述代碼將ImageLoader一分為二款侵,ImageLoader只負(fù)責(zé)圖片加載的邏輯,ImageCache負(fù)責(zé)緩存策略镶苞,這樣喳坠,ImageLoader的代碼變少了,職責(zé)也清晰了茂蚓,并且如果緩存策略改變了的話壕鹉,只需要修改ImageCache而不需要在修改ImageLoader了。從這個(gè)例子可以更加清晰的理解什么是單一職責(zé)原則聋涨,如何去劃分一個(gè)類的職責(zé)晾浴,每個(gè)人的看法不同,這需要根據(jù)個(gè)人的經(jīng)驗(yàn)牍白,業(yè)務(wù)邏輯而定脊凰。

開(kāi)閉原則:讓程序更穩(wěn)定,更靈活

開(kāi)閉原則(英文縮寫(xiě)為OCP): 軟件中的對(duì)象(類茂腥,函數(shù)狸涌,模塊等)對(duì)于擴(kuò)展是開(kāi)放的,但是對(duì)于修改是封閉的最岗。在軟件的生命周期內(nèi)帕胆,因?yàn)樽兓?jí)和維護(hù)的原因需要對(duì)原代碼修改時(shí)般渡,可能會(huì)將錯(cuò)誤引入已經(jīng)經(jīng)過(guò)測(cè)試的舊代碼懒豹,破壞原有的系統(tǒng)。因?yàn)楫?dāng)需求變化時(shí)驯用,我們應(yīng)盡可能的通過(guò)擴(kuò)展來(lái)實(shí)現(xiàn)脸秽,而不是修改原來(lái)的
代碼。

在實(shí)際的開(kāi)發(fā)過(guò)程中蝴乔,只通過(guò)繼承的方式來(lái)升級(jí)记餐,維護(hù)原有的系統(tǒng)只是一個(gè)理想化的狀態(tài),修改原代碼淘这,擴(kuò)展代碼往往是同時(shí)存在的剥扣。我們應(yīng)盡可能的影響原代碼。避免引入的錯(cuò)誤造成系統(tǒng)的破壞铝穷。

還是上面的那個(gè)例子钠怯,雖然通過(guò)內(nèi)存緩存解決了每次都從網(wǎng)絡(luò)下載圖片的問(wèn)題,但是Android內(nèi)存有限曙聂,并且當(dāng)應(yīng)用重啟后內(nèi)存緩存會(huì)丟失晦炊。我們需要修改一下,增加SD卡緩存,代碼如下:

public class ImageLoader {

    //內(nèi)存緩存
    ImageCache mImageCache = new ImageCache();
    //SD卡緩存
    DiskCache mDiskCache = new DiskCache();
    //線程池断国,線程數(shù)量為CPU的數(shù)量
    private ExecutorService mExecutorService = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors()
    );

    public void displayImage(final String url, final ImageView imageView){
        //先從內(nèi)存緩存中讀取贤姆,如果沒(méi)有再?gòu)腟D卡中讀取
        Bitmap bitmap = mImageCache.get(url);
        if(bitmap == null){
            bitmap = mDiskCache.get(url);
        }
        if(bitmap != null){
            imageView.setImageBitmap(bitmap);
            return;
        }
      //從網(wǎng)絡(luò)下載圖片
       ..........
    }
}
public class DiskCache {
    private final static String cacheDir = "sdcard/cache/";

    /* 從緩存中獲取圖片 */
    public Bitmap get(String url){
        return BitmapFactory.decodeFile(cacheDir + url);
    }

    /* 將圖片添加到緩存中 */
    public void put(String url,Bitmap bitmap){
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(cacheDir + url);
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(fileOutputStream != null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上述代碼我們?cè)黾恿薙D卡緩存,我們?cè)陲@示圖片的時(shí)候先判斷內(nèi)存緩存中是否存在如果不存在就在SD卡中找稳衬,否則再?gòu)木W(wǎng)絡(luò)下載霞捡,這樣就會(huì)有一個(gè)問(wèn)題,每增加一個(gè)新的緩存方法薄疚,我們都需要修改原來(lái)的代碼碧信,這樣可能引入Bug,而且會(huì)使原來(lái)的代碼越來(lái)越復(fù)雜,還有用戶也不能自定義緩存方法街夭。我們具體使用哪一種緩存方法是通過(guò)if條件判斷的,條件太多砰碴,是很容易寫(xiě)錯(cuò)的。而且代碼會(huì)越來(lái)越臃腫板丽,并且可擴(kuò)展性差呈枉。可擴(kuò)展性是框架的重要特性之一埃碱。

根據(jù)開(kāi)閉原則猖辫,當(dāng)軟件需求改變的時(shí)候,我們應(yīng)該通過(guò)擴(kuò)展的方式實(shí)現(xiàn)砚殿,而不是修改自己的代碼住册。對(duì)上述代碼進(jìn)行優(yōu)化:

public class ImageLoader {

    //默認(rèn)緩存方式為內(nèi)存緩存
    ImageCache mImageCache = new MemoryCache();

    //線程池,線程數(shù)量為CPU的數(shù)量
    private ExecutorService mExecutorService = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors()
    );
   
    //設(shè)置緩存方式
    public void setImageCache(ImageCache cache){
        mImageCache = cache;
    }

    public void displayImage(final String url, final ImageView imageView){
        //先從緩存中讀取
        Bitmap bitmap = mImageCache.get(url);
        if(bitmap != null){
            imageView.setImageBitmap(bitmap);
            return;
        }
     //網(wǎng)絡(luò)下載圖片
      ............
    }
}
public interface ImageCache {
    Bitmap get(String url);
    void put(String url, Bitmap bitmap);
}

通過(guò)上述代碼我們可以看出瓮具,ImageLoader增加了一個(gè)方法setImageCache,我們可以通過(guò)該方法設(shè)置緩存方式凡人,這就是我們常說(shuō)的依賴注入名党。當(dāng)然我們還可以自定義自己的緩存方式,只需要實(shí)現(xiàn)ImageCache這個(gè)接口即可挠轴。然后再調(diào)用setImageCache這個(gè)方法來(lái)設(shè)置传睹。而不需要修改ImageLoader的代碼。這樣當(dāng)緩存需求改變的時(shí)候我們可以通過(guò)擴(kuò)展的方式來(lái)實(shí)現(xiàn)而不是修改的方法岸晦,這就是所說(shuō)的開(kāi)閉原則欧啤。同時(shí)是ImageLoader的代碼更簡(jiǎn)潔,擴(kuò)展性和靈活性也更高启上。

里氏替換原則:構(gòu)建擴(kuò)展性更好的系統(tǒng)

里氏替換原則(英文縮寫(xiě)為L(zhǎng)SP):所有引用基類的地方都必須能夠透明的使用其子類的對(duì)象邢隧。我們知道面向?qū)ο笥腥筇匦裕悍庋b,繼承和多態(tài)冈在,里氏替換原則就是依賴?yán)^承和多態(tài)這兩大原則倒慧,里氏替換原則簡(jiǎn)單來(lái)說(shuō)就是:只要是父類引用出現(xiàn)的地方都可以替換成其子類的對(duì)象,并且不會(huì)產(chǎn)生任何的錯(cuò)誤和異常。

下面以Android中的Window和View的關(guān)系的例子來(lái)理解里氏替換原則:

//窗口類
public class Window {
    public void show(View child){
        child.draw();
    }
}

建立視圖抽象類纫谅,測(cè)量視圖的寬高為公共代碼炫贤,繪制交給具體的子類去實(shí)現(xiàn)

public abstract class View {
    public abstract void draw();
    public void measure(int width,int height){
        //測(cè)量視圖大小
        ...........
    }
}

//文本類具體實(shí)現(xiàn)
public class TextView extends View {
    @Override
    public void draw() {
        //繪制文本
        ...........
    }
}

//按鈕類具體實(shí)現(xiàn)
public class Button extends View {
    @Override
    public void draw() {
        //繪制按鈕
        .............
    }
}

上述示例中,Window依賴于View付秕,View定義了一個(gè)視圖抽象兰珍,measure是各個(gè)子類共享的方法,子類通過(guò)重寫(xiě)View的draw方法來(lái)實(shí)現(xiàn)具體各自特色的內(nèi)容询吴。任何繼承View的子類都可以設(shè)置給show方法掠河,這就是所說(shuō)的里氏替換原則,通過(guò)里式替換汰寓,就可以自定義各種各樣的口柳,千變?nèi)f化的View,然后傳遞給Window有滑,Window負(fù)責(zé)組織View跃闹,并將View顯示到屏幕上。

上面ImageLoader的例子也體現(xiàn)了里氏替換原則毛好,可以通過(guò)setImageCache方法來(lái)設(shè)置各種各樣的緩存方式望艺,如果 setImageCache中的cache對(duì)象不能被子類替換,那么又怎么能設(shè)置各種各樣的緩存方式呢肌访?

依賴倒置原則:讓項(xiàng)目擁有變化的能力

依賴倒置原則(英文縮寫(xiě)為DIP)指代了一種特定的解耦方式找默,使得高層次的模塊不依賴于低層次模塊的實(shí)現(xiàn)細(xì)節(jié)的目的,依賴模塊被顛倒了吼驶。這個(gè)概念更加的抽象惩激,該怎么理解呢?

依賴倒置原則有幾個(gè)關(guān)鍵的點(diǎn):

  • 1.高層模塊不應(yīng)該依賴底層模塊蟹演,兩者都應(yīng)該依賴其抽象风钻。
  • 2.抽象不應(yīng)該依賴細(xì)節(jié)。
  • 3.細(xì)節(jié)應(yīng)該依賴抽象酒请。

在Java語(yǔ)言中骡技,抽象就是接口或者抽象類,兩者都是不能直接被實(shí)例化的羞反;細(xì)節(jié)就是實(shí)現(xiàn)類布朦,實(shí)現(xiàn)接口或者繼承抽象類而產(chǎn)生的類就是細(xì)節(jié),可以直接實(shí)例化昼窗;高層模塊就是調(diào)用端是趴;底層模塊就是實(shí)現(xiàn)端。

依賴倒置原則在Java語(yǔ)言中的表現(xiàn)就是:模塊間的依賴通過(guò)抽象發(fā)生膏秫,實(shí)現(xiàn)類直接不能直接發(fā)生依賴右遭,其依賴關(guān)系是通過(guò)接口或者抽象類產(chǎn)生的做盅。

如果類與類之間直接依賴于細(xì)節(jié),那么它們之間就有直接的耦合窘哈,當(dāng)需求變化的時(shí)候吹榴,意味著要同時(shí)修改依賴者的代碼。這就限制了系統(tǒng)的可擴(kuò)展性滚婉。

public class ImageLoader {
    //直接依賴于細(xì)節(jié)
    MemoryCache mImageCache = new MemoryCache();
    ...................
}

ImageLoader直接依賴于MemoryCache,MemoryCache是一個(gè)具體的實(shí)現(xiàn)图筹,這就導(dǎo)致ImageLoader直接依賴于細(xì)節(jié),當(dāng)MemoryCache不能滿足而被其他緩存實(shí)現(xiàn)替換時(shí)让腹,就必須需要修改ImageLoader的代碼远剩。

public interface ImageCache {
    Bitmap get(String url);
    void put(String url, Bitmap bitmap);
}

public class ImageLoader {
    //依賴于抽象,并且有一個(gè)默認(rèn)的實(shí)現(xiàn)
    ImageCache mImageCache = new MemoryCache();
    ......................

在這里我們建立了ImageCache抽象骇窍,并且讓ImageLoader直接依賴于抽象而不是具體的細(xì)節(jié)瓜晤,當(dāng)需求變化時(shí),只需要實(shí)現(xiàn)ImageCache或者繼承已有的類來(lái)完成相應(yīng)的緩存功能腹纳。然后再將具體的實(shí)現(xiàn)注入到ImageLoader中痢掠,保證了系統(tǒng)的高擴(kuò)展性。這就是依賴倒置原則嘲恍。

接口隔離原則:讓系統(tǒng)擁有更高的靈活性

接口隔離原則(英文縮寫(xiě)為L(zhǎng)SP):客戶端不應(yīng)該依賴它不需要的接口足画。另外一種定義是:類間的依賴關(guān)系應(yīng)該建立在最小的接口上。接口隔離原則將龐大佃牛,臃腫的接口拆分成更小更具體的接口淹辞,這樣客戶端只需要知道它感興趣的方法。接口隔離的目的是解開(kāi)耦合俘侠,從而容易重構(gòu)更改和重新部署象缀。

接口隔離原則說(shuō)白了就是讓依賴的接口盡可能的小,看一下上個(gè)例子實(shí)現(xiàn)SD卡緩存的代碼:

 /* 將圖片添加到緩存中 */
 public void put(String url,Bitmap bitmap){
     FileOutputStream fileOutputStream = null;
     try {
         fileOutputStream = new FileOutputStream(cacheDir + url);
         bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
     } catch (FileNotFoundException e) {
         e.printStackTrace();
     }finally {
         if(fileOutputStream != null){
             try {
                 fileOutputStream.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
  }

我們看到這段代碼的可讀性非常的差爷速,各種try...catch都是非常簡(jiǎn)單的代碼攻冷,但是會(huì)嚴(yán)重影響代碼的可讀性,并且多層級(jí)的大括號(hào)很容易將代碼寫(xiě)到錯(cuò)誤的層級(jí)中遍希。那么如何解決這樣的問(wèn)題呢?Java中有一個(gè)Closeable接口里烦,該接口標(biāo)識(shí)了一個(gè)可關(guān)閉的對(duì)象凿蒜,它只有一個(gè)close方法。實(shí)現(xiàn)該接口的類有很多胁黑,F(xiàn)ileOutputStream也實(shí)現(xiàn)了該接口废封,當(dāng)程序有有多個(gè)可關(guān)閉的對(duì)象時(shí),如果都像上述代碼那樣在finally中去關(guān)閉丧蘸,就非常的麻煩了漂洋。

我們可以抽取一個(gè)工具類來(lái)專門去關(guān)閉需要關(guān)閉的對(duì)象。

public class CloseUtils {

    /**
     * 關(guān)閉Closeable對(duì)象
     * @param closeable
     */
    public static void closeQuietly(Closeable closeable){
        if(null != closeable){
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

使用工具類替換上述的代碼

public void put(String url,Bitmap bitmap){
     FileOutputStream fileOutputStream = null;
    try {
        fileOutputStream = new FileOutputStream(cacheDir + url);
        bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }finally {
        CloseUtils.closeQuietly(fileOutputStream);
    }
}

這樣代碼就簡(jiǎn)潔多了,并且CloseUtils可以用到多個(gè)可以關(guān)閉對(duì)象的地方刽漂,保證了代碼的重用性演训,它依賴于Closeable抽象而不是具體的實(shí)現(xiàn),并且建立在最小的依賴原則之上贝咙,他只需要知道對(duì)象是否可關(guān)閉样悟,其他的一概不關(guān)心,這就是接口隔離庭猩。如果現(xiàn)在只需要關(guān)閉一個(gè)對(duì)象時(shí)窟她,它卻暴露了其他的接口方法,比如OutputStream的write方法蔼水,這使得更多的細(xì)節(jié)暴露在客戶端面前震糖,還增加了使用難度。而通過(guò)Closeable接口將可關(guān)閉的對(duì)象抽象起來(lái)趴腋,這樣客戶端只需要依賴Closeable就可將其他的細(xì)節(jié)隱藏起來(lái)吊说,客戶端只需要知道這個(gè)對(duì)象可關(guān)閉即可。

在上述的ImageLoader中于样,只需要知道該緩存對(duì)象有讀取和緩存的接口即可疏叨,其他的一概不管,這樣緩存的具體實(shí)現(xiàn)是對(duì)ImageLoader隱藏的穿剖。這就是用最小化接口隔離了實(shí)現(xiàn)類的細(xì)節(jié)蚤蔓。

Robert C Martin在21世紀(jì)早期將單一職責(zé),開(kāi)閉原則糊余,里氏替換秀又,接口隔離和依賴倒置5個(gè)原則定義為SOLID原則,作為面向?qū)ο缶幊痰?個(gè)基本原則贬芥。當(dāng)這些原則在一起使用時(shí)吐辙,它使得一個(gè)軟件系統(tǒng)更清晰,更簡(jiǎn)單蘸劈,最大程度的擁抱變化昏苏。

迪米特原則:更好的可擴(kuò)展性

迪米特原則(英文縮寫(xiě)為L(zhǎng)OD):也稱為最少知識(shí)原則,一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的理解威沫。通俗的講贤惯,一個(gè)類對(duì)自己需要耦合或者調(diào)用的類知道的最少,類的內(nèi)部如果實(shí)現(xiàn)與調(diào)用者或依賴者沒(méi)有關(guān)系棒掠。調(diào)用者或依賴者只需要知道它調(diào)用的方法即可孵构,其他的一概不知。

下面以租房的例子來(lái)理解說(shuō)明這個(gè)原則烟很。

租房大多數(shù)通過(guò)中介來(lái)租颈墅,我們假設(shè)設(shè)定的情景為:我們只要求房子的面積和租金蜡镶,其他的一概不管,中介提供給我們符合要求的房子恤筛。

public class Room {
    
    public float area;
    public float price;

    public Room(float area, float price) {
        this.area = area;
        this.price = price;
    }
}
public class Mediator {
    private List<Room> mRooms = new ArrayList<>();

    public Mediator(){

        for (int i = 0; i < 5; i++) {
            mRooms.add(new Room(14 + i,(14 + i) * 150));
        }
    }

    public List<Room> getRooms(){
        return mRooms;
    }
}
public class Tenant {
    private float roomArea;
    private float roomPrice;
    private static final float diffArea = 0.0001f;
    private static final float diffPrice = 100.0001f;

    public void rentRoom(Mediator mediator){
        List<Room> rooms = mediator.getRooms();
        for (Room room : rooms) {
            if(isSuitable(room)){
                System.out.print("租到房子了" + room.toString());
                break;
            }
        }
    }

    private boolean isSuitable(Room room){
        return Math.abs(room.price - roomPrice) < diffPrice
                && Math.abs(room.area - roomArea) < diffArea;
    }
}

從上面的代碼看出官还,Tenant不僅依賴Mediator,還需要頻繁的與Room打交道叹俏,租戶類只需要通過(guò)中介找到一間符合要求的房子即可妻枕。如果把這些檢索都放在Tenant中,就弱化了中介的作用粘驰,而且導(dǎo)致TenantRoom耦合度較高屡谐。當(dāng)Room變化的時(shí)候,Tenant也必須跟著變化蝌数,而且Tenant還和Mediator耦合愕掏,這樣關(guān)系就顯得有些混亂了。

我們需要根據(jù)迪米特原則進(jìn)行解耦顶伞。

public class Mediator {
    private List<Room> mRooms = new ArrayList<>();

    public Mediator(){

        for (int i = 0; i < 5; i++) {
            mRooms.add(new Room(14 + i,(14 + i) * 150));
        }
    }

    public Room rentOut(float price,float area){
        for (Room room : mRooms) {
            if (isSuitable(price,area,room)) {
                return room;
            }
        }
        return null;
    }

    private boolean isSuitable(float price,float area,Room room){
        return Math.abs(room.price - price) < Tenant.diffPrice
                && Math.abs(room.area - area) < Tenant.diffArea;
    }
}
public class Tenant {
    
    private float roomArea;
    private float roomPrice;
    public static final float diffArea = 0.0001f;
    public static final float diffPrice = 100.0001f;

    public void rentRoom(Mediator mediator) {
        Room room = mediator.rentOut(roomPrice, roomArea);
        if(null != room){
            System.out.print("租到房子了" + room.toString());
        }
    }

}

我們將對(duì)Room的操作移到了Mediator中饵撑,這本來(lái)就是Mediator的職責(zé),根據(jù)租戶的條件檢索符合的房子唆貌,并且將房子返回給用戶即可滑潘。這樣租戶就不需要知道有關(guān)Room的細(xì)節(jié),比如和房東簽合同锨咙,房產(chǎn)證的真?zhèn)蔚扔锫薄V恍枰P(guān)注和我們相關(guān)的即可。

總結(jié)

在應(yīng)用開(kāi)發(fā)過(guò)程中酪刀,我們不僅要完成應(yīng)用的開(kāi)發(fā)工作粹舵,還需要在后續(xù)的升級(jí),維護(hù)中讓?xiě)?yīng)用系統(tǒng)能夠擁抱變化骂倘。擁抱變化意味著在滿足需求且不破壞系統(tǒng)穩(wěn)定的前提下保持高擴(kuò)展性眼滤,高內(nèi)聚,低耦合历涝,在經(jīng)歷了各個(gè)版本變更之后依然保持清晰诅需,靈活,穩(wěn)定的系統(tǒng)架構(gòu)荧库。那么遵守面向?qū)ο蟮牧笤瓌t是我們邁向的第一步诱担。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市电爹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌料睛,老刑警劉巖丐箩,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摇邦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡屎勘,警方通過(guò)查閱死者的電腦和手機(jī)施籍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)概漱,“玉大人丑慎,你說(shuō)我怎么就攤上這事∪看荩” “怎么了竿裂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)照弥。 經(jīng)常有香客問(wèn)我腻异,道長(zhǎng),這世上最難降的妖魔是什么这揣? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任悔常,我火速辦了婚禮,結(jié)果婚禮上给赞,老公的妹妹穿的比我還像新娘机打。我一直安慰自己,他們只是感情好片迅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布残邀。 她就那樣靜靜地躺著,像睡著了一般障涯。 火紅的嫁衣襯著肌膚如雪罐旗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天唯蝶,我揣著相機(jī)與錄音九秀,去河邊找鬼。 笑死粘我,一個(gè)胖子當(dāng)著我的面吹牛鼓蜒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播征字,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼都弹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了匙姜?” 一聲冷哼從身側(cè)響起畅厢,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎氮昧,沒(méi)想到半個(gè)月后框杜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體浦楣,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年咪辱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了振劳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡油狂,死狀恐怖历恐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情专筷,我是刑警寧澤弱贼,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站仁堪,受9級(jí)特大地震影響哮洽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弦聂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一鸟辅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莺葫,春花似錦匪凉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至堡纬,卻和暖如春聂受,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烤镐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蛋济, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炮叶。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓碗旅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親镜悉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祟辟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容