Android就业面试技巧系列-技术篇(MVP)
MVC
MVP
(控制器Controller)- 负责转发请求,对请求进行处理。
(视图View) - 界面设计人员进行图形界面设计。
(模型Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
模型(Model) “数据模型”(Model)用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“模型”有对数据直接访问的权力,例如对数据库的访问。
“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中数据的变化一般会通过一种刷新机制被公布。
为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改变。(比较:观察者模式)
视图(View) 视图层能够实现数据有目的的显示。在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册。
控制器(Controller) 控制器起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据模型上的改变。
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:
Controller(控制器)/Presenter(主持人、协调者)负责逻辑的处理,Model提供 数据,View负责显示
作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。
在MVC里,View是可以直接访问Model的,从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
在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很大的不同之处。
MVP的优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
MVC代码举例:
模拟一个需求:首先我们要进入一个 Splash界面, Splash界面中,有一个
ProgressBar控件和TextView控件,我们判断它是否有网络连接,如果有的话就隐藏 ProgressBar和跳转到MainActivity如果没有网络的话则显示ProgressBar和TextView,TextView则提示用户No internet。就这么简单的一个需求,我们看看如何用MVP模式做这个需求:
M层接口:
[Java]
纯文本查看
复制代码
1
2
3
|
public
interface
IConnectionStatus {
boolean
isOnline();
}
|
M层实现:
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
7
|
import
com.manning.androidhacks.hack020.presenter.model.IConnectionStatus;
public
class
ConnectionStatus
implements
IConnectionStatus {
@Override
public
boolean
isOnline() {
return
true
;
// 这里应该检测是否联网,此处先简单的return true代替
}
}
|
V层接口:
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
|
public
interface
ISplashView {
void
showProgress();
void
hideProgress();
void
showNoInetErrorMsg();
void
moveToMainView();
}
|
V层实现:
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
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
SplashActivity
extends
Activity
implements
ISplashView {[/align]
private
TextView mTextView;
private
ProgressBar mProgressBar;
private
SplashPresenter mPresenter ;
// P层
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.splash);
mPresenter =
new
SplashPresenter(
this
);
// 设置ISplashView 给P层
mTextView = (TextView) findViewById(R.id.splash_text);
mProgressBar = (ProgressBar) findViewById(R.id.splash_progress_bar);
}
@Override
protected
void
onResume() {
super
.onResume();
mPresenter.didFinishLoading();
// 通过P层间接调用M层
}
public
void
showProgress() {
mProgressBar.setVisibility(View.VISIBLE);
}
public
void
hideProgress() {
mProgressBar.setVisibility(View.INVISIBLE);
}
public
void
showNoInetErrorMsg() {
mTextView.setText(
"No internet"
);
}
@Override
public
void
moveToMainView() {
startActivity(
new
Intent(
this
, MainActivity.
class
));
}
[align=left]}
|
P层实现:
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
public
class
SplashPresenter { [/align]
private
IConnectionStatus connectionStatus;
private
ISplashView splashView;
public
SplashPresenter(ISplashView splashView){
this
.splashView = splashView;
connectionStatus =
new
ConnectionStatus (
this
);
}
public
void
didFinishLoading() {
if
(connectionStatus.isOnline) {
splashView.hideProgress();
// 有网,则隐藏ProgressBar
splashView.moveToMainView();
// 跳转到主页面
}
else
{
splashView.showProgress();
// 没网,展示Progressbar
splashView.showNoInetErrorMsg();
// 设置错误信息
}
}
}
|