帶你領略Clean架構的魅力

官方微信:動力節點Java學院

前言

當項目需求不斷擴張的時候,當開發團隊人員不斷增加,當新技術不斷湧現,當軟體質量不斷提高,我還是不能和你分手,不能和你分手。我對唱出聲的同學不發表任何意見。如果你真的碰到上述問題而沒有演進你的架構,可能你碰到的問題都是屬於靈異事件。那這裡的核心點是架構,那它又是個什麼玩意?它能帶來什麼好處?

架構

到底什麼是架構?我現在的水平還不能告訴你,沒資格。我能告訴你自己在學習過程中的領悟,我想架構就是一種約定,怎樣的約定?為了更好的研發效率、擴展性和穩定性,更精準的風險防控能力。這樣說可能有點抽象。

作為一名資深宅男,床到門就是世界上最遙遠的距離,上一次廁所好像經歷了九九八十一難,但我們從不寂寞,你說是吧,Mac。額,好像扯遠了,我用完東西可能一直放在我喜歡的地方,只是在其他人眼中顯得有點亂,在我眼裡是很有規律的。因為我一直遵循著這個規定,每本書都有我給它定義的位置。但是我老媽不知道啊,雖然整理的很乾凈,但是書放錯了位置,老媽可能是無意識的,就算下次我問她,她也記不清了。為了找到這本書,我也只能遍歷一遍了。如果我們雙方都遵循我們倆一起定義的一個規定,一個約定,達成一種共識。那麼就很簡單了,書永遠在那,就算是添新書還是移除,雙方都有章可循。

Advertisements

由此可見,架構的好處就是除了引進新的技術,自己或者說產品本身都有更好的體驗外,還由於做了統一規範,讓結構更加清晰,健壯,團隊協作更加方便。

以上並不是今天的主題,架構的理解也僅限於我的理解,相對於Android,我們知道的架構更多的可能叫MVC、MVP和MVVM等。關於這個。。。對方不想和你說話,並向你扔了一個鏈接。一篇好的文章是由文章本身內容和優秀的評論組成的,所以,大家慢慢欣賞,算了,買一送一,喜歡研究架構的同學戳 這裡 ,谷歌爸爸的。這裡,我直接引出今天的主題Clean架構。

Clean架

大多數情況下,碰到問題,我們要從3個方向去思考,what、how、why。引用某大佬的一句話:未經思考的人生是不值得去過的人生。所以,列位看官不要僅僅滿足於快餐文化,經常多動腦子,看問題千萬不要流於表面!額,又扯遠了。。。我先說說what。

Advertisements

What

概念一直都是枯燥乏味的,如果不喜歡的朋友,可以直接跳到How或者本章節的總結部分,但是概念也是最基礎的,最基礎是不是最重要我不發表意見。

秉承我們No picture,say a J8的優雅傳統,先上個毫無意義的結構圖。

官方微信:動力節點Java學院

這張圖可能對於你們小白看來覺得很不可思議,但對於我們專業來說,也是一臉懵逼(手動滑稽),我簡單的解釋下:

  • Enterprise Business Rules:業務對象

  • Application Business Rules:用於處理我們的業務對象,業務邏輯所在,也稱為Interactor

  • Interface Adapters: 介面轉換,拿到我們需要的數據,表現層(Presenters)和控制層(Controllers)就在這一層

  • Frameworks and Drivers: 這裡是所有具體的實現了:比如:UI,工具類,基礎框架等等。

關於上面的圖,米娜可以戳這裡。

我解釋后估計你還是一頭霧水,我們再來看一個圖:

好像相對於上面那張圖更好理解,知道為什麼嗎?因為字少了好多。哈哈。接下來的內容以及我的開源項目中都是以此為基礎來寫的。分別來解釋下。

表現層 (Presentation Layer)

我們這裡的表現層以MVP為基礎,個人覺得Clean本身也是MVP的基礎上更加抽象,更加獨立。熟悉的MVP的同學非常清楚這一層是幹嘛用的。老規矩,先上張圖。

是不是很眼熟?P層使得V層(Fragment和Activity)內部除UI邏輯再無其它邏輯。而我的開源項目中的Presenter由多個Interactor組成。底下會介紹。

領域層 (Domain Layer)

圖上很明顯了,這裡主要是interactor的實現類和業務對象。講道理這裡應該只屬於java模塊,但是有時候我們的業務對象,可能要實現第三方庫中的實體類介面,不得不改為Android模塊,暫時沒想到很好的辦法,有知道的大佬可以指教一下。

數據層 (Data Layer)

這是一種Repository模式,具體的可以看這裡。以我現在的見解,只能說只要項目複雜而需要分層,那麼就應該用這個模式,它讓clean架構的clean更加亮眼。

這個本來就是概念,我相信大家也不願意看,所以就簡單介紹。如果想詳細了解,可以戳這裡,

總結

現在談談自己的看法,後者是相對前者較為具體的一種符合Android的結構。在這插一個clean架構的依賴性規則:內層不能依賴外層。三者也都分別解釋了是幹什麼用的,那麼為什麼有分為這三者,它們又有什麼聯繫?我是個俗人,那就應該用俗話來講,從數據層利用Repository模式讓領域層感覺不到數據訪問層的存在,即原始數據是獨立的,業務規則不綁定具體哪一種數據,通俗點講就是你要什麼數據?我給你取,但你不需要知道我從哪裡取的;因此領域層對數據層怎麼實現的是一無所知,而領域層主要工作就是你給了我數據,那我就要用,怎麼用?都是我來決定;用完之後再回調給表現層渲染UI。因此大多數的業務邏輯都在領域層,可以說是一個APP的核心。我認為這裡透露著一個很重要的設計理念就是數據驅動UI,我都想給自己點個贊,哈哈。其實,到這裡,你心裡已經有點13數的話,可以跳到Why,因為怎麼用已經是具體的東西,而架構本身就是一種共識,是抽象的,從Java角度講你可以多個類去實現這個介面。下面的使用只是我對Clean架構理解的一點代碼體現。

