Android应用篇 - MVC、MVP 和 MVVM

前面写的设计模式篇是代码设计上的,而对于系统设计,也有不少的模式,比如 MVC、MVP、MVVM。众所周知,GoF 总结过23个设计模式,这23个设计模式是某些特定的编程问题的特效药,这是业内公认的。 而 MVC | MVP | MVVM 是一种模式,但却在 GoF 总结出来的这个23个设计模式之外,确切的说它不是一种设计模式,它是多种设计模式的组合,并不仅仅只是一个单独的一个模式。 

目录:

  1. MVC
  2. MVP
  3. MVVM

1. MVC

  • 1.1 MVC 简介

MVC 是 Model,View 和 Controller 的意思,MVC 要实现的目标是将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。很多时候,大家都会错误的使用 MVC。View 层是界面,Model 层是业务逻辑,Controller 层用来调度 View 层和 Model 层,将用户界面和业务逻辑合理的组织在一起,起粘合剂的效果。所以 Controller 中的内容能少则少,这样才能提供最大的灵活性。

比如,有一个 View 会提交数据给 Model 进行处理以实现具体的行为,View 通常不会直接提交数据给 Model,它会先把数据提交给 Controller,然后 Controller 再将数据转发给 Model。假如此时程序业务逻辑的处理方式有变化,那么只需要在 Controller 中将原来的 Model 换成新实现的 Model 就可以了,控制器的作用就是这么简单, 用来将不同的 View 和不同的 Model 组织在一起。

各 Model 之间是可以相互调用的,Controller 也可以无障碍的调用 Model,因此将业务逻辑放在 Model 中可以灵活的使用组合的方式复用代码。

而 Controller 之间是不可以相互调用的,要复用代码只能将代码提升至父类,通过继承实现,显然这种做法既不正确,也不灵活,因此完全不提倡。

综上所述,仅仅只是代码复用这一点,也足以将"厚 Controller,薄 Model" 这种不健康的 MVC 思想打入十八层地狱。 


 

  • 1.2 Android 中的 MVC

下面这张图展示了 MVC 在 Android 中的使用:

  • 最上面一层,是直接面向于最终用户的"视图层" (View)。它是提供给用户的操作界面,是程序的外壳。界面就是各种 UI 组件(XML 布局或者 Java 自定义控件对象)。只负责展示数据,同时接收控制器传过来的结果。
  • 最底下的一层,是核心的"数据层" (Model),也就是程序需要操作的数据或信息 (系统中的业务逻辑部分)。通常是数据库SQLite、网络请求的 JSON、本地 XML 或者 Java 对象数据。它代表了一些实体类,用来描述业务逻辑怎么组合,同时也为数据定义业务规则。
  • 中间的一层,就是"控制层" (controller),负责根据用户从"视图层"输入的指令,选取"数据层"中的数据,然后对其进行相应的操作,产生最终的结果 (可以分派用户的请求并选择恰当的视图以用于显示,同时也可以解释用户的数据并将它们映射为模型层可执行的操作)。控制器是与应用程序相关联的动作集合,负责处理待响应的请求。通过界面响应用户输入,通过模型层处理数据,最后返回结果给界面。

总结下流程:

  • (1) View 接受用户的交互请求。
  • (2) View 将请求转交给 Controller。
  • (3) Controller (用户做的动作比如:update 数据,删除指定名字的学生等) 操作 Model 进行数据更新 (根据用户指示,执行底层的数据动作等等)。
  • (4) 数据更新之后,Model 通知 View 数据变化。
  • (5) View 显示更新之后的数据。

Model 层适合做一些业务逻辑处理,比如数据库存取操作、网络操作、复杂的算法等耗时操作。View 层显示数据部分,XML 布局可以视为是 View 层,显示 Model 层的数据结果。Controller 层适合使用 Activity 担当,Android 中 Activity 用于处理用户交互问题 (发起业务请求),读取用户输入 (等待业务处理结果),响应用户点击等事件。

  • 1.3 demo

来看一个例子,MvcActivity 属于 Controller,里面有 View 层 和 Model 层。

/**
 * 在 Android 中,Activity 就是 Controller 层
 */
public class MvcActivity extends Activity {

    // Model 层,如果请求方式或者类型变了,可以直接更换实现类即可
    private IMvcRequestModel<String> requestModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvc);

        // View 层
        final TextView textView = (TextView) findViewById(R.id.text);
        Button button = (Button) findViewById(R.id.button);

        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (requestModel == null)
                    requestModel = new BookRequestModel();
                requestModel.requestData("get First Java", new IResponseListener() {
                    @Override
                    public void onSuccess(final String t) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                textView.setText(t);
                            }
                        });
                    }

                    @Override
                    public void onFail(String error) {

                    }

                    @Override
                    public void onTimeout() {

                    }
                });
            }
        });
    }
}

看看 Model 层的定义:

/**
 * 通用请求数据的 model 接口
 *
 * @param <Param> 参数
 */
public interface IMvcRequestModel<Param> {

    void requestData(Param param, IResponseListener listener);
}
/**
 * 请求回调
 */
public interface IResponseListener {

    void onSuccess(String t);

    void onFail(String error);

    void onTimeout();
}
/**
 * 请求书籍的 model 实现类
 */
public class BookRequestModel implements IMvcRequestModel<String> {

