一、架构设计目的
- 通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合
- 提高开发效率,便于测试及问题定位
- 提高可维护性、可扩展性
二、三层架构模型
一般而言,软件的开发可分为三层架构:界面层(User Interface layer, UIL)、数据访问层(Data access layer, DAL)、业务逻辑层(Business Logic Layer, BLL)。
- 界面层:主要是指与用户交互的界面。用于接收用户输入的数据和显示处理后用户需要的数据。
- 数据访问层:与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。
- 业务逻辑层:UI层和DA层之间的桥梁。实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等等。如果逻辑层相当强大和完善,无论表现层如何定义和更改,逻辑层都能完善地提供服务。
1.1、 分层规则
- UI层只能作为一个外壳,不能包含任何业务逻辑处理过程。只有少量(或者没有)SQL语句或者存储过程的调用,并且这些语句保证不会修改数据。
- 设计时应该从BLL出发,而不是UI出发。BLL层在API上应该实现所有负责的业务处理过程(以面向对象的方式)。如果把UILayer拿掉,项目还能在Interface/API的层次上提供所有功能)。
1.2、 优势
- 结构清晰、耦合度低。
- 可维护性高,可扩展性高。
- 利于开发任务同步进行,容易适应需求变化。
1.3、 劣势
- 有时会导致级联修改。这种修改尤其体现在自上而下的方向,如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。
- 增加了代码量,增加了工作量。
三、 MVC框架
1、框架示意图
2、请求流程
- View接受用户的请求
- View传递请求给Controller
- Controller操作Model进行数据更新
- Model通知View变化
- View根据更新的数据做出显示
3、程序实例
小明需要开发一个功能,当点击“find student id”按钮时,可以查询到他自己的学号。
对于MVC 实现,应该是这样的:
1、先实现一个 model,需要有通知View更新的能力,当model加载成功,如从db、获取后,需要告知View:
public class StudentModel {
String mId;
WelcomeActivity mActivity;
public StudentModel (WelcomeActivity activity) {
this.mActivity = activity;
}
public void loadModel (){
//search from db or intent
mId = "1201030018";
mActivity.updateUI(this);
}
}
2、Controller : 我们为了更好的理解MVC,将Activity进行了拆解。提取了一个简单的Controller
public class WelcomeController {
WelcomeActivity mActivity;
StudentModel mStudent;
WelcomeController (WelcomeActivity activity) {
this.mActivity = activity;
}
public void loadData (){
mStudent = new StudentModel(mActivity);
mStudent.loadModel();
}
}
3、View : View需要发出点击事件,并且传递给Controller,同时需要根据Model更新UI:
public class WelcomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//省略
final WelcomeController controller = new WelcomeController(this);
mFindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
controller.loadData();
}
});
}
public void updateUI (StudentModel model) {
mID.setText(model.mId);
}
}
调用链是:
View:OnClick ->
Controller:loadData->
Model:loadModel->
View:updateUI
Tips:为了更明显的表明意图,将Activity单纯的看做一个View。
4、优缺点
1、优点:
- 把业务逻辑全部分离到Controller中,模块化程度高。当业务逻辑变更的时候,不需要变更View和Model,只需要Controller换成另外一个Controller就行了(Swappable Controller)。
- 观察者模式可以做到多视图同时更新。
2、缺点:
- Controller测试困难。因为视图同步操作是由View自己执行,而View只能在有UI的环境下运行。在没有UI环境下对Controller进行单元测试的时候,Controller业务逻辑的正确性是无法验证的:Controller更新Model的时候,无法对View的更新操作进行断言。
- View无法组件化。View是强依赖特定的Model的,如果需要把这个View抽出来作为一个另外一个应用程序可复用的组件就困难了。因为不同程序的的Domain Model是不一样的。
四、MVP框架
1、框架示意图
2、请求流程
- View 接受用户请求
- View 传递请求给Presenter
- Presenter做逻辑处理,修改Model
- Model 通知Presenter数据变化
- Presenter 更新View
相比于MVC,MVP则使用Presenter解除了View与Model的耦合,Presenter将Model的变化返回给View。
3、程序实例
同样的例子,我们使用MVP进行改写
1、我们需要先对Model进行封装,当loadModel后,不直接通知View更新,而是通知监听方(Presenter):
public class StudentModel {
String mId;
OnGetListener mListener;
public StudentModel (OnGetListener listener) {
this.mListener = listener;
}
public void loadModel (){
//search from db or intent
mId = "1201030018";
if (mListener != null) {
mListener.onSuccess(this);
}
}
interface OnGetListener {
void onSuccess (StudentModel student);
}
}
2、再定义Presenter ,接收到来自View的操作命令后,进行逻辑处理,处理Model,修改完成后 通知View进行修改。
public class WelcomePresenter {
WelcomeActivity mActivity;
StudentModel mStudent;
WelcomePresenter (WelcomeActivity activity) {
this.mActivity = activity;
}
public void loadData (){
mStudent = new StudentModel(new StudentModel.OnGetListener() {
@Override
public void onSuccess(StudentModel student) {
mActivity.updateUI(student);
}
});
}
}
3、对于View,操作方式只是修改了持有的对象 WelcomeController
->WelcomePresenter
,其他保持不变。
这时调用流程变成了:
View:OnClick ->
Presenter:loadData->
Model:loadModel->
Presenter:onSuccess>
View:updateUI
我们将View的数据更新 从Model->View
,转变成了Presenter->View
,解除了View和Model的耦合。
4、优缺点
1、优点:
- 便于测试。Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter业务逻辑的正确性。
- View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务逻辑完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做高度可复用的View组件。
2、缺点:
- Presenter中除了业务逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护成本高。
五、MVVM框架
1、框架示意图
2、请求流程
在Android 借助Google提出的 DataBinding 框架完成 双向绑定操作。在MVVM框架中,除了将 Presenter 换成了 ViewModel 外,请求流程都和MVP类似,只是在最后一步中,由于View与ViewModel使用了双向绑定,两者的交互通过Data Binding 完成。如此使得View和控制模块的耦合更加降低,减轻了视图的压力。
3、程序实例
同样的例子,我们引入Data Binding解决这个问题。
1、把Controller\Presenter扔掉,先写一个ViewModel。
这个VM的概念是一个和视图相关的模型,因此可以和DAO相关的模型不一样,因为我们只需要展示student id
的数据以及click事件
,以及loadData
行为,用来从Model中获取数据,于是我们实现了一个VM :(这里我们需要遵循Data Binding的语法,如继承自BaseObservable,数据更新后调用notifyChange
方法等。)
public class StudentViewModel extends BaseObservable{
String studentID;
View.OnClickListener btnListener;
//省略get set
public StudentViewModel() {
btnListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
loadData();
}
};
}
public void loadData() {
StudentModel model = new StudentModel(new StudentModel.OnGetListener() {
@Override
public void onSuccess(StudentModel student) {
setStudentID(student.mId);
}
});
model.loadModel();
}
}
2、接着使用databinding语法 对 xml 进行数据绑定,我们将 Click事件、输出结果都绑定到VM上。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="userVM" type="com.moon.coderread.StudentViewModel"/>
</data>
<LinearLayout
//省略
<TextView
android:id="@+id/id"
android:layout_width="match_parent"
android:layout_height="20dp"
android:text="@{userVM.studentID}"/>
<Button
android:id="@+id/findBtn"
android:onClick="@{userVM.btnListener}"
android:text="find Student ID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
3、最后在View(Activity)
中引入VM :
public class WelcomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
ActivityWelcomeBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_welcome);
final StudentViewModel studentViewModel = new StudentViewModel();
binding.setUserVM(studentViewModel);
}
}
这样就完成了最基本的MVVM 栗子。现在我们的Activity看起来很清爽,不用再处理Click事件、以及updateUI
了。
4、优缺点
1、优点
- 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 提高可维护性。解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提高了代码的可维护性。
- 简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。
2、缺点
- 过于简单的图形界面不适用,或说牛刀杀鸡。
- 对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。
- 数据绑定的声明是指令式地写在View的模版当中的,这些内容是无法进行断点调试的。
六、总结
各种MV+X模式的发展,实质上就是将Model与View之间以一种低耦合、低成本的方式进行控制。MVC中模型和视图没有完全分离,造成Activity代码臃肿,MVP中通过Presenter来进行中转,模型和视图彻底分离,但由于V和P互相引用,代码不够优雅。ViewModel通过Data Binding实现了视图和数据的绑定,解决了这种MVP的缺陷,但目前也存在Data Binding还不成熟的问题。
从Presenter到ViewModel的转变,借助了Data Binding的方式实现了双向的数据绑定,减少了视图模块和控制模块的耦合。随着Google Data Binding技术的不断更新完善,相信MVVM模式将在Android平台下应用得更加广泛。
参考:
https://www.jianshu.com/p/9df92995ee26