MVC,MVP,MVPVM(一)实践之路

软件架构(六)MVC架构历史 接收 HTTP 请求,触发命令或查询,使用查询返回的数据、ViewModel、模板和模板引擎生成 HTML 并将它返回给客户端。从查询中接收(或者从 Model 实体中提取)原始数据,持有这些模板会用到的数据。),一种通用设计方案,发展至今,已不局限于前端或后端。不过,Presenter 仍然不会主动更新视图,它始终需要执行一次新的请求才能让变化可见。我们对比MVC、MVP、MVVM、MVPVM的变种升级过程,可以更好的了解架构的历史变迁。MVC有很多变种,这里列出对现在行业影响最大的几种,逐一说明。 阅读详情
文章目录

1.MVC,MVP,MVPVM(一)实践之路
2.MVC,MVP,MVPVM(二)提升效率之Templates

简介

分别使用MVC,MVP,MVP+VM,实践具体需求,对比优劣,逐步优化。

需求

实现我的押金页面,包含未缴纳,已缴纳,免押金3种状态
1.顶部title:3种状态展示不同文案;
2.金额:已缴纳,未缴纳状态金额字号,色值不同;免押金状态不展示;
3.底部tips:已缴纳,免押金状态展示不同文案;已缴纳状态,不展示;
4.按钮:未缴纳,已缴纳状态,文案,及点击事件都不相同;

我的押金页面.png

MVC的实现方式

activity_main.xml

<LinearLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:paddingLeft="30dp"
            android:paddingRight="30dp"
            tools:context="com.listen.test_mvc.MainActivity">

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="50dp"
                android:textColor="@android:color/black"
                android:textSize="20sp"
                tools:text="您需要缴纳押金"/>

            <TextView
                android:id="@+id/tv_money"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="50dp"
                android:layout_marginTop="50dp"
                android:textColor="@android:color/darker_gray"
                android:textSize="40sp"
                tools:text="¥200"/>

            <TextView
                android:id="@+id/tv_tips"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/black"
                android:textSize="16sp"
                tools:text="押金随时可退"/>

            <Button
                android:id="@+id/btn_pay_or_return"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="30dp"
                android:layout_marginRight="30dp"
                android:layout_marginTop="100dp"
                android:textColor="@android:color/black"
                android:textSize="16sp"
                tools:text="缴纳押金"/>
        </LinearLayout>

在MainActivity中通过butterKnife框架初始化view

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.tv_title)
    TextView mTvTitle;
    @BindView(R.id.tv_money)
    TextView mTvMoney;
    @BindView(R.id.tv_tips)
    TextView mTvTips;
    @BindView(R.id.btn_pay_or_return)
    Button mBtnPayOrReturn;

    ///////////////////////////////////////////////////////////////////////////
    // 缴纳押金,退还押金的点击事件
    ///////////////////////////////////////////////////////////////////////////
    private View.OnClickListener mDepositPayClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "缴纳押金", Toast.LENGTH_SHORT).show();
        }
    };

    private View.OnClickListener mDepositReturnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "退还押金", Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }
}

定义IDepositRepository封装数据获取逻辑(server,sqlite),此处模拟网络请求

public interface IDepositRepository {
    void getDepositInfo();
}
public interface OnDepositLoadListener {
    void onLoadDepositSuccess(MyDepositModel model);
}
public class DepositRepositoryImpl implements IDepositRepository {

    private OnDepositLoadListener mOnDepositLoadListener;

    public DepositRepositoryImpl(OnDepositLoadListener onDepositLoadListener) {
        mOnDepositLoadListener = onDepositLoadListener;
    }

    public void getDepositInfo() {
        new HttpTask() {
            @Override
            public void onRequestSuccess(MyDepositModel model) {
                mOnDepositLoadListener.onLoadDepositSuccess(model);
            }
        }.path("http://xxxx/getMydeposit").execute();
    }
}

MyDepositModel用于存储数据

public class MyDepositModel {
    public String moneyPaied;// 已经缴纳押金时,该字段表示已经缴纳的金额
    public String moneyNeed; // 未缴纳押金时,该字段表示需要缴纳的金额
    public String isDepositPay;// 是否缴纳押金,1:是,0:否
    public String isAuth; // 是否实名认证,1:是,0:否

    public static MyDepositModel mock() {
        MyDepositModel model = new MyDepositModel();
        model.moneyPaied = "200.00";
        model.moneyNeed = "300.00";
        model.isDepositPay = "0";
        model.isAuth = "0";
        return model;
    }

    public boolean isDepositPay() {
        return "1".equals(isDepositPay);
    }

    public boolean isAuth() {
        return "1".equals(isAuth);
    }
}

在MainActivity中调用IDepositRepository请求数据,通过OnDepositLoadListener获取请求成功后的数据,根据数据展示不同的view

public class MainActivity extends AppCompatActivity implements OnDepositLoadListener {

    @BindView(R.id.tv_title)
    TextView mTvTitle;
    @BindView(R.id.tv_money)
    TextView mTvMoney;
    @BindView(R.id.tv_tips)
    TextView mTvTips;
    @BindView(R.id.btn_pay_or_return)
    Button mBtnPayOrReturn;

    private IDepositRepository mIDepositRepositoryImpl;

