面向?qū)ο罅蠡驹瓌t - 網(wǎng)絡(luò)引擎切換

仍記得前年的這個(gè)時(shí)候我去一個(gè)新的公司報(bào)道,公司總共就十來個(gè)人做開發(fā),剛好做 android 的那哥們離職,就丟了一個(gè)項(xiàng)目給我。后面的日子我算是飽受煎熬,今天我們以一個(gè)訪問網(wǎng)絡(luò)的部分,來給大家做一下講解。里面每次獲取接口數(shù)據(jù)是這樣的:

請求后臺數(shù)據(jù).png

當(dāng)時(shí)我看到這個(gè)的時(shí)候覺得沒什么,只是我想換一下網(wǎng)絡(luò)請求的框架,想把它換成 OKHttp ,我一直不喜歡用 xUtils。但后來想想還是算了,好幾十個(gè)地方那我不都得去改?得過且過吧。過了一段時(shí)間后臺突然說,我們需要加個(gè)平臺參數(shù)來區(qū)別到底是 android 端還是 iOS 端,每個(gè)接口都多帶個(gè)參數(shù) platform = android。聽到這個(gè)的我眼淚掉下來,默默無聞的回家找了好幾個(gè)小時(shí),所有訪問接口都統(tǒng)一加了個(gè)參數(shù)。第二天早上我其實(shí)就想到,我為什么不去改 xUtils 的源代碼呢?看來昨天那幾個(gè)小時(shí)白忙活了。這個(gè)時(shí)候我其實(shí)還沒有意識到我要去更換整個(gè)網(wǎng)絡(luò)架構(gòu),盡管我知道后面還是會有問題,得過且過吧。剛過幾天測試發(fā)現(xiàn)有些頁面有時(shí)候會崩掉有時(shí)候又不會,我一看后臺接口返回的數(shù)據(jù)接口尼瑪...
有數(shù)據(jù)的情況下:

無數(shù)據(jù)的情況下:

我記得我當(dāng)時(shí)想去找后臺,讓他幫幫忙,但是問問 iOS 的那哥們他說他做了一些處理。我心里在想我不能就這么暴露我自己,因此下定決心要自己來封裝。所以就出現(xiàn)了我接下來要寫的這篇文章,我記得我在內(nèi)涵段子項(xiàng)目部分寫過這個(gè)內(nèi)容,但是那時(shí)是授人以魚,這次是要授人以漁。說明我不是亂寫還是有很扎實(shí)的理論依據(jù),這是理論結(jié)合實(shí)戰(zhàn)的年代,當(dāng)然我也不會寫得太完善很多問題會留作思考,授人以魚不如授人以漁。

1.一般寫法

上面這種寫法應(yīng)該只有學(xué)校的老師才會教我們這樣寫吧,我以 OKHttp 為例,這種我就不寫了,寫一個(gè)稍微好一點(diǎn)的級別,請看初步版本:

調(diào)用部分

        Map<String, Object> params = new HashMap<>();
        // 特定參數(shù)
        params.put("iid", 6152551759L);
        params.put("aid", 7);

        HttpUtils.get(this, ConstantValue.UrlConstant.HOME_DISCOVERY_URL,
                params, true, new HttpCallBack<DiscoverListResult>() {
                    @Override
                    public void onSuccess(DiscoverListResult result) {
                        if (result.isOK()) {
                            // 沒有列表數(shù)據(jù)的情況, 打印 Toast 或者做一些其他處理
                        } else {
                            // 有數(shù)據(jù)列表的情況,顯示列表
                            showListData(result);
                        }
                    }

                    @Override
                    public void onFailure(Exception e) {

                    }
                });

