MVC
MVC的设计图
MVC理解
MVC 全名是Model-View-Controller,是一种软件设计典范,用一种业务逻辑,数据,界面分离的方法组织代码,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。 Model层:处理数据,业务逻辑 View层:用于界面的显示 Controller层:起到桥梁作用,来控制View层和Model层通信。
MVC的优点
把业务逻辑全部分离到Controller中,模块化程度高。当业务逻辑变更的时候,不需要变更View和Model,只需要Controller换成另外一个Controller就行了
MVC的缺点
- Controller测试困难。因为视图同步操作是由View自己执行,而View只能在有UI的环境下运行。在没有UI环境下对Controller进行单元测试的时候,Controller业务逻辑的正确性是无法验证的:Controller更新Model的时候,无法对View的更新操作进行断言。
- View无法组件化。View是强依赖特定的Model的,如果需要把这个View抽出来作为一个另外一个应用程序可复用的组件就困难了。因为不同程序的的Domain Model是不一样的
web项目中的MVC
Model层:定义一些数据库的操作类 View层:使用jsp等来负责页面的显示, Controller层:使用servlet来作为View与Model层的桥梁,通过request接收View传来的数据,调用Model层的方法进行处理
Android项目中的MVC

Android项目中MVC是什么?特点
- Model 层:针对业务模型建立的数据结构和类,比如网络请求数据
- View层:XML布局文件,Activity也承担了View的功能
- Controller层:Android的控制层通常是Activity和Fragment
Android中MVC的缺点:
- Activity并不是MVC标准的Controller,既有Controller的职责也有View的职责
MVP
MVP 是MVC的一种演进版本,它将MVC中的Controller改为了Presenter,view通过接口与Presenter进行交互,降低耦合,方别进行单元测试。
MVP的设计图

MVP的理解
View层:负责绘制UI元素,与用户进行交互(Activity,View,Fragment) Model层:对数据的操作,对网络的操作,好业务路基相关的逻辑处理 Presenter层:作为View与Model交互的中间纽带,处理与用户交互的逻辑,可以把Presenter理解为一个中间层的角色,它接收Model层的数据,并且处理之后传递个View层,还需要处理View层的用户交互等操作。
MVP和MVC的区别
- MVP中绝对不允许View直接访问Model
- 本质上是增加了一个接口,降低了一层耦合
MVP的特点
- Presenter完全将Model和View解耦,主要逻辑处于Presenter层
- Presenter和具体View没有直接关联,通过定义好的接口进行交互
- View 变更时,可以保持Presenter不变
- View 只应该有简单的Set/Get 方法,用户输入,界面显示的内容
- 低耦合:Model和View解耦
MVP的优点?
- 低耦合:Model,View 的变换不会影响对方
- 方便测试:你可以直接调用Presenter层写测试用式
Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter业务逻辑的正确性
- 最后是可维护性和可扩展性,MVP的各个类职责都非常明确且单一,后期的扩展,维护都会更加容易
MVP的缺点
- 代码类增加了,一个小功能你可能要为它专门写Presenter和Model层的实现,以前这些一口气全加在View层了
- Presenter中除了业务逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难
怎么优化MVP的类文件量
定义契约类,将view、presenter的接口定义在一个契约类中 一个契约类就是一个业务模块
MVP的官方样例工程
MVP的一个官方基础样例:todo项目