    ///////////////////////////////////////////////////////////////////////////
    // 缴纳押金,退还押金的点击事件
    ///////////////////////////////////////////////////////////////////////////
    private View.OnClickListener mDepositPayClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "缴纳押金", Toast.LENGTH_SHORT).show();
        }
    };

    private View.OnClickListener mDepositReturnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "退还押金", Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        mIDepositRepositoryImpl = new DepositRepositoryImpl(this);
        requestData();
    }

    ///////////////////////////////////////////////////////////////////////////
    // 模拟请求网络数据
    ///////////////////////////////////////////////////////////////////////////
    private void requestData() {
        mIDepositRepositoryImpl.getDepositInfo();
    }

    ///////////////////////////////////////////////////////////////////////////
    // 请求数据后的回调
    ///////////////////////////////////////////////////////////////////////////
    @Override
    public void onLoadDepositSuccess(MyDepositModel model) {
        showMydepositView(model);
    }

    ///////////////////////////////////////////////////////////////////////////
    // 根据数据的不同状态展示不同的view
    ///////////////////////////////////////////////////////////////////////////
    private void showMydepositView(MyDepositModel model) {
        if (model.isAuth()) {
            // 已经实名认证
            showAuthView();
        } else if (model.isDepositPay()) {
            // 已经缴纳押金
            showDepositPaiedView(model);
        } else {
            // 未缴纳押金
            showDepositNoPaiedView(model);
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // 展示未缴纳押金view
    ///////////////////////////////////////////////////////////////////////////
    private void showDepositNoPaiedView(MyDepositModel model) {
        // title
        mTvTitle.setText("您需要缴纳押金");

        // money
    mTvMoney.setTextColor(getResources().getColor(android.R.color.darker_gray));
        mTvMoney.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 30);
        mTvMoney.setText("¥ " +model.moneyNeed);

        // tips
        mTvTips.setText("押金随时可退");

        //button
        mBtnPayOrReturn.setText("缴纳押金");
        mBtnPayOrReturn.setOnClickListener(mDepositPayClickListener);
    }

    ///////////////////////////////////////////////////////////////////////////
    // 展示缴纳押金view
    ///////////////////////////////////////////////////////////////////////////
    private void showDepositPaiedView(MyDepositModel model) {
        // title
        mTvTitle.setText("您当前押金");

        // money
mTvMoney.setTextColor(getResources().getColor(android.R.color.holo_red_light));
        mTvMoney.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 40);
        mTvMoney.setText("¥ " + model.moneyPaied);

        // tips
        mTvTips.setVisibility(View.INVISIBLE);

        //button
        mBtnPayOrReturn.setText("退还押金");
        mBtnPayOrReturn.setOnClickListener(mDepositReturnClickListener);
    }

    ///////////////////////////////////////////////////////////////////////////
    // 展示已实名认证view
    ///////////////////////////////////////////////////////////////////////////
    private void showAuthView() {
        // title
        mTvTitle.setText("您已享受免押金服务");

        // money
        mTvMoney.setVisibility(View.INVISIBLE);

        // tips
        mTvTips.setText("您已完成实名认证");

        //button
        mBtnPayOrReturn.setVisibility(View.INVISIBLE);
    }
}

效果图
免押金.png
未缴纳.png
已缴纳.png

项目结构:
model:MydepositModel作为数据的载体,Repository负责从网络获取数据,两者共同承担着model的职责;
view:activity_main.xml负责view的展示形式;
control:MainActivity负责接收view的交互请求,提交给model;当model发生变化时操作view,更新展示逻辑。

mvc1.png
mvc2.png

Activity:view的容器,控制生命周期,页面交互与事件处理
xml:view展示与布局
view逻辑:操作view进行更新,如setText,setVisible等
业务逻辑:model更新,根据返回数据,执行逻辑主线,如:已/未缴纳/已认证
Repository:数据中心(server,sqlite)
model:存储数据
交互逻辑:用户操作view,产生事件与数据,反向传递给model进行处理,如setOnclick,或在EditText中输入内容提交server等

总结:

xml作为view层,控制能力太弱,如果要去动态的改变一个Textview的字号,色值,或者隐藏/显示一个按钮,这些都没办法在xml中做,只能把代码写在Activity中。MyDepositModel以后,需要根据isAuth,isDepositPaied等业务逻辑,控制view的展示。造成了Activity既是view层,又是controller层,导致代码膨胀,当业务复杂度继续增加时,一个Activity上千行代码是很常见的,大量逻辑参与其中,维护及代码阅读难度将不断提升。
view和model直接交互,如:mTvMoney.setText(model.moneyNeed),耦合较重,无法独立变化。mTvMoney作为一个Textview,只需要提供通过setText方法将String设置到TextView上进行展示的一种能力,至于这个String是从model1,还是model2中获取的,mTvMoney并不关心,而model作为数据源,也同样不需要关心当前是展示在mTvMoney上,还是mTvTips上。mBtnPayOrReturn按钮也是一样,只需提供一种点击响应的能力,至于点击后是操作缴纳押金,还是退还押金,mBtnPayOrReturn并不关心。

MVP的实现方式1

在view和model之间新增presenter作为沟通的桥梁,presenter从model获取数据后,更新view的展示,使得view和model之间没有耦合,也将业务逻辑从view上抽离出来。

实现MainPresenter,持有IMainView,IDepositRepository成员变量,获取数据,根据业务逻辑更新view的展示。

public interface IMainPresenter {
    /**
     * @desc 进入页面后刷新数据
     */
    void requestData();

    /**
     * @desc 点击按钮
     */
    void onButtonClickAction();
}
/**
 * @author listen
 * @desc 主页面的presenter
 */
public class MainPresenter implements IMainPresenter, OnDepositLoadListener {

    private IMainView mIMainView;
    private IDepositRepository mIDepositRepositoryImpl;
    private MyDepositModel mModel;

    public MainPresenter(IMainView iMainView) {
        mIMainView = iMainView;
        mIDepositRepositoryImpl = new DepositRepositoryImpl(this);
    }

    @Override
    public void requestData() {
        mIDepositRepositoryImpl.getDepositInfo();
    }

    /**
     * @desc 实现OnDepositLoadListener回调,获取MyDepositModel,并根据业务逻辑更新view的展示
     */
    @Override
    public void onLoadDepositSuccess(MyDepositModel model) {
        if (model.isAuth()) {
            // 已经实名认证
            showAuthView();
        } else if (model.isDepositPay()) {
            // 已经缴纳押金
            showDepositPaiedView(model);
        } else {
            // 未缴纳押金
            showDepositNoPaiedView(model);
        }
    }