How

下面的例子是從我開源庫 CrazyDaily 中選取的,以知乎日報為例。

數據層 (Data Layer)

數據層就是從我們的倉庫(Repository)中取數據,可以從雲端、磁碟或者內存中取。

public interface ZhihuService {String HOST = "http://news-at.zhihu.com/api/4/"; @GET("news/latest") Flowable<ZhihuNewsEntity> getZhihuNewsList(); @GET("news/{id}") Flowable<ZhihuNewsDetailEntity> getZhihuNewsDetail(@Path("id") long id);}public class ZhihuDataRepository implements ZhihuRepository {... @Injectpublic ZhihuDataRepository(HttpHelper httpHelper) {mZhihuService = httpHelper.getZhihuService();} @Overridepublic Flowable<ZhihuNewsEntity> getZhihuNewsList() { return mZhihuService.getZhihuNewsList()...}...}

這裡比較尷尬的是只提供了雲端的數據,採用的是retrofit+okhttp的框架獲取。比較正確的方式應該是給ZhihuDataRepository提供一個Factory而不是HttpHelper,Factory根據不同的條件獲取相應的數據。比如像這樣:

@InjectUserDataRepository(UserDataStoreFactory dataStoreFactory,UserEntityDataMapper userEntityDataMapper) { this.userDataStoreFactory = dataStoreFactory; this.userEntityDataMapper = userEntityDataMapper;}

UserDataStoreFactory是從不同地方獲取數據的一個工廠類,UserEntityDataMapper是我們的數據包裝類,不知道還記得上面的Interface Adapters嗎?細心的朋友可以關注到ZhihuDataRepository實現了ZhihuRepository,但是ZhihuRepository並非數據層的東西,而是領域層的東西,很顯然,以介面進行關聯,但內容獨立,沒錯,這就是傳送中的依賴倒置原則。

領域層 (Domain Layer)

public interface ZhihuRepository { Flowable<ZhihuNewsEntity> getZhihuNewsList(); Flowable<ZhihuNewsDetailEntity> getZhihuNewsDetail(long id);}public abstract class UseCase<T, Params> {... public UseCase() {...} protected abstract Flowable<T> buildUseCaseObservable(Params params); public void execute(Params params, DisposableSubscriber<T> subscriber) {...}...}public class ZhihuNewsListUseCase extends UseCase<ZhihuNewsEntity, Void> { private final ZhihuRepository mZhihuRepository; @Injectpublic ZhihuNewsListUseCase(ZhihuRepository zhihuRepository) {mZhihuRepository = zhihuRepository;} @Overrideprotected Flowable<ZhihuNewsEntity> buildUseCaseObservable(Void aVoid) { return mZhihuRepository.getZhihuNewsList()...}}

真的很完美,跟數據層一毛線關係都沒有,利用介面(ZhihuRepository)來控制數據層(ZhihuDataRepository)。真的感覺架構越來越有意思了。我可以在這裡處理我們大部分的業務邏輯。

表現層 (Presentation Layer)

@ActivityScopepublic class HomePresenter extends BasePresenter<HomeContract.View> implements HomeContract.Presenter { private ZhihuNewsListUseCase mZhihuUseCase;... @Inject //多個UseCasepublic HomePresenter(ZhihuNewsListUseCase zhihuUseCase ...) {mZhihuUseCase = zhihuUseCase;...} @Overridepublic void getZhihuNewsList() {mZhihuUseCase.execute(new BaseSubscriber<ZhihuNewsEntity>() { @Overridepublic void onNext(ZhihuNewsEntity zhihuNewsEntity) {mView.showZhihu(zhihuNewsEntity);}});}}public interface HomeContract { interface View extends IView { void showZhihu(ZhihuNewsEntity zhihuNewsEntity);...} interface Presenter extends IPresenter<View> { void getZhihuNewsList();...}public class HomeActivity extends BaseActivity<HomePresenter> implements HomeContract.View { @Overrideprotected void initData() {mPresenter.getZhihuNewsList();...} @Overridepublic void showZhihu(ZhihuNewsEntity zhihuNewsEntity) {...}...}

說實話真的不想貼代碼,太麻煩了,但不貼,不太好理解,而與我們相濡以沫的也就是代碼了,眼睛莫名一酸。表現層更多的是MVP的概念,所以。。

簡單的理下邏輯,Activity發起獲取數據信號(View)->調用Presenter介面(Presenter)->調用UseCase介面(Domain)->調用Repository介面(Data)->拿到原始數據->回調Repository(Data)->回調UseCase(Domain)->回調Presenter(Presenter)->回調Activity(View)

如果這都看到不懂,買塊豆腐撞死算了。

Why

其實How並不是很重要,但卻佔了很大篇幅。

你認為Why是最重要的嗎?這個問題留在心中,先來看看Why。

  • Easy to maintain

  • Easy to test

  • Very cohesive

  • Decoupled

看見高內聚,低耦合我就頭疼。說說其它吧,易於維護,這樣的結構已經能說明了,易於測試也好理解,各個模塊獨立,來看看這:

  • 展示層 (Presentation Layer) : 使用android instrumentation和espresso進行集成和功能測試

  • 領域層 (Domain Layer) :使用JUnit和Mockito進行單元測試

  • 數據層 (Data Layer) :使用Robolectric(因為依賴於Android SDK中的類)進行集成測試和單元測試

來源:推酷

官方微信:動力節點Java學院

Advertisements

你可能會喜歡