一張圖看Goodle Clean設計架構

来源:http://www.cnblogs.com/1992monkey/archive/2016/06/14/5585233.html
-Advertisement-
Play Games

之前用一張圖分析了Google給出的MVP架構,但是在Google給出的所有案例裡面除了基本的MVP架構還有其它幾種架構,今天就來分析其中的Clean架構。同樣的,網上介紹Clean架構的文章很多,我也就不用文字過多敘述了,還是用一張類圖來分析一下Clean架構的這個案例吧。好了,先直接上圖! 上完 ...


  之前用一張圖分析了Google給出的MVP架構,但是在Google給出的所有案例裡面除了基本的MVP架構還有其它幾種架構,今天就來分析其中的Clean架構。同樣的,網上介紹Clean架構的文章很多,我也就不用文字過多敘述了,還是用一張類圖來分析一下Clean架構的這個案例吧。好了,先直接上圖!

  

  上完圖,再說一說我對Clean架構的一個理解吧。對比前一篇文章的MVP架構圖可以看出,clean在一定程度上繼承了mvp的設計思想,但是其抽象程度比mvp更高。初次看這個demo的時候,確實被震撼了一下——原來用Java可以這樣寫代碼!!!跟之前用的一些項目框架和我自己平時寫的一些代碼對比一下,只能感嘆clean的這種設計思想真不是一般的程式員可以想出來的。它對介面、抽象類和實現類之間的實現、繼承、調用關係發揮到了一個比較高的層次,它並不是像我們平時寫代碼那樣很直白地寫下來,而是充分利用了面向對象的封裝性、繼承性和多態性,是對面向對象思想的一個高度理解。其實,要說clean複雜,它確實有些難理解,可是如果你真的理解了面向對象思想,那麼又會覺得這樣的設計完全在情理之中。

  舉個例子,在這個案例裡面,對實體類的設計就進行了高度的抽象與封裝。首先,為所有的實體類設計了基類——UseCase,UseCase的代碼如下:

 1 /*
 2  * Copyright 2016, The Android Open Source Project
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *      http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.architecture.blueprints.todoapp;
18 
19 /**
20  * Use cases are the entry points to the domain layer.
21  *
22  * @param <Q> the request type
23  * @param <P> the response type
24  */
25 public abstract class UseCase<Q extends UseCase.RequestValues, P extends UseCase.ResponseValue> {
26 
27     private Q mRequestValues;
28 
29     private UseCaseCallback<P> mUseCaseCallback;
30 
31     public void setRequestValues(Q requestValues) {
32         mRequestValues = requestValues;
33     }
34 
35     public Q getRequestValues() {
36         return mRequestValues;
37     }
38 
39     public UseCaseCallback<P> getUseCaseCallback() {
40         return mUseCaseCallback;
41     }
42 
43     public void setUseCaseCallback(UseCaseCallback<P> useCaseCallback) {
44         mUseCaseCallback = useCaseCallback;
45     }
46 
47     void run() {
48        executeUseCase(mRequestValues);
49     }
50 
51     protected abstract void executeUseCase(Q requestValues);
52 
53     /**
54      * Data passed to a request.
55      */
56     public interface RequestValues {
57     }
58 
59     /**
60      * Data received from a request.
61      */
62     public interface ResponseValue {
63     }
64 
65     public interface UseCaseCallback<R> {
66         void onSuccess(R response);
67         void onError();
68     }
69 }

  實體基類UseCase的設計用了泛型和介面,僅僅設計了兩個欄位mRequestValues和mUseCaseCallback。其中,mRequestValues代表數據請求參數,用泛型進行了封裝,它其實也是一個類的對象;mUseCaseCallback代表請求結果,同樣的,它也是一個類的對象,只不過這個類是用介面的形式進行抽象和封裝的。同時,UseCase中定義抽象方法executeUseCase()作為實體操作的入口。

  接下來,我們隨便看一個UseCase的實現類,就拿ActivateTask來說,ActivateTask繼承了UseCase,其實現代碼如下:

 1 /*
 2  * Copyright 2016, The Android Open Source Project
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *      http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.architecture.blueprints.todoapp.tasks.domain.usecase;
18 
19 import android.support.annotation.NonNull;
20 
21 import com.example.android.architecture.blueprints.todoapp.UseCase;
22 import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository;
23 
24 import static com.google.common.base.Preconditions.checkNotNull;
25 
26 /**
27  * Marks a task as active (not completed yet).
28  */
29 public class ActivateTask extends UseCase<ActivateTask.RequestValues, ActivateTask.ResponseValue> {
30 
31     private final TasksRepository mTasksRepository;
32 
33     public ActivateTask(@NonNull TasksRepository tasksRepository) {
34         mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
35     }
36 
37     @Override
38     protected void executeUseCase(final RequestValues values) {
39         String activeTask = values.getActivateTask();
40         mTasksRepository.activateTask(activeTask);
41         getUseCaseCallback().onSuccess(new ResponseValue());
42     }
43 
44     public static final class RequestValues implements UseCase.RequestValues {
45 
46         private final String mActivateTask;
47 
48         public RequestValues(@NonNull String activateTask) {
49             mActivateTask = checkNotNull(activateTask, "activateTask cannot be null!");
50         }
51 
52         public String getActivateTask() {
53             return mActivateTask;
54         }
55     }
56 
57     public static final class ResponseValue implements UseCase.ResponseValue { }
58 }

  可以看到,在ActivateTask 中,實現了父類UseCase的兩個介面RequestValues 和ResponseValue ,這兩個類將分別作為最終的實體請求對象類和返回結果對象類,同時,UseCase中的抽象方法executeUseCase()也被實現。因為實現的代碼裡面加入了泛型和介面,所以看起來會比較複雜,但是說到底無非就是繼承和實現的關係,僅此而已。通過這種面向介面的設計方式,可以讓我們的代碼看起來結構更清晰、更統一。

  接下來,我們可以看一下這個項目中的任務執行類UseCaseThreadPoolScheduler,同樣,UseCaseThreadPoolScheduler的設計採用了面向介面的方式,它實現了seCaseScheduler介面,UseCaseScheduler和UseCaseThreadPoolScheduler的實現分別如下:

 1 /*
 2  * Copyright 2016, The Android Open Source Project
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *      http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.architecture.blueprints.todoapp;
18 
19 /**
20  * Interface for schedulers, see {@link UseCaseThreadPoolScheduler}.
21  */
22 public interface UseCaseScheduler {
23 
24     void execute(Runnable runnable);
25 
26     <V extends UseCase.ResponseValue> void notifyResponse(final V response,
27             final UseCase.UseCaseCallback<V> useCaseCallback);
28 
29     <V extends UseCase.ResponseValue> void onError(
30             final UseCase.UseCaseCallback<V> useCaseCallback);
31 }
 1 /*
 2  * Copyright 2016, The Android Open Source Project
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *      http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.architecture.blueprints.todoapp;
18 
19 import android.os.Handler;
20 
21 import java.util.concurrent.ArrayBlockingQueue;
22 import java.util.concurrent.Executors;
23 import java.util.concurrent.ThreadPoolExecutor;
24 import java.util.concurrent.TimeUnit;
25 
26 /**
27  * Executes asynchronous tasks using a {@link ThreadPoolExecutor}.
28  * <p>
29  * See also {@link Executors} for a list of factory methods to create common
30  * {@link java.util.concurrent.ExecutorService}s for different scenarios.
31  */
32 public class UseCaseThreadPoolScheduler implements UseCaseScheduler {
33 
34     private final Handler mHandler = new Handler();
35 
36     public static final int POOL_SIZE = 2;
37 
38     public static final int MAX_POOL_SIZE = 4;
39 
40     public static final int TIMEOUT = 30;
41 
42     ThreadPoolExecutor mThreadPoolExecutor;
43 
44     public UseCaseThreadPoolScheduler() {
45         mThreadPoolExecutor = new ThreadPoolExecutor(POOL_SIZE, MAX_POOL_SIZE, TIMEOUT,
46                 TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(POOL_SIZE));
47     }
48 
49     @Override
50     public void execute(Runnable runnable) {
51         mThreadPoolExecutor.execute(runnable);
52     }
53 
54     @Override
55     public <V extends UseCase.ResponseValue> void notifyResponse(final V response,
56             final UseCase.UseCaseCallback<V> useCaseCallback) {
57         mHandler.post(new Runnable() {
58             @Override
59             public void run() {
60                 useCaseCallback.onSuccess(response);
61             }
62         });
63     }
64 
65     @Override
66     public <V extends UseCase.ResponseValue> void onError(
67             final UseCase.UseCaseCallback<V> useCaseCallback) {
68         mHandler.post(new Runnable() {
69             @Override
70             public void run() {
71                 useCaseCallback.onError();
72             }
73         });
74     }
75 
76 }

  可以看出,UseCaseThreadPoolScheduler實現了UseCaseScheduler中的三個抽象方法。

  接下來,我們再看看UseCaseHandler這個類,在UseCaseHandler中,通過子類實例化父類的形式,用UseCaseThreadPoolScheduler實例化了UseCaseScheduler對象。UseCaseHandler的代碼如下:

/*
 * Copyright 2016, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.architecture.blueprints.todoapp;


import com.example.android.architecture.blueprints.todoapp.util.EspressoIdlingResource;

/**
 * Runs {@link UseCase}s using a {@link UseCaseScheduler}.
 */
public class UseCaseHandler {

    private static UseCaseHandler INSTANCE;

    private final UseCaseScheduler mUseCaseScheduler;

    public UseCaseHandler(UseCaseScheduler useCaseScheduler) {
        mUseCaseScheduler = useCaseScheduler;
    }

    public <T extends UseCase.RequestValues, R extends UseCase.ResponseValue> void execute(
            final UseCase<T, R> useCase, T values, UseCase.UseCaseCallback<R> callback) {
        useCase.setRequestValues(values);
        useCase.setUseCaseCallback(new UiCallbackWrapper(callback, this));

        // The network request might be handled in a different thread so make sure
        // Espresso knows
        // that the app is busy until the response is handled.
        EspressoIdlingResource.increment(); // App is busy until further notice

        mUseCaseScheduler.execute(new Runnable() {
            @Override
            public void run() {

                useCase.run();
                // This callback may be called twice, once for the cache and once for loading
                // the data from the server API, so we check before decrementing, otherwise
                // it throws "Counter has been corrupted!" exception.
                if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {
                    EspressoIdlingResource.decrement(); // Set app as idle.
                }
            }
        });
    }

