clean架構相信你們早有耳聞,畢竟是Bob大叔的心血之做,最近又把five.agency/blog/androi… 幾篇關於clean 架構的文章拿出來讀讀加深了對該架構的一些理解。java
clean架構又稱「洋蔥架構」,這個是因爲它的架構示意圖得名的android
刪除一些android項目中使用不到的東西加上咱們使用的東西看起來以下圖git
從最抽象的核心到細節的邊緣github
Entities,即Domain 對象或業務對象,是App的核心。 它們表明了APP的主要功能, 它們包含業務邏輯, 他們不會與外在世界的細節進行互動。編程
Use cases設計模式
Use case,又名interactors(交互器),又名business services,是Entities的擴展,是業務邏輯的延伸,也就是說。 它們包含的邏輯不只限於一個實體,而是處理更多的實體。 網絡
Repositories用於持久化Entities。 就這麼簡單。 它們被定義爲接口並用做想要對Entities執行CRUD操做的用例的輸出端口。架構
若是你熟悉MVP模式,Presenters就會作你指望他們作的事情。 他們處理用戶交互,調用適當的業務邏輯,並將數據發送到UI進行渲染。 這裏一般有不一樣類型之間的映射app
大概這幾個概念能夠幫助咱們更好的理解clean 架構,可是核心一點就是越在洋蔥裏面越是抽象。dom
首先須要掌握涉及的領域,原文稱做「Master of your domain」,就是說當你打開你的工程,除去技術相關,你就應該從宏觀上知曉你的app是作什麼的,而不要沉浸到細節之中" 爲啥須要咱們站在這個高度去看待本身的項目呢,由於這也是clean核心所在,該架構越往洋蔥裏面抽象度愈來愈高,都是業務邏輯的高度抽象,即"內層包含業務邏輯、外層包含實現細節"
繼而給出了設計出這樣特性的clean架構須要具有的技術點
(1)Dependency rule(依賴原則)
(2)Abstraction(抽象)
(3)Communication between layers(層之間的通訊)
下面將一一介紹
3.1 Dependency rule(依賴原則)
從最開始的第一幅咱們就能夠看出箭頭是"依賴",即 外層看到並瞭解內層,可是內層既看不到也不知道外層,結合前面所強調的 內層包含業務邏輯(抽象),外層包含實現細節。 結合依賴關係規則,業務邏輯既看不到,也不知道實現細節。 一般咱們能夠經過放在不一樣的module中去來調整它們之間的依賴關係
3.2 Abstraction(抽象)
以前也說過當你走向圖的中間時,東西變得更加抽象。 這是有道理的:抽象每每比細節更加穩定,就像搭積木同樣根基越穩越好。
舉個例子好比咱們能夠將抽象接口定義爲「加載網絡圖片」並將其放入內層,業務邏輯就可使用它來顯示圖片。 另外一方面,咱們能夠經過實現改接口調用具體的圖片加載庫glide或者Picasso,而後將該實現放入外層。業務邏輯可使用加載圖片功能而無需知道實現細節的任何內容。這樣抵禦了變化的風險,往後換成什麼方式加載圖片,洋蔥內部的抽象業務邏輯是不會感知到的
3.3 Communication between layers(層之間的通訊)
既然已經劃分好了層,分離了內容,將業務邏輯放在應用程序的架構中心和架構邊緣的實現細節中,一切看起來都很棒。 可是你可能很快遇到了一個有趣的問題。
若是你的UI是一個實現細節,那麼internet也是一個實現細節,業務邏輯介於二者之間,咱們如何從internet獲取數據,經過業務邏輯傳遞它,而後將其發送到屏幕?
組合和繼承的就要發揮功效了
下面用代碼示意一下,以RSS Reader 爲例咱們的用戶應該可以管理他們的RSS提要訂閱,從提要中獲取文章並閱讀它們。
這裏是數據流問題,用例介於表示層和數據層之間。 咱們如何創建層之間的溝通? 記住那些輸入和輸出端口?
從上圖能夠看出咱們的 Use Case必須實現輸入端口(接口)。 Presenter在 Use Case上調用方法,數據流向 Use Case(feedId)。 Use Case映射feedId提供文章並但願將它們發送回表示層。 它有一個對輸出端口(回調)的引用,由於輸出端口是在同一層定義的,所以它調用了一個方法。 所以,數據發送到輸出端口 - Presenter。
讓咱們從domain層開始,建立咱們的核心業務模型和邏輯。
咱們的商業模式很是簡單:
咱們的邏輯,咱們將使用UseCases。 他們在簡潔的類中封裝了小部分業務邏輯。 他們都將實施通用的UseCase 契約接口:
public interface UseCase<P, R> {
interface Callback<R> {
void onSuccess(R return);
void onError(Throwable throwable);
}
void execute(P parameter, Callback<R> callback);
}
public interface CompletableUseCase<P> {
interface Callback {
void onSuccess();
void onError(Throwable throwable);
}
void execute(P parameter, Callback callback);
}
複製代碼
UseCase接口是輸入端口,Callback接口是輸出端口,GetFeedArticlesUseCase實現以下
class GetFeedArticlesUseCase implements UseCase<Integer, List<Article>> {
private final FeedRepository feedRepository;
@Override
public void execute(final Integer feedId, final Callback<List<Article>> callback) {
try {
callback.onSuccess(feedRepository.getFeedArticles(feedId));
} catch (final Throwable throwable) {
callback.onError(throwable);
}
}
}複製代碼
而後到洋蔥外面的UI實現,View有一個簡單的契約類
interface View {
void showArticles(List<ArticleViewModel> feedArticles);
void showErrorMessage();
void showLoadingIndicator();
}複製代碼
該視圖的Presenter具備很是簡單的顯示邏輯。 它獲取文章,將它們映射到view odels並傳遞到View,再看下FeedArticlesPresenter:
class FeedArticlesPresenter implements UseCase.Callback<List<Article>> {
private final GetFeedArticlesUseCase getFeedArticlesUseCase;
private final ViewModeMapper viewModelMapper;
public void fetchFeedItems(final int feedId) {
getFeedArticlesUseCase.execute(feedId, this);
}
@Override
public void onSuccess(final List<Article> articles) {
getView().showArticles(viewModelMapper.mapArticlesToViewModels(articles));
}
@Override
public void onError(final Throwable throwable) {
getView().showErrorMessage();
}
}複製代碼
到這裏能夠看出FeedArticlesPresenter實現了Callback接口,並將其自身傳遞給use case,它其實是use case的輸出端口,並以這種方式關閉了數據流
谷歌架構項目上也有個 todo-mvp-clean分支能夠看下具體玩法。
這個跟設計模式中依賴倒置和接口隔離有着密不可分的關係,經過依賴倒置,只依賴於抽象而不是細節,將細節的實現倒置到實現類中,這樣洋蔥的核心就是清清爽爽的業務邏輯(抽象);
一般一個良好的架構通常須要知足如下幾點:
可是一個架構每每針對特定的場景,架構也是須要慢慢演進的,好比後面的模塊化、插件化等等都是業務發展到必定程度,當前架構的弊端慢慢的凸顯須要更新。但無論如何變化,一些核心基本點仍是相伴相隨,好比依賴翻轉、面向接口編程、關注點分離等都是咱們須要點亮的技能點之一。
參考