    @Override
    public void requestData(final String param, final IResponseListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 模拟请求
                String response = getData(param);
                listener.onSuccess(response);
            }
        }).start();
    }

    private static String getData(String param) {
        return "First Java";
    }
}

最后执行起来,点击按钮,文本控件会显示 "First Java"。当然可以根据业务类型,多定义一些 Model 接口。

2. MVP

  • 2.1 MVP 简介

MVC 的问题就在于 xml 作为 View 层,控制能力实在太弱了,你想去动态的改变一个页面的背景,或者动态的隐藏/显示一个按钮,这些都没办法在 xml 中做,只能把代码写在 Activity 中,造成了 Activity 既是 Controller 层,又是 View 层的这样一个窘境。

大家回想一下自己写的代码,如果是一个逻辑很复杂的页面,Activity 或者 Fragment 是不是动辄上千行呢?这样不仅写起来麻烦,维护起来更是噩梦。View 层和 Model 层是相互可知的,这意味着两层之间存在耦合,耦合对于一个大型程序来说是非常致命的,因为这表示开发,测试,维护都需要花大量的精力。

MVP (Model View Presenter) 作为 MVC 的演化,解决了 MVC 不少的缺点,对于 Android 来说,MVP 的 Model 层相对于MVC 是一样的,而 Activity 和 Fragment 不再是 Controller 层,而是纯粹的 View 层,所有关于用户事件的转发全部交由Presenter 层处理。

  • 2.2 demo

先看看 View 层:

/**
 * View 层通用接口,最终由 Activity / Fragment 来实现,所以 Activity / Fragment 作为纯粹的 View 层
 * @param <T>
 */
public interface IView<T> {

    void showProgress();

    void hideProgress();

    void setData(T t);

    void showMessage(String message);
}
/**
 * 在 MVP 中,Activity 作为纯粹的 View 层,而不再是 Controller 层.
 */
public class MvpActivity extends Activity implements IView<String> {

    private TextView textView;
    private Button button;

    private Presenter presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp);

        textView = (TextView) findViewById(R.id.text1);
        button = (Button) findViewById(R.id.button1);

        presenter = new Presenter(this, new BookRequestModel());

        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onButtonClick("request First Java");
            }
        });
    }

    @Override
    public void showProgress() {
        // 显示 progress
    }

    @Override
    public void hideProgress() {
        // 隐藏 progress
    }

    @Override
    public void setData(final String s) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText(s);
            }
        });
    }

    @Override
    public void showMessage(final String message) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MvpActivity.this, message, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

模型层和 MVC 一样:

/**
 * MVP 通用 Model 接口
 * @param <Param>
 */
public interface IMvpRequestModel<Param> {

    void requestData(Param param, IResponseListener listener);
}
/**
 * 请求回调
 */
public interface IResponseListener {

    void onSuccess(String t);

    void onFail(String error);

    void onTimeout();
}
/**
 * 请求书籍的 model 实现类
 */
public class BookRequestModel implements IMvpRequestModel<String> {

    @Override
    public void requestData(final String param, final IResponseListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 模拟请求
                String response = getData(param);
                listener.onSuccess(response);
            }
        }).start();
    }

    private static String getData(String param) {
        return "First Java";
    }
}

最后看看 Presenter 层,当然也可以抽出接口或者抽象类,我这边直接写了一个:

public class Presenter {

    private final IView<String> view;
    private final IMvpRequestModel<String> model;

    public Presenter(IView<String> view, IMvpRequestModel<String> model) {
        this.view = view;
        this.model = model;
    }

    void onButtonClick(String param) {
        view.showProgress();
        model.requestData(param, new IResponseListener() {
            @Override
            public void onSuccess(String t) {
                view.hideProgress();
                view.setData(t);
            }

            @Override
            public void onFail(String error) {
                view.hideProgress();
                view.showMessage(error);
            }

            @Override
            public void onTimeout() {
                view.hideProgress();
                view.showMessage("timeout");
            }
        });
    }
}

3. MVVM

  • 3.1 MVVM 简介

MVVM 可以算是 MVP 的升级版,将 Presenter 改名为 ViewModel。关键在于 View 和 Model 的双向绑定,当 View 有用户输入后,ViewModel 通知 Model 更新数据,同理 Model 数据更新后,ViewModel 通知 View 更新。

在 Google I/O 2015 上,伴随着 Android M 预览版发布了 Data Binding 兼容函数库。

android mvvm pattern

  • 3.2 demo

在 app module 的 build.gradle 下:

android {
    // ...
    dataBinding {
        enabled true
    }
}

Model 类:

public class Book {

    private long id;
    private String name;

    public Book(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

  <data>
    <import type="io.kzw.mvcpvm.mvvm.Book" />
    <variable
      name="book"
      type="Book" />
  </data>

  <LinearLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:orientation="vertical">

    <TextView
      android:id="@+id/text2"
      android:layout_height="wrap_content"
      android:layout_width="match_parent"
      android:text="@{book.name}" />

    <Button
      android:id="@+id/button2"
      android:layout_height="wrap_content"
      android:layout_width="match_parent"
      android:text="load content" />
  </LinearLayout>
</layout>

Activity:

public class MvvmActivity extends Activity {

    private ActivityMvvmBinding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
        Book book = new Book(1, "Head Java");
        binding.setBook(book);
    }
}

关于更多 MVVM 的用法可以戳这:https://www.jianshu.com/p/c0988e7f31fd

发布了126 篇原创文章 · 获赞 215 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/u014294681/article/details/88757672