    public <V extends UseCase.ResponseValue> void notifyResponse(final V response,
            final UseCase.UseCaseCallback<V> useCaseCallback) {
        mUseCaseScheduler.notifyResponse(response, useCaseCallback);
    }

    private <V extends UseCase.ResponseValue> void notifyError(
            final UseCase.UseCaseCallback<V> useCaseCallback) {
        mUseCaseScheduler.onError(useCaseCallback);
    }

    private static final class UiCallbackWrapper<V extends UseCase.ResponseValue> implements
            UseCase.UseCaseCallback<V> {
        private final UseCase.UseCaseCallback<V> mCallback;
        private final UseCaseHandler mUseCaseHandler;

        public UiCallbackWrapper(UseCase.UseCaseCallback<V> callback,
                UseCaseHandler useCaseHandler) {
            mCallback = callback;
            mUseCaseHandler = useCaseHandler;
        }

        @Override
        public void onSuccess(V response) {
            mUseCaseHandler.notifyResponse(response, mCallback);
        }

        @Override
        public void onError() {
            mUseCaseHandler.notifyError(mCallback);
        }
    }

    public static UseCaseHandler getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new UseCaseHandler(new UseCaseThreadPoolScheduler());
        }
        return INSTANCE;
    }
}

  從上面的代碼中,我們可以看到,聲明的變數mUseCaseScheduler是UseCaseScheduler的對象,但是在構建UseCaseHandler對象的時候,傳入的參數卻是UseCaseThreadPoolScheduler對象,即用UseCaseThreadPoolScheduler實例化了UseCaseScheduler對象。然後,對mUseCaseScheduler的所有操作都轉化成了對UseCaseThreadPoolScheduler對象的操作。

  然後,我們仔細看UseCaseHandler的實現的代碼,我們會發現其實對實體進行操作的入口就是execute()方法!因為這個方法裡面調用了UseCase的run(),而UseCase的run()最終調用了UseCase的executeUseCase()。通過剛剛的分析,我們應該知道,我們實際上操作的實體應該是UseCase的實現類,而不是UseCase類本身,那麼這中間是通過什麼方式將對UseCase的操作轉移到UseCase的實現類上面的呢?我們會發現UseCaseHandler的execute()傳入了UseCase對象作為參數,好的,那麼我們就看看execute()是在哪裡被調用的吧!

  經過追蹤,我們看到在TasksPresenter類中調用了此方法,調用處的代碼如下:

 1 @Override
 2     public void activateTask(@NonNull Task activeTask) {
 3         checkNotNull(activeTask, "activeTask cannot be null!");
 4         mUseCaseHandler.execute(mActivateTask, new ActivateTask.RequestValues(activeTask.getId()),
 5                 new UseCase.UseCaseCallback<ActivateTask.ResponseValue>() {
 6                     @Override
 7                     public void onSuccess(ActivateTask.ResponseValue response) {
 8                         mTasksView.showTaskMarkedActive();
 9                         loadTasks(false, false);
10                     }
11 
12                     @Override
13                     public void onError() {
14                         mTasksView.showLoadingTasksError();
15                     }
16                 });
17     }

  可以看到,我們傳入的參數實際上是UseCase的實現類ActivateTask的對象,到這裡,我們就明白啦!原來也是子類實例化父類的方式。

  上面我只是簡單粗略地講述了一下項目中部分模塊的代碼,僅僅是舉個例子,更多的東西需要大家自己用面向對象的思想去理解。我說這些的目的就是想告訴大家,充分運面向對象的思想就可以設計出很多看似複雜的架構和項目,但是不管再怎麼複雜的代碼也肯定是有跡可循的,我們只要抓住了這些設計思想的本質,多看幾遍代碼,一定會豁然開朗!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、數組的擴展,ES6在數組擴展了一些API,以實現更多的功能 1.Array.from:可以將類數組和可遍歷的數據結構轉換成真正的數組,如下所示 如果參數是真正的數組,則直接返回一個一樣的新數組,參數也可是一個實現了Iterator介面的數據結構,如set,如下所示 Array.from還支持第二 ...
  • 一、簡介 1.1 GCD (Grand Central Dispatch )是Apple開發的一個多核編程的解決方法。 Grand 含義是“偉大的、巨集大的”,Central含義“中央的”,Dispatch含義是“分發、派遣,調度”; 1.2 GCD中有2個核心概念 任務:執行什麼操作 隊列:用來存放 ...
  • 網路安全與加密 1.數據安全 2.Base64 3.常見的加密演算法和其它 4.單向散列函數 5.對稱加密 6.非對稱加密 7.數字簽名 1.數字簽名的應用場景 需要嚴格驗證發送方身份信息情況 2.數字簽名原理 1)客戶端處理 ①對"消息"進行 HASH 得到 "消息摘要" ②發送方使用自己的私鑰對" ...
  • 多線程基本概念 多線程實現方案 1.pthread 2.NSThread (1)基本使用 (2)設置線程屬性 (3)線程的狀態 (4)線程安全 (5)線程間通信 (6)如何計算代碼段的執行時間 3.GCD (1)GCD基本知識 兩個核心概念 隊列和任務 同步函數和非同步函數 (2)GCD基本使用【重點 ...
  • command+shift+0會出現如下圖 然後輸入你想找的API 記得找帶Reference這種標記的文檔 ...
  • ARC已經出來很久了,自動釋放記憶體的確很方便,但是並非絕對安全絕對不會產生記憶體泄露。導致iOS對象無法按預期釋放的一個無形殺手是——迴圈引用。迴圈引用可以簡單理解為A引用了B,而B又引用了A,雙方都同時保持對方的一個引用,導致任何時候引用計數都不為0,始終無法釋放。若當前對象是一個ViewContr ...
  • 剛接手了公司iOS的兩個APP, 現在碰到了這樣一個問題: 有一臺iPhone在一個APP中使用了微信支付,支付成功後,點擊返回到該APP,結果卻跳到了另外一個APP去了。 這兩個APP都是公司開發的,然後並不是在所有的手機上出現這種情況,只是在其中某一臺iPhone手機上出現這種情況。 支付過程大 ...
  • 前一陣子做了一個小項目,關於android的,想記錄一下學到的一些知識,做成一個小系列吧,算是對自己這一個多月來的見證。首先說明,這些知識也都是從網上各處學習來的,我自己做了一些小整理。 1.SQLite資料庫 之前用的資料庫是MySQL和SQLServer,還用過oracle,雖然不是很精通,這次 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...