封裝部分

    /**
     * get 請求數(shù)據(jù)列表
     * @param context 上下文
     * @param url 訪問路徑
     * @param params 訪問參數(shù)
     * @param cache 是否緩存
     * @param callback 數(shù)據(jù)解析回調(diào)
     * @param <T> 解析類的泛型
     */
    public static <T> void get(Context context, String url, Map<String, Object> params, final boolean cache, final HttpCallBack<T> callback) {
        OkHttpClient mOkHttpClient = new OkHttpClient();
        // 公共參數(shù)拼接
        params.put("app_name", "joke_essay");
        params.put("version_name", "5.7.0");
        params.put("ac", "wifi");
        params.put("device_id", "30036118478");
        params.put("device_brand", "Xiaomi");
        params.put("update_version_code", "5701");
        params.put("manifest_version_code", "570");
        params.put("longitude", "113.000366");
        params.put("latitude", "28.171377");
        params.put("device_platform", "android");

        final String jointUrl = Utils.jointParams(url, params);  //打印

        // 緩存邏輯的一些處理

        Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(context);
        //可以省略,默認(rèn)是GET請求
        Request request = requestBuilder.build();
        // 異步請求數(shù)據(jù)
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                // 失敗
                callback.onFailure(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String resultJson = response.body().string();
                // json 轉(zhuǎn)換,有無數(shù)據(jù)列表,緩存處理
            }
        });
    }

這個(gè)看上去就稍微好一丁點(diǎn)了,至少也能解決掉一些問題,當(dāng)然有些哥們估計(jì)是用的別人封裝好的那就另當(dāng)別論了,其實(shí)前年網(wǎng)上對 OKHttp 的封裝還是比較少,關(guān)鍵不是不想而是很少。

