[譯] 如何創建高度模塊化的 Android 應用
「單一職責原則規定,每個模塊或類應該對軟體提供的某單一功能負責。」(en.wikipedia.org/wiki/Single_responsibility_principle)
- 原文鏈接: Creating Highly Modular Android Apps
- 原文作者 : Ronaldo Pace
- 譯文出自 : 掘金翻譯計劃
- 譯者 :DeadLion · GitHub)
- 校對者 :Graning · GitHub), Kulbear · GitHub)
Android 中構建 UI 的職責通常委派給一個類(比如 Activity、Fragment 或 View/Presenter)。這通常涉及到以下任務:
- 填充 View(xml 布局)
- View 配置(運行時參數、布局管理、適配)
- 數據源連接(DB 或者 數據存儲的監聽/訂閱)
- 載入緩存數據
- 新數據的按需請求分派
- 監聽用戶事件(tap、scroll)然後響應事件
除此之外,Activity 和 Fragment 通常還會委派一些額外的職責:
- App 導航
- Activity 結果處理
- Google Play 服務連接和交互
- 過渡動畫配置
這不是單一職責,當前的處理方式包括了繼承或組合,這太複雜了。
繼承地獄
「當一個對象或類是基於另一個對象或類,這就是繼承。它是為了代碼重用,並允許原始軟體通過公共類和介面單獨擴展。這些對象或類的關係,通過繼承形成一種層級。」
(en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)))
對於這種複雜的結構,如 UI 構建,繼承能讓它很快變成一坨 x。看看下面的模擬案例:
據此繼承樹構建代碼會很快變得難於管理 (」繼承地獄」)。要避免這種情況,開發人員應遵循」組合而非繼承」的原則。
組合優於繼承
「在面向對象編程中有個原則,組合替代繼承(組合復用原則)。類應該通過組合實現行為多態和代碼復用(通過包含其他類的實例來實現所需的功能)。」(en.wikipedia.org/wiki/Composition_over_inheritance)
組合優於繼承原則是個很棒的想法,無疑可以幫助我們解決上面提出的問題。然而,幾乎沒有庫、示例代碼或者教程來教你如何在 Android 上實現這原則。一種實現它的簡單方法就是使用運行時參數(又叫 intent extras)來組合功能,但是,仍會導致形成一個巨大的難以管理的怪物類。
很榮幸,這裡要提及兩個庫, LightCycle 和 CompositeAndroid。兩者都緊緊的綁定在 Activity 或 Fragment,拋開其他諸如 MVP 或 MVVM 的現代模式,都不是很靈活,因為它們僅僅依賴 Android 原生回調(無法添加額外回調),也不支持模塊間通信。
修飾模式
開發者們每天都要面對這些提出的問題, EyeEm Android 團隊開始開發一種模式,以一種更加靈活的方式來解決該問題,而不是直接附加到一個組件上如 Activity 或 Fragment 。該模式可以用來對任何開發者希望通過組合來模塊化的類進行解耦。
該模式和 LightCycle/Composite 的方法非常相似,由三個類組成:
- 基本類,稱為 DecoratedObject(裝飾對象),調度其繼承和額外的方法給一個調度對象。
- DecoratorsObject 實例化,保存所有組成對象的列表並分派方法給它們。
- Decorator 抽象類,所有方法和額外介面都只聲明未實現。由創建此類的開發人添加單一職責的具體實現。
使用這種方式開發人員獲得的直接好處
- 職責分離
- 功能動態運行置換
- 並行開發
為了讓開發者能毫無障礙的實現上述模式,一個在編譯時生成代碼的工具被創造了出來,接下來我們會看到,將之前提交的那些職責分解成單一職責類是多麼簡單。
Decorator 庫
如何三步創建你自己的模塊化單一職責應用
要實現裝飾模式首先創建應生成的代碼藍圖,在這裡我們將使用一個帶 RecyclerView 的 Activity 作為例子,但同樣能用在 Fragment、Presenter 甚至 View 。這這個例子中,我們將使用 activity 生命周期中的 onCreate/onStart/onStop/onDestroy ,但是也會額外創建幾個適合 RecyclerView 案例的回調。
@Decoraten public class ActivityBlueprint extends AppCompatActivity {nn @Override protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);}n @Override protected void onStart() {super.onStart();}n @Override protected void onStop() {super.onStop();}n @Override protected void onDestroy() {super.onDestroy();}nn public int getLayoutId() {return R.layout.recycler_view;}n public RecyclerView.LayoutManager getLayoutManager() {return new LinearLayoutManager(this);}n public RecyclerView.Adapter getAdapter() {return null;}n public void setupRecyclerView(RecyclerView recyclerView, WrapAdapter wrapAdapter, RecyclerView.Adapter adapter) { /**/ }nn public interface DataInstigator {n RealmList getList();n RealmObject getData();n }nn public interface RequestInstigator {n void reload();n void loadMore();n }n }n
這個簡單的藍圖使用 @Decorate 註解,將會生成完整的修飾模式實現,Serializable builder 類可以作為參數傳遞。為了完成 Activity 的實現,我們擴展了生成類,並將 received builder 綁定上去。
public classRecyclerViewActivityextendsDecoratedAppCompatActivity{nn @Overrideprotected void onCreate(Bundle savedInstanceState) {n bind(getBuilder(getIntent().getSerializableExtra(KEY.BUILDER)));n super.onCreate(savedInstanceState);n setContentView(getLayoutId());n RecyclerView rv = (RecyclerView) findViewById(R.id.recycler);n rv.setLayoutManager(getLayoutManager());n RecyclerView.Adapter adapter = getAdapter();n WrapAdapter wrapAdapter = newWrapAdapter(adapter);n rv.setAdapter(wrapAdapter);n setupRecyclerView(rv, wrapAdapter, adapter);n }nn @Overrideprotected void onDestroy() {n super.onDestroy();n unbind();n }n }n
現在可以方便的將職責分發到可綁定的修飾類上。每個修飾器包含所有生命周期的回調,可以實現任何可選介面。最後,可以組合得到一個簡單的建造者模式:
Intent i = new Intent(context, RecyclerViewActivity.class);n i.putExtra(KEY.BUILDER, new DecoratedActivity.Builder()n .addDecorator(GridInstigator.class)n .addDecorator(LoadMoreDecorator.class)n .addDecorator(PhotoGridAdapter.class)n .addDecorator(PhotoListInstigator.class)n .addDecorator(PhotoRequestInstigator.class));n i.putExtra(KEY.URL, url);n
完整示例應用
請查看我們 Github 上的相關庫和完整的示例應用 GitHub - eyeem/decorator: Dynamic inheritance library 。該示例應用在開始下一步之前從當前 activity 通過簡單的添加/移除修飾器來模擬每個用戶在 Activity 執行 tap。
上面展示的代碼大部分都是出自示例。你會發現一個用 Realm 和 Retrofit 真正實現的修飾器列表,就是這篇文章開始提到的 UI 構建任務。
- CoordinatorLayoutInstigator,重寫了 CoordinatorLayout 的默認布局,可選實例化一個 header
- ToolbarInstigator,接管 toolbar,並且應用一個標題
- ToolbarUp 和 ToolbarBack 修飾器,導航工具欄上圖標的行為
- 載入更多的修飾器,添加一個無限滾動的功能到 RecyclerView
- PhotoList 和 PhotoRequest 修飾器,本地數據存儲和 API 請求圖片列表 API 調用
現實世界應用
EyeEm 已經在使用修飾器——並且體驗非常好。來 Play Store 看看吧。我們目前為所有 UI 元素使用 裝飾 view presenters(使用 Square Mortar 庫),為過渡動畫使用了裝飾 activities,處理不同 API 級別,A/B 測試,導航,跟蹤和新攝影師入職時的少數特殊情況,
最後說明
上面所示的代碼和實現純粹只是示例,僅作為指導。
當我們為 Android 創建這個庫時,該模式是開放給任何用例的。這個庫是一個純 Java 實現,它在編譯時生成代碼,可用於任何 Java 類,我們鼓勵開發人員在他們任何 Java 項目中編寫模塊化的單一職責的代碼!來
說的夠多了-將它添加到你的 build.gradle 中,然後開始構建模塊化應用吧。
在 EyeEm,我們正在探索攝影和技術的交叉點。除了建立尖端的計算機視覺技術,我們的 iOS,Android 和 web 應用程序被 1800 萬世界各地的攝影師用於獲得靈感、 學習、 分享他們的工作,發現驚人的天賦,獲得出版和展出,甚至通過我們的市場賺錢。
查看更多譯文,請直接在 GitHub 上 Star 我們吧:GitHub - xitu/gold-miner: 掘金翻譯計劃,翻譯掘金上優質的英文文章
推薦閱讀:
※Android 內存優化
※2015年後,如何看待「小米在做加法,魅族在做減法」的說法?
※讓你的電腦變成windows+安卓雙系統