    /**
     * @desc 未支付状态view
     */
    private void showDepositNoPaiedView(MyDepositModel model) {
        // title
        mIMainView.setTitleText("您需要缴纳押金"); // 不暴露mTvTitle,只提供设置title文案的能力

        // money
        mIMainView.setMoneyTextVisible();// 提供操作MoneyText显示/隐藏的能力
        mIMainView.setMoneyTextColorGray();// 提供MoneyText字体设置为灰色的能力
        mIMainView.setMoneyTextSizeSmall();// 提供MoneyText字号设置小的能力
        mIMainView.setMoneyText("¥ " + model.moneyNeed);// 提供设置MoneyText文案的能力

        // tips
        mIMainView.setTipsTextVisible();// 提供操作TipsText显示/隐藏的能力
        mIMainView.setTipsText("押金随时可退");// 提供设置TipsText文案的能力

        //button
        mIMainView.setButtonVisible();// 提供操作Button显示/隐藏的能力
        mIMainView.setButtonText("缴纳押金");// 提供设置Button文案的能力
    }

    /**
     * @desc 支付状态view
     */
    private void showDepositPaiedView(MyDepositModel model) {
        // title
        mIMainView.setTitleText("您当前押金");

        // money
        mIMainView.setMoneyTextVisible();
        mIMainView.setMoneyTextColorRed();
        mIMainView.setMoneyTextSizeBig();
        mIMainView.setMoneyText("¥ " + model.moneyNeed);

        // tips
        mIMainView.setTipsTextInvisible();

        //button
        mIMainView.setButtonVisible();
        mIMainView.setButtonText("退还押金");
    }

    /**
     * @desc 实名认证状态view
     */
    private void showAuthView() {
        // title
        mIMainView.setTitleText("您已享受免押金服务");

        // money
        mIMainView.setMoneyTextInvisible();

        // tips
        mIMainView.setTipsTextVisible();
        mIMainView.setTipsText("您已完成实名认证");

        //button
        mIMainView.setButtonInvisible();
    }

    /**
     * @desc 当点击Button时触发的操作
     */
    @Override
    public void onButtonClickAction() {
        if (mModel.isDepositPay()) {
            mIMainView.showToast("退还押金");
        } else {
            mIMainView.showToast("缴纳押金");
        }
    }
}

实现view层

/**
 * @author listen
 * @desc 主页面view层接口
 */
public interface IMainView {
    void setTitleText(String text);

    void setMoneyTextColorGray();
    void setMoneyTextSizeSmall();
    void setMoneyText(String text);
    void setMoneyTextInvisible();
    void setMoneyTextVisible();
    void setMoneyTextColorRed();
    void setMoneyTextSizeBig();

    void setTipsText(String text);
    void setTipsTextInvisible();
    void setTipsTextVisible();

    void setButtonText(String text);
    void setButtonInvisible();
    void setButtonVisible();

    void showToast(String text);
}
public class MainActivity extends AppCompatActivity implements IMainView {
    @BindView(R.id.tv_title)
    TextView mTvTitle;
    @BindView(R.id.tv_money)
    TextView mTvMoney;
    @BindView(R.id.tv_tips)
    TextView mTvTips;
    @BindView(R.id.btn_pay_or_return)
    Button mBtnPayOrReturn;

    private IMainPresenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        mPresenter = new MainPresenter(this);
        mPresenter.requestData();

        mBtnPayOrReturn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.onButtonClickAction();
            }
        });
    }

    @Override
    public void setTitleText(String text) {
        mTvTitle.setText(text);
    }

    @Override
    public void setMoneyTextColorGray() {
        mTvMoney.setTextColor(getResources().getColor(android.R.color.darker_gray));
    }

    @Override
    public void setMoneyTextColorRed() {
        mTvMoney.setTextColor(getResources().getColor(android.R.color.holo_red_light));
    }

    @Override
    public void setMoneyTextSizeSmall() {
        mTvMoney.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 30);
    }

    @Override
    public void setMoneyTextSizeBig() {
        mTvMoney.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 40);
    }

    @Override
    public void setMoneyText(String text) {
        mTvMoney.setText(text);
    }

    @Override
    public void setMoneyTextInvisible() {
        mTvMoney.setVisibility(View.VISIBLE);
    }

    @Override
    public void setMoneyTextVisible() {
        mTvMoney.setVisibility(View.VISIBLE);
    }

    @Override
    public void setTipsText(String text) {
        mTvTips.setText(text);
    }

    @Override
    public void setTipsTextInvisible() {
        mTvTips.setVisibility(View.INVISIBLE);
    }

    @Override
    public void setTipsTextVisible() {
        mTvTips.setVisibility(View.VISIBLE);
    }

    @Override
    public void setButtonText(String text) {
        mBtnPayOrReturn.setText(text);
    }

    @Override
    public void setButtonInvisible() {
        mBtnPayOrReturn.setVisibility(View.INVISIBLE);
    }

    @Override
    public void setButtonVisible() {
        mBtnPayOrReturn.setVisibility(View.VISIBLE);
    }

    @Override
    public void showToast(String text) {
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
    }
}

mvp项目结构.png

mvp2.png

总结

presenter处理业务逻辑并更新view,Activity只提供基础的操作view的能力,2者互相独立,view与业务分离。
业务变化1:不管已缴纳,未缴纳,免押金任何状态tipsText都不显示,此时就不需要去修改Activity,直接在presenter设置mIMainView.setTipsTextVisible()即可;
业务变化2:新增一种状态,已实名认证,不过某些条件不满足,押金不能全免,只能减半,titleText显示”已认证,还需缴纳押金”,moneyText大号字体,红色,tipsText显示”押金已减半”,Button显示文案”补足押金”,这种场景下,就不用去修改Activity的任何代码,只要在presenter新增逻辑分支,根据view提供的能力进行更新即可。