public interface TaskDetailContract {
interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
void showMissingTask();
void hideTitle();
void showTitle(String title);
void hideDescription();
void showDescription(String description);
void showCompletionStatus(boolean complete);
void showEditTask(String taskId);
void showTaskDeleted();
void showTaskMarkedComplete();
void showTaskMarkedActive();
boolean isActive();
}
interface Presenter extends BasePresenter {
void editTask();
void deleteTask();
void completeTask();
void activateTask();
}
}
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TasksRepository mTasksRepository;
private final TaskDetailContract.View mTaskDetailView;
@Nullable
private String mTaskId;
public TaskDetailPresenter(@Nullable String taskId,
@NonNull TasksRepository tasksRepository,
@NonNull TaskDetailContract.View taskDetailView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
mTaskDetailView.setPresenter(this);
}
@Override
public void start() {
openTask();
}
private void openTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTaskDetailView.setLoadingIndicator(true);
mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
if (!mTaskDetailView.isActive()) {
return;
}
mTaskDetailView.setLoadingIndicator(false);
if (null == task) {
mTaskDetailView.showMissingTask();
} else {
showTask(task);
}
}
@Override
public void onDataNotAvailable() {
if (!mTaskDetailView.isActive()) {
return;
}
mTaskDetailView.showMissingTask();
}
});
}
@Override
public void editTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTaskDetailView.showEditTask(mTaskId);
}
@Override
public void deleteTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTasksRepository.deleteTask(mTaskId);
mTaskDetailView.showTaskDeleted();
}
@Override
public void completeTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTasksRepository.completeTask(mTaskId);
mTaskDetailView.showTaskMarkedComplete();
}
@Override
public void activateTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTasksRepository.activateTask(mTaskId);
mTaskDetailView.showTaskMarkedActive();
}
private void showTask(@NonNull Task task) {
String title = task.getTitle();
String description = task.getDescription();
if (Strings.isNullOrEmpty(title)) {
mTaskDetailView.hideTitle();
} else {
mTaskDetailView.showTitle(title);
}
if (Strings.isNullOrEmpty(description)) {
mTaskDetailView.hideDescription();
} else {
mTaskDetailView.showDescription(description);
}
mTaskDetailView.showCompletionStatus(task.isCompleted());
}
}
public class TaskDetailActivity extends AppCompatActivity {
public static final String EXTRA_TASK_ID = "TASK_ID";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.taskdetail_act);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);
String taskId = getIntent().getStringExtra(EXTRA_TASK_ID);
TaskDetailFragment taskDetailFragment = (TaskDetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.contentFrame);
if (taskDetailFragment == null) {
taskDetailFragment = TaskDetailFragment.newInstance(taskId);
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
taskDetailFragment, R.id.contentFrame);
}
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}
public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
@NonNull
private static final String ARGUMENT_TASK_ID = "TASK_ID";
@NonNull
private static final int REQUEST_EDIT_TASK = 1;
private TaskDetailContract.Presenter mPresenter;
private TextView mDetailTitle;
private TextView mDetailDescription;
private CheckBox mDetailCompleteStatus;
public static TaskDetailFragment newInstance(@Nullable String taskId) {
Bundle arguments = new Bundle();
arguments.putString(ARGUMENT_TASK_ID, taskId);
TaskDetailFragment fragment = new TaskDetailFragment();
fragment.setArguments(arguments);
return fragment;
}
@Override
public void onResume() {
super.onResume();
mPresenter.start();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.taskdetail_frag, container, false);
setHasOptionsMenu(true);
mDetailTitle = (TextView) root.findViewById(R.id.task_detail_title);
mDetailDescription = (TextView) root.findViewById(R.id.task_detail_description);
mDetailCompleteStatus = (CheckBox) root.findViewById(R.id.task_detail_complete);
FloatingActionButton fab =
(FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.editTask();
}
});
return root;
}
@Override
public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_delete:
mPresenter.deleteTask();
return true;
}
return false;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.taskdetail_fragment_menu, menu);
}
@Override
public void setLoadingIndicator(boolean active) {
if (active) {
mDetailTitle.setText("");
mDetailDescription.setText(getString(R.string.loading));
}
}
@Override
public void hideDescription() {
mDetailDescription.setVisibility(View.GONE);
}
@Override
public void hideTitle() {
mDetailTitle.setVisibility(View.GONE);
}
@Override
public void showDescription(@NonNull String description) {
mDetailDescription.setVisibility(View.VISIBLE);
mDetailDescription.setText(description);
}
@Override
public void showCompletionStatus(final boolean complete) {
checkNotNull(mDetailCompleteStatus);
mDetailCompleteStatus.setChecked(complete);
mDetailCompleteStatus.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
mPresenter.completeTask();
} else {
mPresenter.activateTask();
}
}
});
}
@Override
public void showEditTask(@NonNull String taskId) {
Intent intent = new Intent(getContext(), AddEditTaskActivity.class);
intent.putExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID, taskId);
startActivityForResult(intent, REQUEST_EDIT_TASK);
}
@Override
public void showTaskDeleted() {
getActivity().finish();
}
public void showTaskMarkedComplete() {
Snackbar.make(getView(), getString(R.string.task_marked_complete), Snackbar.LENGTH_LONG)
.show();
}
@Override
public void showTaskMarkedActive() {
Snackbar.make(getView(), getString(R.string.task_marked_active), Snackbar.LENGTH_LONG)
.show();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_EDIT_TASK) {
if (resultCode == Activity.RESULT_OK) {
getActivity().finish();
}
}
}
@Override
public void showTitle(@NonNull String title) {
mDetailTitle.setVisibility(View.VISIBLE);
mDetailTitle.setText(title);
}
@Override
public void showMissingTask() {
mDetailTitle.setText("");
mDetailDescription.setText(getString(R.string.no_data));
}
@Override
public boolean isActive() {
return isAdded();
}
}
MVVM
MVVM模式(Model–View–ViewModel模式),和MVP模式相比,MVVM 模式用ViewModel替换了Presenter ,其他层基本上与 MVP 模式一致,ViewModel和Model/View进行了双向绑定。View的数据的变化会同时修改数据源,而数据源数据的变化也会立即反应到View上 Data Binding,google发布的一个实现MVVM的支持库,能将数据绑定到xml文件中。
MVVM的优点
- 提高可维护性。解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提高了代码的可维护性
- 简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试
|