写点什么

[译] 使用 MODEL-VIEW-INTENT 第四部分 — 独立 UI 组件

用户头像
Android架构
关注
发布于: 23 分钟前

父子关系的组件充满着代码异味,因为它们表示了一种父类与子类的直接耦合,这就导致了代码很难阅读,很难维护,当需求发生变化会影响很多组件(尤其是在大型系统中几乎是不可能完成的任务)最后,同样重要的是,引入了很多很难预测甚至更难去复刻和调试的共享状态。


到现在为止还挺好的,但是我们假设信息必须从 Presenter A 流向 Presenter B:如何让不同的 Presenter 相互间通信? 它们不通信!什么样的场景才需要一个 Presenter 不得不与另一个 Presenter 通信?事件 X 发生了?Presenters 完全不用相互间通信,他们仅仅观察相同的 Model(或者精确到相同的业务逻辑)。这是它们如何得到关于变化的通知:从底层。



无论何时一个事件 X 发生了(例如:一个用户点击了在 View1 上的按钮), 这个 Presenter 会让信息下沉到业务逻辑。既然其他的 Presenter 观察相同的业务逻辑, 他们从已经变化的业务逻辑(model 已经发生变化)里得到通知。



我们已经在[第一部分](


)强调了一个很重要的原则(单向数据流)。


让我们用真实案例来实现上面的内容:在我们的电商 app 我们可以将任意一项商品放到购物车里。另外,这里还有一个页面,我们可以看到我们购物车的所有商品,并且我们一次性可以选择或者移除多个商品项。



如果我们可以把这个大的页面分离成很多小的,独立的并且可复用的 UI 组件,那岂不是很酷?比如说一个 Toolbar,它显示被选择的 item 的数量,和一个用来显示购物车里的商品项列表的 RecyclerView。


<LinearLayout><com.hannesdorfmann.SelectedCountToo


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


lbarandroid:id="@+id/selectedCountToolbar"android:layout_width="match_parent"android:layout_height="wrap_content"/>


<com.hannesdorfmann.ShoppingBasketRecyclerViewandroid:id="@+id/shoppingBasketRecyclerView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/></LinearLayout>


但是如何使这些组件进行相互间通信呢?显然每个组件有它自己的 Presenter:selectedCountPresenter shoppingBasketPresenter。这是父子关系吗?不,两者都仅仅观察同一个 Model(从相同的业务逻辑里获取更新):



public class SelectedCountPresenterextends MviBasePresenter<SelectedCountView, Integer> {


private ShoppingCart shoppingCart;


public SelectedCountPresenter(ShoppingCart shoppingCart) {this.shoppingCart = shoppingCart;}


@Override protected void bindIntents() {subscribeViewState(shoppingCart.getSelectedItemsObservable(), SelectedCountView::render);}}


class SelectedCountToolbar extends Toolbar implements SelectedCountView {


...


@Override public void render(int selectedCount) {if (selectedCount == 0) {setVisibility(View.VISIBLE);} else {setVisibility(View.INVISIBLE);}}}


ShoppingBasketRecyclerView 的代码看起来不错,有很多相同的地方,因此我忽略掉这些相同的地方了。然而,如果我们仔细观察 selectedCountPresenter 我们会注意到这个 Presenter 与 shoppingcart 耦合。我们想要使用这个 UI 组件可以在我们 App 的其他的页面使用,让这个组件变的可复用,我们需要移除这个依赖,这事实上是一个简单的重构:这个 Presenter 得到一个 Observable 作为 Model 的构造函数取代原来的 ShoppingCart:


public class SelectedCountPresenterextends MviBasePresenter<SelectedCountView, Integer> {


private Observable<Integer> selectedCountObservable;


public SelectedCountPresenter(Observable<Integer> selectedCountObservable) {this.selectedCountObservable = selectedCountObservable;}


@Override protected void bindIntents() {subscribeViewState(selectedCountObservable, SelectedCountToolbarView::render);}}


就是这样,任何时候,当我们想要显示当前 item 选择数量的时候,我们可以用这个 SelectedCountToolbar 组件。这个组件在购物车,可以记物品项的数量。但是,这个 UI 控件也可以用在你 App 里完全不同的情景下。此外,这个 UI 控件可以放在一个独立库中,并且在其他的 app 中使用,比如一个能显示选择多少张照片的 app。


Observable<Integer> selectedCount = photoManager.getPhotos().map(photos -> {int selected = 0;for (Photo item : photos) {if (item.isSelected()) selected++;}return selected;});


return new SelectedCountToolbarPresnter(selectedCount);

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
[译]使用 MODEL-VIEW-INTENT 第四部分 — 独立 UI 组件