2.鏈?zhǔn)秸{(diào)用

隨著項(xiàng)目越來越大功能越來越強(qiáng),這明顯滿足不了功能,而且人員也越來越多的情況下,吐槽隨之而來,比如不支持 cookie , https 證書,超時(shí)斷線重連等等。好吧硬著頭皮去加了一下功能,這個(gè)時(shí)候問題就來了,就是一個(gè)方法要傳的參數(shù)一大堆,后面要跟十幾個(gè)參數(shù),而有些參數(shù)我又不想要,只能全都傳 null 。第一,成員調(diào)用非常蛋疼需要對照你的參數(shù)一個(gè)一個(gè)去看,第二,可閱讀并不好并不美觀,所以又做了如下修改,最終調(diào)用就變成了如下方式:

         HttpUtils.with(this)
                .get() // get請求
                .param("aid", 7)// 添加單個(gè)參數(shù),params 可以添加多個(gè)參數(shù)
                .param("iid", 6152551759L)
                .url(ConstantValue.UrlConstant.HOME_DISCOVERY_URL)// 接口地址
                .setCookie(true)//配置是否使用cookie
                .cache(true)//設(shè)置是否使用緩存,如果使用緩存必須設(shè)置為true
                .retryDelayMillis(1000)//配置請求失敗重試間隔時(shí)間,單位毫秒
                .retryCount(3)//配置請求失敗重試次數(shù)
                .execute(new HttpCallBack<DiscoverListResult>() {
                    @Override
                    public void onSuccess(DiscoverListResult result) {
                        if (result.isOK()) {
                            // 沒有列表數(shù)據(jù)的情況, 打印 Toast 或者做一些其他處理
                        } else {
                            // 有數(shù)據(jù)列表的情況,顯示列表
                            showListData(result);
                        }
                    }

                    @Override
                    public void onFailure(Exception e) {

                    }
                });

3.單一職責(zé)原則

這時(shí)技術(shù)總監(jiān)就上來問了,目前所有的功能都寫在一個(gè)類里怎么行呢,這樣隨著功能的增多,HttpUtils 類會越來越大,代碼也越來越復(fù)雜。這樣一看我的 HttpUtils 簡直就沒有設(shè)計(jì)可言,更不要說擴(kuò)展性、靈活性了。網(wǎng)絡(luò)加載封裝就越來越脆弱…… 回家想了半天,那我多拆幾個(gè)類出來不就行了?到底怎么拆這個(gè)時(shí)候 單一職責(zé)就來了。

單一職責(zé)原則的英文名稱是Single Responsibility Principle,簡稱SRP。它的定義是:就一個(gè)類而言,應(yīng)該僅有一個(gè)引起它變化的原因。簡單來說,一個(gè)類中應(yīng)該是一組相關(guān)性很高的函數(shù)、數(shù)據(jù)的封裝。這個(gè)在 《Android 源碼設(shè)計(jì)模式》那本書中講得很清楚,學(xué)了就要用,到底是什么意思?請看我如何拆分,貼一些事例代碼。

單一職責(zé)拆分.png

/**
 * Email 240336124@qq.com
 * Created by Darren on 2017/3/12.
 * Version 1.0
 * Description:
 */
public class HttpCache {
    /**
     * 獲取數(shù)據(jù)
     */
    public String getCache(String finalUrl) {
         // 省略一些代碼 ...
    }

    /**
     * 緩存數(shù)據(jù)
     */
    public long saveCache(String finalUrl, String resultJson) {
         // 省略一些代碼 ...
    }
}
public class HttpUtils {
    private OKHttpRequest mHttpRequest;

    private HttpUtils(Context context) {
        mHttpRequest = new OKHttpRequest();
        mHttpRequest.with(context);
    }

     // 省略一些代碼 ...
}

3.開閉原則

代碼提交之后感覺思路是對的,后來一想最根本的問題還是沒有解決,我當(dāng)初的第一感覺是想把 xutils 換成 OkHttp,但是我沒想過要去改每個(gè)接口,比如后來接著 Retrofit 又出來了,以后還指不定要出一些什么,我想能不能再寫得強(qiáng)大一些,可以切換,而不用改動(dòng)原來的代碼。到底應(yīng)該怎么辦?開閉原則就來了。

開閉原則的英文全稱是Open Close Principle,簡稱OCP,它是Java世界里最基礎(chǔ)的設(shè)計(jì)原則,它指導(dǎo)我們?nèi)绾谓⒁粋€(gè)穩(wěn)定的、靈活的系統(tǒng)。開閉原則的定義是:軟件中的對象(類、模塊、函數(shù)等)應(yīng)該對于擴(kuò)展是開放的,但是,對于修改是封閉的。我的理解是對于原來寫好的代碼里面是不可修改,但是對于外部又是可擴(kuò)展的。理解起來還是有點(diǎn)抽象,結(jié)合一下我的代碼就會好些,又要改了其實(shí)就是多了一個(gè)接口而已。

開閉原則.png

/**
 * description:
 * author: Darren on 2017/8/21 11:36
 * email: 240336124@qq.com
 * version: 1.0
 */
public class HttpUtils {
    // 這個(gè)可以在 application 中去初始化
    private static IHttpRequest mInitHttpRequest;
    private IHttpRequest mHttpRequest;
    public static void initHttpRequest(IHttpRequest httpRequest) {
        mInitHttpRequest = httpRequest;
    }
    // 如果有兩種的情況下 比如 volley 下載文件并不是很屌 ,那么可以換成 OKHttp 
    public HttpUtils httpRequest(IHttpRequest httpRequest) {
        this.mHttpRequest = httpRequest;
        return this;
    }
    // 省略部分代碼 ......
    public <T> void execute(HttpCallBack<T> callback) {
        // 如果沒有指定,那么就用 application 中初始化的
        if(mHttpRequest == null){
            mHttpRequest = mInitHttpRequest;
        }
        mHttpRequest.get(mContext, mParams, mUrl, mCache, callback);
    }
}

IHttpRequest 代碼

/**
 * description:
 * author: Darren on 2017/8/24 11:34
 * email: 240336124@qq.com
 * version: 1.0
 */