/**
 * @desc 押金减半状态view
 */
private void showDepositHalfPayView(MyDepositModel model) {
   // title
   mIMainView.setTitleText("已认证,还需缴纳押金");

   // money
   mIMainView.setMoneyTextVisible();
   mIMainView.setMoneyTextColorRed();
   mIMainView.setMoneyTextSizeBig();
   mIMainView.setMoneyText("¥ " + model.moneyNeed);

   // tips
   mIMainView.setTipsTextVisible();
   mIMainView.setTipsText("押金已减半");

   //button
   mIMainView.setButtonVisible();
   mIMainView.setButtonText("补足押金");
}
/**
 * @desc 押金减半时,button的点击响应
 */
public void onButtonClickAction() {
        if ("押金减半") {
            mIMainView.showToast("补足押金");
        } 
    }

业务变化3:presenter依赖的是IMainView,不管是MainActivity,还是Main1Activity,只要是实现了IMainView即可复用当前presenter。

当我们把业务逻辑抽取到presenter后,Activity基本上只剩下一些view的逻辑,真正实现了减负,变成了一个相对纯净的view。当我们需要修改view的逻辑时,就去找Activity,需要修改数据逻辑时,就去找Repository,修改业务逻辑时就去找presenter,每个模块职责分明。
缺点:
1.view与presenter之间交互过于频繁,Activity中都是一些setText,setVisibility等方法。这时很容易让人想到使用Databinding可以很好的简化这部分代码。

MVP的实现方式2

通过DataBinding实现model到view的单向绑定,减少view与model之间因频繁交互而产生的冗余代码。

在标签中引入data=MyDepositModel,presenter=IMainPresenter。当model变化时,通过data将数据映射到view上。当Button产生点击事件时交由presenter响应并处理。使用Databinding以后,开发流程上省略了findView,setView的过程,在写xml的时候就可以直接将model进行关联及映射。

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable name="data" type="com.listen.test_mvvm.model.data.MyDepositModel"/>
        <variable name="presenter" type="com.listen.test_mvvm.presenter.IMainPresenter"/>
        <import type="android.view.View"/>
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingLeft="30dp"
        android:paddingRight="30dp"
        tools:context="com.listen.test_mvvm.view.MainActivity">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:textColor="@android:color/black"
            android:textSize="20sp"
            android:text="@{data.title}"/>

        <TextView
            android:id="@+id/tv_money"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="50dp"
            android:layout_marginTop="50dp"
            android:textColor="@{data.isDepositPay ? @android:color/holo_red_light : @android:color/darker_gray}"
            android:textSize="@{data.isDepositPay ? @dimen/sp_40 : @dimen/sp_30}"
            android:visibility="@{data.isAuth ? View.INVISIBLE : View.VISIBLE}"
            android:text="@{data.money}"/>

        <TextView
            android:id="@+id/tv_tips"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            android:visibility="@{data.showTips ? View.VISIBLE : View.INVISIBLE}"
            android:text="@{data.tips}"/>

        <Button
            android:id="@+id/btn_pay_or_return"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="30dp"
            android:layout_marginTop="100dp"
            android:onClick="@{presenter.onButtonClickAction}"
            android:visibility="@{data.isAuth ? View.INVISIBLE : View.VISIBLE}"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            android:text='@{data.isDepositPay ? "退还押金" : "缴纳押金"}'/>
    </LinearLayout>
</layout>

将业务逻辑转移到MyDepositModel

public class MyDepositModel {
    public String moneyPaied;// 已经缴纳押金时,该字段表示已经缴纳的金额
    public String moneyNeed; // 未缴纳押金时,该字段表示需要缴纳的金额
    public String isDepositPay;// 是否缴纳押金,1:是,0:否
    public String isAuth; // 是否实名认证,1:是,0:否

    public boolean isDepositPay() {
        return "1".equals(isDepositPay);
    }

    public boolean isAuth() {
        return "1".equals(isAuth);
    }

    public String getTitle() {
        if (isAuth()) {
            return "您已享受免押金服务";
        }

        if (isDepositPay()) {
            return "您当前押金";
        } else {
            return "您需要缴纳押金";
        }
    }

    public String getMoney() {
        if (isDepositPay()) {
            return "¥ " + moneyPaied;
        } else {
            return "¥ " + moneyNeed;
        }
    }

    public String getTips() {
        if (isAuth()) {
            return "您已完成实名认证";
        }

        if (!isDepositPay()) {
            return "押金随时可退";
        }

        return "";
    }

    public boolean isShowTips() {
        if (isAuth() || !isDepositPay()) {
            return true;
        }
        return false;
    }
}

MainPresenter不再与view频繁的交互,仅仅是作为view和model的连接器,主干逻辑更为清晰

public class MainPresenter implements IMainPresenter, OnDepositLoadListener {
    private IMainView mIMainView;
    private IDepositRepository mIDepositRepository;
    private MyDepositModel mModel;

    public MainPresenter(IMainView iMainView) {
        mIMainView = iMainView;
        mIDepositRepository = new DepositRepository(this);
    }

    // 请求数据
    @Override
    public void requestData() {
        mIDepositRepository.getDepositInfo();
    }

    // 获取数据,通知view更新
    @Override
    public void onLoadDepositSuccess(MyDepositModel model) {
        mModel = model;
        mIMainView.updateData(model);
    }

    // 接收并处理view的点击事件
    @Override
    public void onButtonClickAction(View v) {
        if (mModel.isDepositPay()) {
            mIMainView.showToast("退还押金");
        } else {
            mIMainView.showToast("缴纳押金");
        }
    }
}

