Android MVP And MAC
面向对象原则之一:单一职责原则
MVC MVP
M Model 模型
V View 视图
C Controller 控制器
P Presenter 纽带曾,提出者
MVC
逻辑图
按照Android里面的意思 Activity相当于View,而很多时候Activity职责不单单是显示View这么简单,还有处理业务逻辑,视图显示,页面的跳转,单纯来说 业务层和显示层已经耦合在一起,不能进行分离解耦,Activity = Controller + view
单纯的MVC模型已经不试用于Android,为了适应Android 架构诞生出来MVP结构更适合Android。
其中MVC各层的职责:
Model :
模型代表着核心的业务逻辑和数据。模型封装了域实体的属性和行为,并暴露出了实体的属性。
View:
视图负责转换模型并把它传递给表示层。视图应关注于如何展示数据,而不应该包含任何业务逻辑业务逻辑封装在模型中。
Controller:
控制器控制程序的逻辑,并且充当着视图和模型之间协调的角色。控制器从视图层接收用户输入的信息,然后使用模型来执行特定的操作,并把最终的结果回传给视图。
MVP
摘自百度文档
MVP如何解决MVC的问题?
在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。
而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用!
不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试--而不需要使用自动化的测试工具。
我们甚至可以在Model和View都没有完成时候,就可以通过编写Mock Object(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。
在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。因此就有人提出了Presenter First的设计模式,就是根据User Story来首先设计和开发Presenter。在这个过程中,View是很简单的,能够把信息显示清楚就可以了。
在后面,根据需要再随便更改View,而对Presenter没有任何的影响了。
如果要实现的UI比较复杂,而且相关的显示逻辑还跟Model有关系,就可以在View和Presenter之间放置一个Adapter。
由这个 Adapter来访问Model和View,避免两者之间的关联。而同时,因为Adapter实现了View的接口,从而可以保证与Presenter之间接口的不变。这样就可以保证View和Presenter之间接口的简洁,又不失去UI的灵活性。 在MVP模式里,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接访问Model--这就是与MVC很大的不同之处。
职责
M 负责 业务逻辑,和数据提供
Presenter 负责View和Model之间进行的交互操作。
View 负责显示相应的界面。
其中我们以一个小Demo进行试验:
一个简单的登录功能,当然网络请求只是模拟数据。
xml:
基本的XML 没有什么好看的。最简单的例子
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/et_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="账号" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密码" />
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录" />
<TextView
android:id="@+id/tv_login_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登录状态:" />
</LinearLayout>
下面是代码部分重点:
还有件事要唠叨一下,毕竟把逻辑 显示控制分离,代码好看是好看了,增加了一些难度,第一个是代码量变多了,面向接口编程了,多出好多东西,还有一个,书写的难度增加了。写代码之前必须要考虑 要显示哪些东西,在什么地方控制它,简单来说 就是 把职责进行分离了。但是Android书写的时候职责有些地方是模糊的,比如Activity 逻辑 控制 显示 集为一体。好多人这样写吧。
我们按照MVP模式编写代码的时候,可以分为三类,
MVP
M Model 需要先定义它要做什么,然后再进行实现它,而我们的Model部分很简单,就是发送一个POST请求(模拟),然后把返回数据进行简单处理一下。
ILoginModel 这里是接口
LoginModel 这里是实现部分
V View 这里是我们只有显示的部分,也就是说只有set 和get部分
ILoginView
LoginView
P Presenter 这里就是重点看上面的图就能看出来是控制View和Model进行交互的部分,View Model交互曾
LoginPresenter
第一个部分:M 部分
写代码之前我们先理解它的职责,文章上部分有写着。
业务逻辑和数据
public interface ILoginModel {
/**
* 请求登录数据
*
* @param loginRequest
* @param onResponse
*/
void postLogin(LoginRequest loginRequest, OnResponse onResponse);
}
相当于Model存储提取处理数据。
public class LoginModel implements ILoginModel {
Handler handler = new Handler(Looper.getMainLooper());
/**
* 模拟POST请求
*
* @param loginRequest
* @param onResponse
*/
@Override
public void postLogin(final LoginRequest loginRequest, final OnResponse onResponse) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Map params = loginRequest.getParams();
String userName = (String) params.get("userName");
final String password = (String) params.get("password");
final String str = "您的账号:" + userName + "---您的密码:" + password;
handler.post(new Runnable() {
@Override
public void run() {
onResponse.onSuccess(str, "login");
}
});
}
}).start();
}
}
第二部分V 部分,首先我们要理解我们到底需要在LoginActivity做什么操作,set get
也就是获取发送登录信息,和获取登录后信息进行显示
public interface ILoginView {
/**
* 获取用户名ID
*
* @return
*/
Editable getId();
/**
* 获取密码输入
*
* @return
*/
Editable getPassword();
/**
* 设置登录后状态
*
* @param state
*/
void setLoginState(String state);
Context getContext();
}
实现部分:
public class LoginActivity extends AppCompatActivity implements View.OnClickListener, ILoginView {
private EditText mEtId, mEtPassword;
private TextView mTvState;
private LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity);
findViewById(R.id.btn_login).setOnClickListener(this);
mEtId = (EditText) findViewById(R.id.et_id);
mEtPassword = (EditText) findViewById(R.id.et_password);
mTvState = (TextView) findViewById(R.id.tv_login_state);
mLoginPresenter = new LoginPresenter(this);
}
@Override
public void onClick(View v) {
mLoginPresenter.requestLogin();
}
@Override
public Editable getId() {
return mEtId.getText();
}
@Override
public Editable getPassword() {
return mEtPassword.getText();
}
@Override
public void setLoginState(String state) {
mTvState.setText(state);
}
@Override
public Context getContext() {
return this;
}
}
下面是Presenter部分了。
mLoginPresenter.requestLogin();
而我们需要点击按钮发送结果数据。
public class LoginPresenter {
private ILoginView mILoginView;
private ILoginModel mILoginModel;
public LoginPresenter(ILoginView context) {
mILoginView = (ILoginView) context;
mILoginModel = new LoginModel();
}
public void requestLogin() {
LoginRequest loginRequest = new LoginRequest();
loginRequest.setUserName(mILoginView.getId().toString());
loginRequest.setPassword(mILoginView.getPassword().toString());
mILoginModel.postLogin(loginRequest, new OnResponse() {
@Override
public void onSuccess(String s, String url) {
mILoginView.setLoginState(s + "网址:" + url);
}
@Override
public void onailureF(Exception e) {
}
});
}
}
刚接触MVP 肯定一头雾水,代码看起来真心方便,但是做起来思维有点麻烦。
首先分析一下整体结构
model
ILoginModel
LoginModel
presenter
LoginPresenter
view
ILoginVIew
LoginActivity
第一步我们应该想我们到底要做个什么东西,就拿上面Demo来说,我们要做一个这样的东西,
2个EditText 一个账号 一个密码
1个Button 负责请求登录
一个TextView 负责显示请求状态信息
整体需求就出来了。
我们IView需要放置
1:获取账号密码接口 get
2:设置请求状态接口 set
我们的Model,处理,存储,获取数据
1:请求登录接口任务
3:处理一些数据
Presenter 负责Model 和View之间的交互
Presenter
存储IView的接口
存储IModel的接口
IView 请求登录的时候调用IModel接口
返回结果Model处理完成以后 直接set到IView接口上。
本人也是一个小菜鸟,错误希望大家指点出来。