设计模式
这篇文章是写的Android的几种系统架构,预计会写三种:MVC,MVP,MVVM,(如果有时间再更新AOP模式)
对于架构,我的理解就是通过设计将功能模块化,做到模块之间高聚合低耦合。各模块各做各的事情,通过接口传递数据。这样做的好处有:
- 方便以后扩展功能,修改。如果要修改某个功能只要改该模块就行,很方便。毕竟项目不是什么东西都想好了才开始做。
- 开发各个部分时更加专心于当前的事情。
- 解耦合
- 便于测试
MVC模式
MVC模式只是简单的将功能划分一下,但分的还不够细。
理论上是分为三个部分:
- M 模型控制层(Model)
- 模型层主要就是处理业务逻辑,数据库操作,网络的操作。
- V 视图层(View)
- 这一层是对界面的描述,一般采用XML文件进行描述。用来显示给用户看的。
- C 控制层(Controller)
- 控制层用来将模型层的处理数据显示在视图层。以及做逻辑处理操作。一般都是用Activity,Fragment,作为控制层。
个人感觉MVC模式虽然是分为三部分,实际上只是分成两部分,M是一部分,VC是另一部分。毕竟Activity又要响应用户操作,又要更新View显示,
- 模型层和控制层之间通过接口来传递数据。
首先有两个接口,和一个Bean类,一个模型层接口,一个控制层用于回调,方便M数据传递给C,或者C拿到M的数据。Bean类用于传递数据
上MVC的伪代码
- 先创建创建装数据的Bean类
public class Bean {
private String data;
void setData(String data){
this.data = data;
}
String getData(){
return data
}
}
- 模型层接口:
public interface Imodel{
void getdata(Iconeroller iconeroller);
}
- 控制层接口:
public interface Icontroller{
void setData(Bean bean);
}
- 模型层实现:
public class Model impements Imodel{
Bean bean = new Bean();
@Override
public void getData(Iconeroller iconeroller){
//为了避免模型里面做耗时操作,Activity被销毁造成内存泄漏,
//先把传进来的控制器变成弱引用
WeakReference<Icontroller> icontroller;
Model(Icontroller icontroller){
icontroller = new WeakReference<Icontroller>(icontroller);
}
//经过一系列操作得出数据,将数据放在Bean类里面
if(icontroller != null){
iconeroller.setdata(bean);
}
}
}
- 控制层实现:
public class MainActivity implements Icontroller{
Model model = new Model(this);
.
.
.
model.getdata();
@Override
public void setData(Bean bean){
bean.getString
}
}
以后如果要更改模型层里面的业务逻辑,只需要继承模型类(不是模型层接口),再重写模型类里面的getData()方法就行,不用修改Activity里面的代码,就能完成修改。
MVP模式
MVC的缺点:由于VC都在Activity里面,造成了Activity很臃肿,一个Activity可以写到两三千行,看都看晕~
- 在MVC里面控制层即显示界面,它的主要职责是显示界面,并相应用户的操作请求。但是随着业务逻辑的增加,在Activity类的职责不断增加,以至于变得庞大臃肿。
在MVP模式里面,我们要改变这种状况,将Activity里面除了显示界面和与用户交互之外的工作分离出来,让Activity做为View单元。
MVP框架由3部分组成,View负责显示和与用户交互,Presenter负责逻辑处理,Model提供数据。(加上Viewinterface是4个)
- View:负责绘制UI元素,与用户进行交互(在Android体现为Activity)当我们将Activity中复杂的逻辑移至另一个类Presenter时,Activity就只负责建立UI元素与Presenter的关联,同时自己也会处理一些简单的逻辑。
- Model:负责存储,检索,操纵数据(有时也实现一个Model interface用来降低耦合)
- Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
- View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试。(在实际开发中是通过View interface与Presenter进行交互,我们可以通过实现这个View interface来模拟Activity的行为对Presenter进行单元测试。)
两种模式的主要区别
- (最主要的区别)View与Model并不直接交互,而是通过Presenter来间接交互,而在MVC中View和Model可以直接交互。
- 在MVP中View与Presenter是一对一的,但是复杂的View可能绑定多个Presenter,而在MVC里面Controller是基于行为的,并且可以被多个View共享,可以负责决定显示哪个View
- Presenter与View的交互是通过接口来进行的,更有利于添加单元测试。
MVP的优点如下:
- 模型与视图完全分离,我们可以修改视图而不影响模型
- 可以更搞笑的使用模型,因为所有的交互都发生在一个地方——Presenter内部
- 我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑,这个特点非常有用,因为视图变化总是比模型的变化频繁。
如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。
ui层一般包括Activity,Fragment,Adapter等直接和Ui相关的类,UI层的Activity在启动之后实例化相应的Presenter,App的控制权后移,由UI转义到Presenter,两者之间通信通过BroadCast、Handler或者接口完成,只传递事件和结果。
整个流程应该是这样:
UI层通知逻辑层(Presenter)用户点击了一个Button,逻辑层(Presenter)自己决定应该进行什么响应,要找哪个模型(Model)去做这件事情。然后逻辑层(Presenter)将完成的结果更新到UI层。
MVP架构存在的问题与解决办法
就算经过MVP架构将Activity中的职责分离出去,Activity内代码量还是很多
解决方法:
在分层的基础上再加入模板方法(Template Method);
具体做法是在Activity内部分层,创建一个BaseActivity,不做具体显示,而是提供一些基础方法实现,展现给用户的Activity基础BaseActivity,重写BaseActivity预留的方法,如果有必要,可以再进行二次继承。
避免Model过多造成混乱
- 模型层中的代码是最多的,很容易变得混乱不堪,一定要做好模块的划分,进行接口隔离,在内部进行分层。
以上代码量过大的问题,在Presenter里面也会出现。
对于这点的解决方法:
就是想办法将Presenter里面的代码再分离出去。
在UI层和Presenter之间设置中介者Mediator,
在Model和presenter之间使用代理Proxy
上述两者分担一部分Presenter的逻辑操作,但是整体框架的控制权还是在Presenter手中。
Mediator和Proxy不是必须的,只在Presenter负担过大时才建议使用。
代码实现
这里我写一个模拟登陆的伪代码
- View接口
public interface IloginView{
Void showProgress();
Void hideProgress();
Void setUsernameError();
Void setPasswordError();
Void navigateToHome();
}
- Model接口
public interface LoginModel{
Void login(String username,String password,LoginPresenterImpl lp);
}
- 实现View接口的类
public class Activity extends Activity implements IloginView,View.OnClickListener{
Private EditText username,password;
Private LoginPresenter loginPresenter;
@Override
Protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
username = (EditText)findViewById(R.id.username);
password = (EditText)findViewById(R.id.password);
findViewById(R.id.button).setOnClickListener(this);
loginPresenter = new LoginPresenter(this);
}
@Override
public void onclick(View v){
loginPresenter.validateCredentials(username.getText.toString(),password.getText.toString);
}
@Override
Void showProgress(){
//播放一个登陆动画
}
@Override
Void hideProgress(){
//停止登陆动画
}
@Override
Void setUsernameError(){
//提示用户名错误登陆失败
}
@Override
Void setPasswordError(){
//提示用户密码错误登陆失败
}
@Override
Void navigateToHome(){
//这里面做登陆成功的操作
}
}
- 实现Model接口的类
public class loginModelImpl implements LoginModel{
@Override
public void login(String username,String PassWord,LoginPresenterImpl lp){
//在这里面进行登陆的网络操作,
new Handler().PostDelayed(new Runable(){
@Override
public void run(){
boolean error = true;
if(TextUtils.isEmpty(username)){
lister.onUsernaeError();
boolean error = false;
}
if(TextUtils.isEmpty(password)){
listener.onPasswordError();
boolean error = false;
}
if(error){
listener.onSuccess();
}
}
},2000){
}
}
}
- Presenter类
pbulic class LoginPresenterImpl implements OnLoginFinished{
private LoginView loginView;
Private LoginModel loginModel;
public LoginPresenterImpl(LoginView loginView){
this.loginView = loginView
this.loginModel = new loginModelImpl ();
}
public void validateCredentials(String username,String password){
if(loginView != null&&loginModel != null){
loginView.showProgress();
loginModel.login(username,password,this);
}
}
public void onDestroy(){
loginView = null;
loginModel = null;
}
@Override
public Void onUsernameError(){
loginView.setUsernameError();
loginView.hideProgress();
}
@Override
public Void onPasswordError(){
loginView.setPasswordError();
loginView.hideProgress();
}
@Override Void onSuccess(){
loginView.navigateToHome();
loginView.showProgress();
}
}
- 网络数据回调接口
public interface OnLoginFinished{
Void onUsernameError();
Void onPasswordError();
Void onSuccess();
}
上面的伪代码运行是这样的:
1. 用户在界面点击按钮登录。Activity把请求发给Presenter去做。
2. 在Presenter里面调用Activity的showProgress显示一个登录动画,然后调用Model的登录方法。
3. 在Model里面做登录的网络操作,登录操作之后调用Presenter的网络数据会掉接口吧登录结果返回给Presenter
4. Presenter根据Model返回的结果通知Activity做相应的操作。
MVVM
MVVM是MVP的升级版,其中VM是ViewModel的缩写,可以看做是View的数据模型和Presenter的合体。ViewModel和View之间的交互通过DataBinding完成。
MVVM的特点
数据驱动
在以往的开发中,处理完数据后,要获得UI的引用,然后更新UI,或者通过UI的来获取用户输入。而在MVVM中UI的更新不用我们管,由数据取驱动UI自动更新UI,UI的改变又同时反馈到数据。我们只需要专注于业务逻辑处理和数据。
低耦合度
在MVVM中数据是独立与UI的,ViewModel不持有UI的引用,ViewModel中只负责处理和提供数据。UI拿到数据后怎么做完全由UI自己决定。
更新UI
在MVVM中,我们可以在工作线程中直接修改View,只要数据是线程安全的。剩下的数据绑定框架会搞定。很多事情都不需要自己去做。
可复用性
一个ViewModel复用到同样一份数据用不同的UI做显示,对于版本迭代频繁的UI改动,只要更换View层就行
单元测试
ViewModel里面是数据和业务逻辑,View中关注的是UI,这样的模式做测试是很方便的,完全没有彼此的依赖。
MVVM中各部件的作用:
View:
View层只做和UI相关的工作,我们只在XML和Activity或Fragment写View层的代码,在View不写业务逻辑相关的代码,也不写UI数据更新的代码,因为更新UI通过DataBinding来实现,直接在ViewModel里面更新数据源,DataBinding自动更新数据。在View里面只做一些控件初始化或者改变控件颜色这种和业务逻辑和数据不想关的操作。
总之就是将View和业务逻辑,数据严格分离开来。
ViewModel:
ViewModel里做的事情刚好和View相反
ViewModel里面就专心做业务逻辑,数据处理的事情,ViewModel处理后的数据更新到UI或者从UI上面获取数据都要通过DataBinding(DataBinding支持双向数据绑定,)。
ViewModel不持有任何UI的引用
Model:
Model基本就是一个实体模型(Bean)同时包括Retrofit的Service。
//TODO最近好忙,以后再继续更新。。。
三种模式的差异在于Model和View的交互,实现与用户的交互操作,与变更通知。
MVP和MVVM完全隔离了Model和View,但是在有些情况下,将数据从Model到ViewModel或者Presenter的拷贝开销很大,可能也会结合MVC的方式,Model直接通知View进行变。
以上内容如有错误,欢迎指正>_<