MainActivity中不再需要fingViewById,也不用定义Textview,Button的成员变量,全部交由DataBinding进行处理,相较MVP的实现,MainActivity进一步简化

public class MainActivity extends AppCompatActivity implements IMainView {
    private IMainPresenter mPresenter;
    private ActivityMainBinding mBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mPresenter = new MainPresenter(this);
        mBinding.setPresenter(mPresenter);

        // 初始化页面数据
        mPresenter.requestData();
    }

    // 更新数据绑定
    @Override
    public void updateData(MyDepositModel model) {
        mBinding.setData(model);
    }

    // 提供Toast提示的能力
    @Override
    public void showToast(String text) {
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
    }
}

IMainView接口也不再需要提供那么多操作view的方法

public interface IMainView {
    void updateData(MyDepositModel model);
    void showToast(String text);
}

问题:
1.xml中参杂了一些业务逻辑,如:data.isDepositPay,data.isAuth,xml中应该尽量只是简单的view逻辑,与业务逻辑隔离。
2.由于使用databinding是model->view的单向绑定,不得不将大部分逻辑搬移到model中,例如:MyDepositModel中即有数据处理逻辑,isDepositPay,isAuth(如果model中存在list等,经常会对外提供getItemById(int id)等方法,做遍历查询)。同时还存在view的展示逻辑,例:isShowTips,getTitle,getMoney,这些方法都是根据数据变化控制view的展示,两者之间其实还是有比较明确的分界线,可以进一步分离,解耦,避免model过重。

MVPVM的实现方式

通过viewModel作为model和view的适配层,model只负责数据存储

activity_main.xml中将原先的model.isDepositPay(),model.isAuth()改成viewModel.moneyTextVisible(),viewModel.moneyTextSizeLarge()等。在xml中依赖viewModel,只关心view显示/隐藏,字号变大/小,色值高亮/正常,至于什么情况下展示高亮,是否显示由viewModel中适配的model逻辑决定。

<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable name="data" type="com.listen.test_mvvm.model.viewmodel.IMyDepositViewModel"/>
        <variable name="presenter" type="com.listen.test_mvvm.presenter.IMainPresenter"/>
        <import type="android.view.View"/>
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingLeft="30dp"
        android:paddingRight="30dp"
        tools:context="com.listen.test_mvvm.view.MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:text="@{data.title}"
            android:textColor="@android:color/black"
            android:textSize="20sp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="50dp"
            android:layout_marginTop="50dp"
            android:text="@{data.money}"
            android:textColor="@{data.moneyTextColorHightLight ? @android:color/holo_red_light : @android:color/darker_gray}"
            android:textSize="@{data.moneyTextSizeLarge ? @dimen/sp_40 : @dimen/sp_30}"
            android:visibility="@{data.moneyTextVisible ? View.VISIBLE : View.INVISIBLE}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.tips}"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            android:visibility="@{data.tipsVisible ? View.VISIBLE : View.INVISIBLE}"/>

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="30dp"
            android:layout_marginTop="100dp"
            android:onClick="@{presenter.onButtonClickAction}"
            android:text="@{data.buttonText}"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            android:visibility="@{data.buttonVisible ? View.VISIBLE : View.INVISIBLE}"/>
    </LinearLayout>
</layout>

IMyDepositViewModel接口,定义view提供的能力

public interface IMyDepositViewModel {
    String getTitle();

    boolean isMoneyTextColorHightLight();
    boolean isMoneyTextSizeLarge();
    boolean isMoneyTextVisible();
    String getMoney();

    boolean isTipsVisible();
    String getTips();

    boolean isButtonVisible();
    String getButtonText();
}

MyDepositBaseViewModel实现IMyDepositViewModel的默认展示逻辑

public abstract class MyDepositBaseViewModel implements IMyDepositViewModel {

    private MyDepositModel mModel;

    public MyDepositBaseViewModel(MyDepositModel model) {
        mModel = model;
    }

    public MyDepositModel getModel() {
        return mModel;
    }

    @Override
    public String getTitle() {
        return "";
    }

    @Override
    public boolean isMoneyTextColorHightLight() {
        return false;
    }

    @Override
    public boolean isMoneyTextSizeLarge() {
        return false;
    }

    @Override
    public boolean isMoneyTextVisible() {
        return false;
    }

    @Override
    public String getMoney() {
        return "";
    }

    @Override
    public boolean isTipsVisible() {
        return false;
    }

    @Override
    public String getTips() {
        return "";
    }

    @Override
    public boolean isButtonVisible() {
        return false;
    }

    @Override
    public String getButtonText() {
        return "";
    }
}

已缴纳押金时viewModel的展示逻辑

public class MyDepositPayViewModel extends MyDepositBaseViewModel {
    public MyDepositPayViewModel(MyDepositModel model) {
        super(model);
    }

    @Override
    public String getTitle() {
        return "您当前押金";
    }

    @Override
    public String getMoney() {
        return "¥ " + getModel().moneyPaied;
    }

    @Override
    public boolean isMoneyTextVisible() {
        return true;
    }

    @Override
    public boolean isMoneyTextColorHightLight() {
        return true;
    }

    @Override
    public boolean isMoneyTextSizeLarge() {
        return true;
    }

    @Override
    public boolean isButtonVisible() {
        return true;
    }

    @Override
    public String getButtonText() {
        return "退还押金";
    }
}

未缴纳押金时viewModel的展示逻辑

public class MyDepositNoPayViewModel extends MyDepositBaseViewModel {

    public MyDepositNoPayViewModel(MyDepositModel model) {
        super(model);
    }

    @Override
    public String getTitle() {
        return "您需要缴纳押金";
    }

    @Override
    public String getMoney() {
        return "¥ " + getModel().moneyNeed;
    }

    @Override
    public boolean isMoneyTextVisible() {
        return true;
    }

