软件开发总是强调一个重点,就是尽可能的将系统做到高内聚低耦合,各模块各司其职,职责单一,逻辑清晰。
mvp框架图
从上图可以清晰的理解什么是MVP(Model、View、Presenter)架构,其实MVP是从MVC模式衍生出来的,目的就是为了让视图层view和模型层model之间解耦。它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
从职责上来说MVP是:
- View层负责处理用户事件和视图部分的展示。在Android中,它可能是Activity或者Fragment类。
- Model层负责访问数据。数据可以是远端的Server API,本地数据库或者SharedPreference等。
- Presenter层是连接(或适配)View和Model的桥梁。
MVP的优点
1.降低耦合度,隐藏数据,Activity中代码更简洁
2.模块职责划分明显,接口明确
3.方便测试驱动开发
4.代码复用度较高
5.代码灵活性
6.google推荐使用,哈哈
MVP的缺点
1.类和接口的数量会增加许多,建议简单的项目可以不使用此架构。
2.Presenter层与View层如果存在过多的联系,往往View一旦更改,Presenter也需要更改。
理论说了一堆,是时候上一波代码了:
项目结构
Model层
这里我主要是指的本地数据库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class User { private int _id; private String name; private int age; public User() { } public User(int _id, String name, int age) { this._id = _id; this.name = name; this.age = age; } public int getId() { return _id; } public void setId(int _id) { this._id = _id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
|
为了较好的维护Presenter和View之间的关系,Google推荐将其接口存放在一个契约类中管理。
BasePresenter
1 2 3
| public interface BasePresenter { void start(); }
|
BaseView
1 2 3
| public interface BaseView<T> { void setPresenter(T presenter); }
|
定义契约:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class UserContract { public interface View extends BaseView<Presenter> { void showUsers(List<User> users); void showMessage(String msg); } public interface Presenter extends BasePresenter { User loadUser(int id); List<User> loadUsers(); boolean releaseDB(); boolean clearData(); } }
|
让各自的Presenter和View实现其接口对应的方法,同时也可以互相调用其接口。
Presenter层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class UserPresenter implements UserContract.Presenter { private final UserContract.View mUserView; public UserPresenter(@NonNull UserContract.View userView) { mUserView = userView; mUserView.setPresenter(this); } @Override public void start() { } @Override public User loadUser(int id) { return null; } @Override public List<User> loadUsers() { return null; } @Override public boolean releaseDB() { return false; } @Override public boolean clearData() { return false; } }
|
View层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class UserFragment extends Fragment implements UserAdapter.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener, UserContract.View, OnStartDragListener { private UserContract.Presenter mPresenter; @Override public void onResume() { super.onResume(); mPresenter.start(); } @Override public void setPresenter(@NonNull UserContract.Presenter presenter) { mPresenter = presenter; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); getActivity().getMenuInflater().inflate(R.menu.menu_main, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case R.id.init: mPresenter.start(); break; case R.id.clear: mPresenter.clearData(); break; } return true; } @Override public void onDestroy() { super.onDestroy(); if (mPresenter.releaseDB()) { Log.d(TAG, "onDestroy: db is released"); } } }
|
可以看出V的接口会在P中被调用,并在V中被实现,在P中是不去实现的。同样,P的接口会在V中被调用,并在P中被实现。简单的说,各自的接口各自负责去实现,对方只调用对方的接口即可。
注意:在定义接口的时候,View的接口尽量是跟视图有关的,如showMessage()、navigateToMain(),而Presenter的接口尽量跟功能相关,如loadUsers(),这样的好处是在相互调用的时候可以看方法的名字就清楚这里的逻辑是要实现什么功能了,见名知义就是这样意思。
写在最后
当然有人会说了,咱写程序本来就是想着少写代码,不能为了用架构而去用架构,这样会得不偿失。对于什么应用适合什么架构,还是有待考量的,MVP不算是新知识,在反复的实践中,还是能感觉到它是利大于弊的,所以我们需要去掌握,这样我们自己写的时候逻辑也清楚,测试的也方便,何乐而不为呢?
希望本文对您日常的开发有所帮助!
我的Github: https://github.com/flemingme