public interface IHttpRequest {
    /**
     * post 提交
     *
     * @param context
     * @param params
     * @param url
     * @param cache
     * @param callback
     * @param <T>
     */
    <T> void post(Context context, Map<String, Object> params, String url, final boolean cache, final HttpCallBack<T> callback);

    /**
     * get 提交
     *
     * @param context
     * @param params
     * @param url
     * @param cache
     * @param callback
     * @param <T>
     */
    <T> void get(Context context, Map<String, Object> params, String url, final boolean cache, final HttpCallBack<T> callback);
}

OKHttpRequest 代碼

/**
 * description:
 * author: Darren on 2017/8/24 10:32
 * email: 240336124@qq.com
 * version: 1.0
 */
public class OKHttpRequest implements IHttpRequest {
    private HttpCache mHttpCache;
    private OkHttpClient mOkHttpClient;

    public OKHttpRequest() {
        mHttpCache = new HttpCache();
        mOkHttpClient = new OkHttpClient();
    }

    @Override
    public <T> void post(Context context, Map<String, Object> params, String url, boolean cache, HttpCallBack<T> callback) {
        // 省略部分代碼 ......
    }

    public <T> void get(Context context, Map<String, Object> params, String url, final boolean cache, final HttpCallBack<T> callback) {
       // 省略部分代碼 ......
    }
}

XUtilsRequest 代碼

/**
 * description:
 * author: Darren on 2017/8/24 10:32
 * email: 240336124@qq.com
 * version: 1.0
 */
public class XUtilsRequest implements IHttpRequest {
    private HttpCache mHttpCache;

    public XUtilsRequest() {
        mHttpCache = new HttpCache();
    }

    @Override
    public <T> void post(Context context, Map<String, Object> params, String url, boolean cache, HttpCallBack<T> callback) {
        // 省略部分代碼 ......
    }

    public <T> void get(Context context, Map<String, Object> params, String url, final boolean cache, final HttpCallBack<T> callback) {
        // 省略部分代碼 ...... 
    }
}

Application 代碼

/**
 * description:
 * author: Darren on 2017/8/21 15:05
 * email: 240336124@qq.com
 * version: 1.0
 */
public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        PreferencesUtil.getInstance().init(this);
        x.Ext.init(this);
        // 初始化指定網(wǎng)絡(luò)請求  
        HttpUtils.initHttpRequest(new XUtilsRequest());
    }
}

開閉原則指導(dǎo)我們,當(dāng)軟件需要變化時(shí),應(yīng)該盡量通過擴(kuò)展的方式來實(shí)現(xiàn)變化,而不是通過修改已有的代碼來實(shí)現(xiàn)。我們盡量不要通過繼承等方式添加新的實(shí)現(xiàn),這會導(dǎo)致類型的膨脹以及歷史遺留代碼的冗余。我們的開發(fā)過程中也沒有那么理想化的狀況,完全地不用修改原來的代碼,因此,在開發(fā)過程中需要自己結(jié)合具體情況進(jìn)行考量,是通過修改舊代碼還是通過繼承使得軟件系統(tǒng)更穩(wěn)定、更靈活,在保證去除“代碼腐化”的同時(shí),也保證原有模塊的正確性。當(dāng)然如果等我們了解了 Builder 設(shè)計(jì)模式或者加入其他的一些設(shè)計(jì)模式之后我們肯定可以寫得更強(qiáng)大和完善。我們接著往下面看。

4.里氏替換原則

里氏替換原則英文全稱是Liskov Substitution Principle,簡稱LSP。我們知道,面向?qū)ο蟮恼Z言的三大特點(diǎn)是繼承、封裝、多態(tài),里氏替換原則就是依賴于繼承、多態(tài)這兩大特性。里氏替換原則簡單來說就是,所有引用基類的地方必須能透明地使用其子類的對象。通俗點(diǎn)講,只要父類能出現(xiàn)的地方子類就可以出現(xiàn)。但是,反過來就不行了,有子類出現(xiàn)的地方,父類未必就能適應(yīng)。 我們簡單的看幾個(gè)使用場景,在我們的開發(fā)過程中無所不在。