    @Override
    public boolean isMoneyTextColorHightLight() {
        return false;
    }

    @Override
    public boolean isMoneyTextSizeLarge() {
        return false;
    }

    @Override
    public boolean isButtonVisible() {
        return true;
    }

    @Override
    public boolean isTipsVisible() {
        return true;
    }

    @Override
    public String getTips() {
        return "押金随时可退";
    }

    @Override
    public String getButtonText() {
        return "缴纳押金";
    }
}

已认证时viewModel的展示逻辑

public class MyDepositAuthViewModel extends MyDepositBaseViewModel {

    public MyDepositAuthViewModel(MyDepositModel model) {
        super(model);
    }

    @Override
    public String getTitle() {
        return "您已享受免押金服务";
    }

    @Override
    public boolean isTipsVisible() {
        return true;
    }

    @Override
    public String getTips() {
        return "您已完成实名认证";
    }
}

MainPresenter获取数据后,根据不同业务逻辑展示
MyDepositAuthViewModel,MyDepositPayViewModel,MyDepositNoPayViewModel。此处有点像设计模式中的策略模式,这3个viewModel就是view的不同展示策略的封装。

public class MainPresenter implements IMainPresenter, OnDepositLoadListener {

    private IMainView mIMainView;
    private IDepositRepository mIDepositRepositoryImpl;
    private MyDepositModel mModel;

    public MainPresenter(IMainView iMainView) {
        mIMainView = iMainView;
        mIDepositRepositoryImpl = new DepositRepositoryImpl(this);
    }

    @Override
    public void requestData() {
        mIDepositRepositoryImpl.getDepositInfo();
    }

    @Override
    public void onLoadDepositSuccess(MyDepositModel model) {
        mModel = model;
        if (mModel.isAuth()) {
            mIMainView.updateData(new MyDepositAuthViewModel(model));
        } else if (mModel.isDepositPay()) {
            mIMainView.updateData(new MyDepositPayViewModel(model));
        } else {
            mIMainView.updateData(new MyDepositNoPayViewModel(model));
        }
    }

    @Override
    public void onButtonClickAction(View v) {
        if (mModel.isDepositPay()) {
            mIMainView.showToast("退还押金");
        } else {
            mIMainView.showToast("缴纳押金");
        }
    }
}

MainActivity.java,只做基本的数据请求,DataBinding初始化,toast提示等操纵。

public class MainActivity extends AppCompatActivity implements IMainView {
    private IMainPresenter mPresenter;
    private ActivityMainBinding mBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mPresenter = new MainPresenter(this);
        mBinding.setPresenter(mPresenter);
        mPresenter.requestData();
    }

    @Override
    public void updateData(IMyDepositViewModel viewModel) {
        mBinding.setData(viewModel);
    }

    @Override
    public void showToast(String text) {
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
    }
}

mvpvm.png
mvpvm2.png
如图:用户操作view,触发事件响应,通过presenter中转,传递给model进行数据处理,获取新数据后处理业务逻辑,并适配成不同状态的viewModel展示策略,view根据不同的viewModel进行更新。

总结:
从mvc到mvpvm,项目中类虽然变多了,不过模块之间职责更加明确清晰。大部分情况,使用mvp结合databinding就可以较好的对view和model进行解耦,且代码冗余较少,当然在页面逻辑简单的情况下,可能连Presenter都没有用上的必要。不过如果是类似本文中的需求,view状态相对复杂的情况下,最好还是经过一层viewModel适配,也可以释放model的压力,xml布局中只依赖抽象的IMyDepositViewModel(model->view的数据输入)和IMainPresenter(view->model的事件输出),不依赖具体。
本文并非按照传统的MVC,MVP,MVVM的路线实现架构,而是采用循序渐进的方式,在MVC中发现Activity过重,所以引入MVP,Presenter作为View和Model的中转,达到解耦的目的。后来发现Activity提供view能力时冗余代码过多,所以引入DataBinding,虽然代码简化了,不过xml中引入了部分业务逻辑,model中同时参杂数据处理逻辑和view展示逻辑,故而引入viewModel,将xml与model进一步解耦,同时减轻model负担,不过此时并不算是mvvm,本质上在mvp的基础上,引入vm,因此presenter的中转作用还在,所以才演变成了现在的mvpvm。同时强调下,架构无绝对的好坏与绝对的标准,大家应该在项目中根据实际场景选择最合适的架构方式。本文中如有说明,解释不到位的地方,还请指出,互相学习共勉。

最终版本项目地址:https://github.com/listen2code/Test_MVPVM

从零开始的Android新项目3 - 谁告诉你MVP和MVVM是互斥的 去年5月左右的时候,笔者在逛GitHub的时候,看到了一个MVP的项目,叫做mosby,仔细看了源码和作者介绍的文章后,发现确实有点意思,虽然会需要多写几个类和方法,但是解决了activity/fragment过重的问题,通过V/P分离能够帮助提高可维护性。时至去年年底,今年年初,MVP才逐渐被大家所知,也不时看到些文章介绍其概念和实践。再说说MVVM (Model-View-ViewModel), 阅读详情

相关推荐

android MVP和MVPVM 架构Demo

android MVP和MVPVM 架构Demo

MVPVM模式介绍

一、概述 MVPVM即:Model-View-Presenter-ViewModel。此模式是MVVM和MVP模式的结合体。但是交互模式发生了比较大的变化。 MVVM参考本博客文章: iOS-MVVM-模式介绍 MVP参考本博客文章: MVP模式介绍  二、原理: Presenter同时持有View、

有志者事竟成-程序员也可以创造优雅的艺术 1615

Android MVPVM架构

一个基于MVPVM的架构,就是MVP+MVVM,Model层实现数据处理,ViewModel层DataBinding数据绑定,View只处理UI,详细流程可以看HomeFragment类

Android MVPVM架构实践

