1、clean架構簡介
clean架構相信大家早有耳聞,畢竟是Bob大叔的心血之作,最近又把five.agency/blog/androi… 幾篇關於clean 架構的文章拿出來讀讀加深了對該架構的一些理解。
clean架構又稱“洋蔥架構”,這個是由於它的架構示意圖得名的
刪除一些android專案中使用不到的東西加上我們使用的東西看起來如下圖
2、核心概念
從最抽象的核心到細節的邊緣
Entities
Entities,即Domain 物件或業務物件,是App的核心。 它們代表了APP的主要功能, 它們包含業務邏輯, 他們不會與外在世界的細節進行互動。
Use cases
Use case,又名interactors(互動器),又名business services,是Entities的擴充套件,是業務邏輯的延伸,也就是說。 它們包含的邏輯不僅限於一個實體,而是處理更多的實體。
Repositories
Repositories用於持久化Entities。 就這麼簡單。 它們被定義為介面並用作想要對Entities執行CRUD操作的用例的輸出埠。
Presenters
如果你熟悉MVP模式,Presenters就會做你期望他們做的事情。 他們處理使用者互動,呼叫適當的業務邏輯,並將資料傳送到UI進行渲染。 這裡通常有不同型別之間的對映
大概這幾個概念可以幫助我們更好的理解clean 架構,但是核心一點就是越在洋蔥裡面越是抽象。
3、clean 架構的核心規則
首先需要掌握涉及的領域,原文稱作“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獲取資料,通過業務邏輯傳遞它,然後將其傳送到螢幕?
組合和繼承的就要發揮功效了
4、實踐
下面用程式碼示意一下,以RSS Reader 為例我們的使用者應該能夠管理他們的RSS提要訂閱,從提要中獲取文章並閱讀它們。
這裡是資料流問題,用例介於表示層和資料層之間。 我們如何建立層之間的溝通? 記住那些輸入和輸出埠?
從上圖可以看出我們的 Use Case必須實現輸入埠(介面)。 Presenter在 Use Case上呼叫方法,資料流向 Use Case(feedId)。 Use Case對映feedId提供文章並希望將它們傳送回表示層。 它有一個對輸出埠(回撥)的引用,因為輸出埠是在同一層定義的,因此它呼叫了一個方法。 因此,資料傳送到輸出埠 - Presenter。
讓我們從domain層開始,建立我們的核心業務模型和邏輯。
我們的商業模式非常簡單:
- Feed - 持有RSS提要相關資料,如網址,縮圖網址,標題和說明
- Article -儲存文章相關資料,如文章標題,網址和釋出日期
我們的邏輯,我們將使用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分支可以看下具體玩法。
5、小結
這個跟設計模式中依賴倒置和介面隔離有著密不可分的關係,通過依賴倒置,只依賴於抽象而不是細節,將細節的實現倒置到實現類中,這樣洋蔥的核心就是清清爽爽的業務邏輯(抽象);
通常一個良好的架構一般需要滿足以下幾點:
- Satisfy a multitude of stakeholders.(滿足各方利益者)
- Encourage separation of concerns.(鼓勵的關注點分離)
- Run away from the real world (Android, DB, Internet…).即高度的抽象
- Enable your components to be testable.(使您的元件成為可測試的)
但是一個架構往往針對特定的場景,架構也是需要慢慢演進的,比如後面的模組化、外掛化等等都是業務發展到一定程度,當前架構的弊端慢慢的凸顯需要更新。但不管如何變化,一些核心基本點還是相伴相隨,比如依賴翻轉、面向介面程式設計、關注點分離等都是我們需要點亮的技能點之一。
參考