// 1. 今天所寫的初始化請求
HttpUtils.initHttpRequest(new XUtilsRequest());
HttpUtils.initHttpRequest(new OKHttpRequest());
// 2. RecyclerView 的 LayoutMananger
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
// 3. Retrofit 添加解析工廠 等等 

使用的地方非常之多,setLayoutManager 的源碼大家可以自己去了解一下,上面的代碼就很好的反應(yīng)了里氏替換原則,XUtilsRequest、OKHttpRequest 都可以替換 IHttpRequest 的工作。IHttpRequest 建立了 post 請求,get 請求,上傳,下載的接口規(guī)范,XUtilsRequest 等根據(jù)接口規(guī)范實(shí)現(xiàn)了相應(yīng)的功能,用戶只需要在 Application 中指定具體的對象就可以動(dòng)態(tài)地替換 IHttpRequest 中的請求。這就使得網(wǎng)絡(luò)請求具有了無線的可能性,也就是保證了可擴(kuò)展性。

里氏替換原則和開閉原則有點(diǎn)相似,但仔細(xì)理解他們之間是不同的概念,只能夠說是有時(shí)候開閉原則和里氏替換原則往往生死相依、不棄不離,通過里氏替換來達(dá)到對擴(kuò)展開放,對修改關(guān)閉的效果,這兩個(gè)原則其實(shí)就是面向?qū)ο笏枷胫械?strong>抽象。

5.依賴倒置原則

依賴倒置原則英文全稱是Dependence Inversion Principle,簡稱DIP。依賴反轉(zhuǎn)原則指代了一種特定的解耦形式,高層模塊不依賴低層次模塊的細(xì)節(jié),說白了高層次就是不依賴細(xì)節(jié)而是依賴抽象。那什么又是低層次什么是高層次?拿上面開閉原則那張圖來講,HttpUtils 是高層次,IHttpRequest、XUtilsRequest 和 OKHttpRequest 是低層次。剛開始 HttpUtils 是這么寫的:

public class HttpUtils {
    private OKHttpRequest mHttpRequest;

    private HttpUtils(Context context) {
        mHttpRequest = new OKHttpRequest();
        mHttpRequest.with(context);
    }

     // 省略一些代碼 ...
}

這個(gè)時(shí)候我們依賴的是具體的 OKHttpRequest,這種情況下很明顯我們依賴的是具體的細(xì)節(jié), 在開閉原則過后,我們 HttpUtils 是這么寫的。

/**
 * description:
 * author: Darren on 2017/8/21 11:36
 * email: 240336124@qq.com
 * version: 1.0
 */
public class HttpUtils {
    // 這個(gè)可以在 application 中去初始化
    private static IHttpRequest mInitHttpRequest;
    private IHttpRequest mHttpRequest;
    public static void initHttpRequest(IHttpRequest httpRequest) {
        mInitHttpRequest = httpRequest;
    }
    // 如果有兩種的情況下 比如 volley 下載文件并不是很屌 ,那么可以換成 OKHttp 
    public HttpUtils httpRequest(IHttpRequest httpRequest) {
        this.mHttpRequest = httpRequest;
        return this;
    }
    // 省略部分代碼 ......
    public <T> void execute(HttpCallBack<T> callback) {
        // 如果沒有指定,那么就用 application 中初始化的
        if(mHttpRequest == null){
            mHttpRequest = mInitHttpRequest;
        }
        mHttpRequest.get(mContext, mParams, mUrl, mCache, callback);
    }
}

這個(gè)時(shí)候我們依賴的就已經(jīng)不在是具體的細(xì)節(jié)了,而是抽象的 IHttpRequest ,具體的實(shí)現(xiàn)我們是在 Application 中配置的,可以配置 Okhttp 或者 xUtils 等等。從上面這幾個(gè)來看要讓整個(gè)系統(tǒng)更加靈活,似乎一直都是抽象的功勞。