简书地址:http://www.jianshu.com/p/b53e1450ca8f 前言写Android也有一段时间了,始终没有找到一种优雅流畅的Android架构模式,前不久看了google关于mvp架构的范例,甚好,建议仔细观摩一下:https://github.com/googlesamples/android-architecture。这篇文章也是参考了google的写法加上自己的理解和实践.........

myterabithia的博客 1163

MVP-VM实践

引入VIewModel 减轻对P对V的操作 protected fun vmProviders(modelClass: Class): T { val viewModel: BaseViewModel if (mContext is FragmentActivity || mContext is Fragment) { viewModel = if (mContext is BaseActivity) { ViewModelProviders.of(mContext as FragmentActivity).get(modelClass) } else { ViewModelProviders.of(mContext as Fragment).get(modelClass) } } else { viewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(App.getInstance()).create(modelClass) } // 监听viewModel被销毁时 也销毁presenter viewModel.setClearedListener(object : BaseViewModel.ViewModelClearedListener { override fun onCleared() { onDestroy() } }) initCommon(viewModel) return viewModel

旋转矩阵的左乘与右乘

旋转矩阵左右乘的详细讲解

qq_46619194的博客 3330

一、什么是 mvvm? MVC、MVP、MVVM三种模式的区别与详解

MVC、MVP、MVVM都是常见的软件架构模式。MVC(Model-View-Controller)架构模式中,将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller)。Controller负责处理用户交互和调度业务逻辑,View负责显示数据,Model负责数据的存储和逻辑处理。MVP(Model-View-Presenter)架构模式,类似于MVC,但将View和Model的交互逻辑抽象到了Presenter中。

qq_44849312的博客 1万+

理想汽车智驾方案介绍 4 World model + 强化学习重建自动驾驶交互环境

本帖将以 DrivingSphere 为例来解析这个过程。

地平线开发者 1921

vue常见问题 MVVM、MVC、MVP的区别总结

vue常见问题 MVVM、MVC、MVP的区别总结1.MVC2.MVVM3.MVP 什么是MVC、MVP、MVVM? MVC、MVP和MVVM是三种常见的软件架构设计模式,主要通过分离关注点的方式来组织代码结构,优化开发效率。 在开发单页面应用时,往往一个路由页面对应了一个脚本文件,所有的页面逻辑都在一个脚本文件里。页面的渲染、数据的获取,对用户事件的响应所有的应用逻辑都混合在一起,这样在开发简单项目时,可能看不出什么问题,如果项目变得复杂,那么整个文件就会变得冗长、混乱,这样对项目开发和后期的项目维护是

$狴犴^的博客 737

基于STM32F103C8T6单片机设计CAN接口8路继电器控制板硬件(原理图+PCB)+软件源码

基于STM32F103C8T6单片机设计CAN接口8路继电器控制板硬件(原理图+PCB)+软件源码,仅供学习及设计参考。

GJZGRB的博客 1418

从仿真到部署:基于ROS2与Gazebo的UR5e机械臂全流程控制实践

本文详细介绍了基于ROS2与Gazebo的UR5e机械臂全流程控制实践,从环境准备、模型构建到控制器开发和仿真优化,最终实现真实机器人部署。通过实战案例和避坑指南,帮助开发者掌握机器人仿真与控制的关键技术,提升UR5e机械臂在物料分拣等任务中的性能与稳定性。

weixin_29184371的博客 288

MVC、MVP以及MVP+VM

文章目录MVC模式MVP模式MVP+VM模式总结 MVC模式 MVC模式的结构分为三部分,实体层Model,视图层View和控制层Controller。 工作原理:当用户触发事件,View层会发出指令到Controller层,由Controller层去通知Model层更新数据,Model层更新完数据后直接显示在View层上。 在Android工程中,View层对应xml布局文件,Controll...

陈德山 793

脑电分析:为什么大脑信号会“变同步”?——事件相关同步化与去同步化 (ERS/ERD) 全景解析

脑电分析中的事件相关同步化(ERS)和去同步化(ERD)是研究大脑动态节律的重要方法,反映特定任务下脑电频段能量的变化。ERD表示神经元活动分散(功率下降),提示脑区活跃处理信息;ERS则反映神经元同步(功率增强),常与静息状态相关。本文详解了ERS/ERD的生理机制、计算流程(包括基线校正和功率谱分析),并提供了Python实现示例,展示如何通过Welch法和STFT进行时频分析,生成ERD/ERS热力图。该技术广泛应用于运动想象脑机接口、癫痫检测等领域,为理解大脑功能状态提供了量化工具。

qq_32516809的博客 1398

java mvc mvp_MVC,MVP,MVPVM(一)实践之路

文章目录简介分别使用MVC,MVP,MVP+VM,实践具体需求,对比优劣,逐步优化。需求实现我的押金页面,包含未缴纳,已缴纳,免押金3种状态1.顶部title:3种状态展示不同文案;2.金额:已缴纳,未缴纳状态金额字号,色值不同;免押金状态不展示;3.底部tips:已缴纳,免押金状态展示不同文案;已缴纳状态,不展示;4.按钮:未缴纳,已缴纳状态,文案,及点击事件都不相同;我的押金页面.pngMVC...

weixin_42367582的博客 236

ESP32 TWAI CAN库实战:从零构建CyberGear微电机控制框架

本文详细介绍了如何利用ESP32的TWAI控制器和Arduino环境,从零构建一个面向对象的CyberGear微电机控制框架。该框架将复杂的CAN通信协议封装为简洁的API,开发者无需深究底层细节,即可轻松实现电机的速度、位置和扭矩控制,大幅简化机器人关节、云台等项目的开发流程。

oil88的博客 499

基于 Qt 的轻量化工业组态软件设计与实现