6.接口隔離原則

接口隔離原則英文全稱是InterfaceSegregation Principles,簡稱ISP。它的定義是:客戶端不應(yīng)該依賴它不需要的接口。另一種定義是:類間的依賴關(guān)系應(yīng)該建立在最小的接口上。接口隔離原則將非常龐大、臃腫的接口拆分成為更小的和更具體的接口,這樣客戶將會只需要知道他們感興趣的方法。接口隔離原則的目的是系統(tǒng)解開耦合,從而容易重構(gòu)、更改和重新部署,讓客戶端依賴的接口盡可能地小。

拿我最近在做的一個(gè)項(xiàng)目來說,我用手機(jī)去調(diào)用相機(jī)拍照,源碼我是沒有的,后面想了很多辦法把相機(jī)破解了,那么我獲取到的數(shù)據(jù)后需及時(shí)傳給后臺,這個(gè)時(shí)候建立了一個(gè) Socket 通信機(jī)制,我在 Activity 的 onDestory 中斷開 Socket 連接我是這么寫的:

    /**
    * 中斷鏈接
    */
    private void breakConnection() {
        mReadFlag = false;
        // 關(guān)閉輸入流
        if (mInStream != null) {
            try {
                mInStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 關(guān)閉輸出流
        if (mOutStream != null) {
            try {
                mOutStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 關(guān)閉Socket
        if (mSocket != null) {
            try {
                mSocket.close();
                mSocket = null;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

這段代碼可能我們平時(shí)都這么寫,各種try…catch嵌套,都是些簡單的代碼,但是會嚴(yán)重影響代碼的可讀性,并且多層級的大括號很容易將代碼寫到錯(cuò)誤的層級中。大家應(yīng)該對這類代碼也非常反感,那我們看看如何解決這類問題。我們看源碼發(fā)現(xiàn)他們都有一個(gè) close 方法,而且這個(gè)方法是 Closeable 接口的,也就是說上面的這幾個(gè)類都是實(shí)現(xiàn)了 Closeable 接口,該接口標(biāo)識了一個(gè)可關(guān)閉的對象。這意味著,在關(guān)閉這一百多個(gè)類型的對象時(shí),都需要寫出上面這樣的這些代碼,而且并沒有什么實(shí)際的意義。這還了得!既然有共性,那么我們可以再寫一個(gè)方法:

    /**
    * 中斷鏈接
    */
    private void breakConnection() {
        mReadFlag = false;
        // 關(guān)閉輸入流
        close(mInStream);
        // 關(guān)閉輸出流
        close(mOutStream);
        // 關(guān)閉Socket
        close(mSocket);
    }

    /**
    * 關(guān)閉 Closeable
    * @param closeable
    */
    private void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

代碼簡潔了很多!保證了代碼的重用性。close 方法的基本原理就是依賴于 Closeable 抽象而不是具體實(shí)現(xiàn)(這其實(shí)也是依賴倒置),并且建立在最小化依賴原則的基礎(chǔ),它只需要知道這個(gè)對象是可關(guān)閉,其他的一概不關(guān)心,也就是這里的接口隔離原則。

Bob大叔(Robert C Martin)在21世紀(jì)早期將單一職責(zé)、開閉原則、里氏替換、接口隔離以及依賴倒置(也稱為依賴反轉(zhuǎn))5個(gè)原則定義為SOLID原則,指代了面向?qū)ο缶幊痰?個(gè)基本原則。當(dāng)這些原則被一起應(yīng)用時(shí),它們使得一個(gè)軟件系統(tǒng)更清晰、簡單、最大程度地?fù)肀ё兓?。SOLID被典型地應(yīng)用在測試驅(qū)動(dòng)開發(fā)上,并且是敏捷開發(fā)以及自適應(yīng)軟件開發(fā)基本原則的重要組成部分。在經(jīng)過這一系列的學(xué)習(xí)之后,我們發(fā)現(xiàn)這幾大原則最終就可以化為這幾個(gè)關(guān)鍵詞:抽象、單一職責(zé)、最小化。那么在實(shí)際開發(fā)過程中如何權(quán)衡、實(shí)踐這些原則,是大家需要在實(shí)踐中多思考與領(lǐng)悟,正所謂”學(xué)而不思則罔,思而不學(xué)則殆”,只有不斷地學(xué)習(xí)、實(shí)踐、思考,才能夠在積累的過程有一個(gè)質(zhì)的飛越。

7.最少知識原則

最少知識原則又稱為迪米特原則英文全稱為Law of Demeter,簡稱LOD,雖然名字不同,但描述的是同一個(gè)原則:一個(gè)對象應(yīng)該對其他對象有最少的了解。通俗地講,一個(gè)類應(yīng)該對自己需要耦合或調(diào)用的類知道得最少,類的內(nèi)部如何實(shí)現(xiàn)、如何復(fù)雜都與調(diào)用者或者依賴者沒關(guān)系,調(diào)用者或者依賴者只需要知道他需要的方法即可,其他的我一概不關(guān)心。類與類之間的關(guān)系越密切,耦合度越大,當(dāng)一個(gè)類發(fā)生改變時(shí),對另一個(gè)類的影響也越大。

迪米特法則還有一個(gè)英文解釋是:Only talk to your immedate friends,翻譯過來就是:只與直接的朋友通信。什么叫做直接的朋友呢?每個(gè)對象都必然會與其他對象有耦合關(guān)系,兩個(gè)對象之間的耦合就成為朋友關(guān)系,這種關(guān)系的類型有很多,例如組合、聚合、依賴等,這個(gè)我就不再做過多的講解了。

8.總結(jié)

末尾給大家推薦一本書《Android 源碼設(shè)計(jì)模式》,不是給大家打廣告,我也沒拿任何好處。很多人一上來就要源代碼,但是我的代碼一直以來都是不會公開的,就算公開也是帶著大家寫的代碼。我其實(shí)最想問的是代碼真的就那么重要嗎?OKhttp 自帶緩存,之所要自己寫緩存,是因?yàn)榫彺嫫鋵?shí)也會分為很多種,比如我想把它先緩存到內(nèi)存中,然后緩存到數(shù)據(jù),接著緩存到磁盤,又或是我只想把他緩存到 SharedPreferences 我們可以借此機(jī)會練練手,當(dāng)然這些我都不會在這里寫了,根據(jù)上面的一些思想,相信你一定有辦法把它寫好。目前還不兼容 Retrofit ,這個(gè)我們必須得等看過 Retrofit 的源碼過后才能做兼容。

最后提醒大家的是在應(yīng)用開發(fā)過程中,最難的不是完成應(yīng)用的開發(fā)工作,而是在后續(xù)的升級、維護(hù)過程中讓應(yīng)用系統(tǒng)能夠擁抱變化。擁抱變化也就意味著在滿足需求且不破壞系統(tǒng)穩(wěn)定性的前提下保持高可擴(kuò)展性、高內(nèi)聚、低耦合,在經(jīng)歷了各版本的變更之后依然保持清晰、靈活、穩(wěn)定的系統(tǒng)架構(gòu)。當(dāng)然,這是一個(gè)比較理想的情況,但我們必須要朝著這個(gè)方向去努力,那么遵循面向?qū)ο罅笤瓌t就是我們走向靈活軟件之路所邁出的第一步。

所有分享大綱:Android進(jìn)階之旅 - 系統(tǒng)架構(gòu)篇

視頻講解地址:http://pan.baidu.com/s/1dFdABLZ

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容