本文设计并实现了一种基于Qt框架的轻量化工业组态软件,解决了传统组态软件体积庞大、部署复杂等问题。该软件采用模块化架构,集成可视化图形编辑、设备数据采集、脚本扩展等核心功能,具有跨平台运行、资源占用低、启动速度快等特点。通过关键技术优化,实现了高效稳定的数据采集、脚本控制和网络通信,可广泛应用于工业机器人HMI、小型PLC监控等轻量化场景。测试结果表明,该软件体积约30MB,运行内存占用60-80MB,启动时间1.2秒,满足工业轻量化监控需求。未来可扩展更多工业组件和协议支持,进一步提升应用价值。

谢谢大家的关注和点赞!这里只有纯纯的知识干货,没有一句废话。希望能实实在在帮到大家~要是觉得有用,别忘了给我点支持哟,你 142

MVC,MVP,MVPVM(二)提升效率之Templates

文章目录1.MVC,MVP,MVPVM(一)实践之路 2.MVC,MVP,MVPVM(二)提升效率之Templates遗留问题 《MVC,MVP,MVPVM(一)实践之路》中讲到如何从MVC到MVPVM的转变,将各个模块分离,职责划清。不过有个缺点,就是类爆炸,为什么代码写着写着就MVC了,就是因为一个Activity搞定,写着爽。但是,如果要实现解耦,就一定意味着会有很多不同的职能类。如果采

listen2code的博客 730

WCK Operation (WCK 操作)

LPDDR6 SDRAM采用双时钟系统(WCK和CK),其中WCK频率是CK的两倍。为确保数据正确传输,需通过WCK2CK同步操作使两种时钟对齐。LPDDR6支持两种同步模式:Auto-sync off模式和WCK Always on模式,可通过MR22寄存器选择。同步操作由特定命令(Read/Write/MRR等)触发,需在满足特定时序参数(如tWCKENL、tWCKPRE等)后完成。同步前必须完成WCK2CK训练,且不同操作(读取/写入/CAS)有各自的时序要求。该机制确保数据在正确时钟相位下传输,是L

李家小院 657

java mvp mvc区别,MVC/MVP/MVVM/MVPVM区别

分析主要是通过它的控制链、控制流向,View 的变化如何反馈到Model,以及Model的变化如何作用到View上。MVCView 持有了Controller,把事件传递给Controller,Controller 由此去触发Model层的事件,Model更新完数据(网络或者本地数据)之后触发View的更新事件MVP咋看一下MVP只是MVC的变更版,把C层替换成了P层,实则不是这样的,最根本的是添...

weixin_39886469的博客 275

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

android框架mvc,mvp,mvvm,mvpvm,mvi等了解,对比

# android的框架mvc,mvp,mvvm,mvpvm,mvi等等那么多,强迫症真的不能忍,先简单对比下,然后结果还是由大家自己做吧##首先:为什么有那么多框架?     既然框架提出,产生,流行那么必定有原因的,也没必要顺我者昌,逆我者亡,先了解对比,看不顺眼就不用,也是可以的。但是不能跟别人交流或者突然接受新代码的时候,一脸懵逼,跟一实习生一样一样~~。那tmd多尴尬------##然后...

云梦九章~ 2万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

备战面试:MVC MVP MVVM MVPVM应用架构模式区别

分析主要是通过它的控制链、控制流向,View 的变化如何反馈到Model,以及Model的变化如何作用到View上。 MVC View 持有了Controller,把事件传递给Controller,Controller 由此去触发Model层的事件,Model更新完数据(网络或者本地数据)之后触发View的更新事件 MVC.png MVP MVP.png 咋看一下MVP只...

u010117864的专栏 730

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Android架构MVC,MVP与MVVM及MVPVM对比分析

前言:近年来,MVC,MVP与MVVM及MVPVM,这些名词常在我们脑海闪现,bobo_wang授权本众号独家发布,bobo_wang blog链接:http://blog.csdn.n...

欢迎关注公众号:【码农突围】,公号后台回复9999,可以获取一份500页的LeetCode刷题笔记。 466

MVVM、MVC、MVP的区别?

Controller 层是 View 层和 Model 层的纽带,它主要负责用户与应用的响应操作,当用户与页面产生交互的时候,Controller 中的事件触发器就开始工作了,通过调用 Model 层,来完成对 Model 的修改,然后 Model 层再去通知 View 层更新。页面的渲染、数据的获取,对用户事件的响应所有的应用逻辑都混合在一起,这样在开发简单项目时,可能看不出什么问题,如果项目变得复杂,那么整个文件就会变得冗长、混乱,这样对项目开发和后期的项目维护是非常不利的。

laowang357的博客 3650

从0开始搭建MVP+ViewModel框架的android应用01---MVPVM诞生记

对比MVC/MVP/MVVM MVC:经典的模式,model,view,controller,比较好理解,但是有些缺点,承担View角色的模块包含了过多的业务逻辑 MVP:衍生于MVC,虽然View和业务解耦了,但是Presenter承担了太多任务 MVVM:采用DataBinding,数据的渲染自动反映在ViewModel上,同时也可以通过ViewModel获取数据,但是业务处理堆在一块。 基于此

Yann.zhao的博客 2800

android开发模式/框架比较分析:从MVC到MVP到MVVM到Clean

开发模式对比分析 MVC MVP MVVM MVPVM CLEAN 修改 3 4 5 5 5 新增 3 5 4 5 5 复用 3 4 4 4 5 单元测试 3 4 5 5 4 UI测试 4 4 4 4 4 学习成本 3 4 5 5 5 代码复杂度 3 4 5 5 5 实现复杂度 1 3 2 4 5 代码量 1 3 2 4 5 可读性 2 4 1...

qq_23081779的博客 5172
上一篇: DataBinding学习笔记(一)源码分析
下一篇: MVC,MVP,MVPVM(二)提升效率之Templates
listen2code
博客等级 码龄10年 7粉丝 7原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值