Android MVC和MVP架构的详解

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,
用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面
及用户交互的同时,不需要重新编写业务逻辑。其中
M层:处理数据,业务逻辑等;
V层:处理界面的显示结果;
C层:起到桥梁的作用,来控制V层和M层通信以此来达到分离视图、显示和业务逻辑层。


在Android中使用如下图:


View 层:在开发中xml文件就是我们的View布局,一般情况下我们都会用xml来实现我们的布局,相对的节省性能的消耗。但是也有一些比较特殊、比较复杂的需求我们需要用到自定义view,其实自定义View也是属于View,这是比较少的情况,正常我们都会用xml来实现View的。

Controller层:控制层,其实Android已经默认给我们提供了Controller,就是Activity和Fragment,仔细一想就可以明白,用户在View层做出请求的操作,Activity和Fragment就会对用户做出的请求做出相应的处理,得到相应的结果之后,再呈现给用户。
其实对于Activity和Fragment人们有时会认为它是属于View层的。的确,在MVC的View层中的xml布局是不会改变的,而当用户在操作的时候我们的View总要做出相应的改变,而有些改变单单是xml是没办法做到的,所以在Activity和Fragment这个Controller层中多多少少都会有View层的东西。但是在Activity和Fragment中总归还是Controller层的代码占绝大多数,因此我们还是把它归类为Controller层。


Model层:Android中除了View和Controller,剩下的绝大多数来说都是Model层的东西了。其实Model一般不会被定义得很死,Model本身就数据和业务逻辑有关的,而不同的需求当然是有不同的数据和业务逻辑的,其中数据模型(javaBean)、数据的存取、网络的操作、以及一些耗时的任务等等。

项目结构:


接下来Demo演示,数据模型类:

public class Person {
	private int per_id;
	private String username;
	private String sex;
	private int age;

	public Person(int per_id, String username, String sex,
			int age) {
		this.per_id = per_id;
		this.username = username;
		this.sex = sex;
		this.age = age;
	}

	public int getPer_id() {
		return per_id;
	}

	public void setPer_id(int per_id) {
		this.per_id = per_id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}


	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person->[per_id=" + per_id + ", username=" + username
				+ ", sex=" + sex + ", age=" + age
				+ "]";
	}

}

获取数据侦听:

public interface GetDataListener {
	void onGetDataSuccess(Person person);

	void onGetDataError(String errmessage);
}

获取数据接口:

public interface GetData {
	void getShopsData(GetDataListener getDataListener);
}

获取数据:

public class GetDataImplement implements GetData {

	@Override
	public void getShopsData(GetDataListener getDataListener) {
		Person person = new Person(1, "layne", "male", 18);
		if(person!=null){
			getDataListener.onGetDataSuccess(person);
		}else{
			getDataListener.onGetDataError("请求失败");
		}
	}

}

控制层:

public class MainActivity extends ActionBarActivity implements GetDataListener,
		OnClickListener {
	private Button bt_request;
	private TextView tv_content;
	private GetData getData;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		InitView();

	}

	private void InitView() {
		bt_request = (Button) findViewById(R.id.bt_request);
		tv_content = (TextView) findViewById(R.id.tv_content);

		bt_request.setOnClickListener(this);

		getData = new GetDataImplement();
	}

	@Override
	public void onClick(View view) {
		switch (view.getId()) {
		case R.id.bt_request:
			getData.getShopsData(this);
			break;
		}

	}

	@Override
	public void onGetDataSuccess(Person person) {
		showData(person);

	}

	@Override
	public void onGetDataError(String errmessage) {
		Toast.makeText(this, errmessage, Toast.LENGTH_SHORT).show();

	}

	// 控制层将Model层里面的数据呈现到View层
	private void showData(Person person) {
		tv_content.setText(person.toString());
	}

}

视图层:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.itman.mvcdemo1.controller.MainActivity" >

    <Button
    android:id="@+id/bt_request"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    android:text="请求数据" />
    
   <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="请求的内容" />

    </LinearLayout>
    
</LinearLayout>

运行结果:

     

总结:

MVC的优点:
很明显如果要求不高,我们平时的小项目就是一般的MVC,这个框架学习成本低,理解起来非常容易,对UI层与业务层进行分离,对Model层和数据方面做一下分层和封装,就是一个扩展性不错,挺清晰的MVC开发框架了。

但是缺点也是很明显的:
随着我们的业务壮大起来,需求越来越多,那么Activity和Fragment的代码就会越来越多,有的甚至夸张到一个activity就有几千行代码的,就会变得越来越臃肿,当我们维护和改变需求的时候是非常耗时间和精力的。这个就是MVC的缺点。



MVP架构

根据上文一开始MVC架构,项目随着需求不断增多,业务逻辑更加的复杂,我们的activity和fragment的代码也是越来越臃肿的。这时的activity和fragment理解和维护起来科室非常的费事费力,很痛苦的一件事,相信我们程序员都不太愿意去面对这样的情况的。这时MVP就应运而生,原先MVC中的Controller层activity或fragment将抽出控制层的代码,剩余的归类为View层,也就是说activity和fragment在MVP中是属于View层的,而被抽出的控制层代码成为了新的一员Presenter。
MVP的各个职责:
View层:
对应于Activity,负责View的绘制以及与用户交互,处理界面的显示结果
Model层:处理数据,业务逻辑等
Presenter层:负责完成View于Model间的交互

MVP其实就是减少了Activity和fragment的职责,减少了Activity和fragment的代码,将繁杂的逻辑代码交给了Presenter,相对应的好处就是耦合度更加的低,更方便单元测试了,同时借用两张图来说明变化(图片出处):


转变为:



MVC和MVP的区别(图片出处):根据图中我们可以知道在MVC中Model层是可以和View层交互的,但是在MVP中Model层和View层的交互是完全交给了Presenter的


接下来我们根据上面的MVC的Demo,将之修改成MVP架构的Demo

项目结构



数据模型:

public class Person {
	private int per_id;
	private String username;
	private String sex;
	private int age;

	public Person(int per_id, String username, String sex,
			int age) {
		this.per_id = per_id;
		this.username = username;
		this.sex = sex;
		this.age = age;
	}

	public int getPer_id() {
		return per_id;
	}

	public void setPer_id(int per_id) {
		this.per_id = per_id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}


	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person->[per_id=" + per_id + ", username=" + username
				+ ", sex=" + sex + ", age=" + age
				+ "]";
	}

}

请求侦听:

public interface GetDataListener {
	void onGetDataSuccess(Person person);

	void onGetDataError(String errmessage);
}

Presenter接口:

public interface IGetDataPresenter {
	void getData(GetDataListener getDataListener);
	
	void clear();
}

Presenter:

public class GetDataPresenterCompl implements IGetDataPresenter {

	private IGetDataView getDataView;
	private Person person;

	public GetDataPresenterCompl(IGetDataView getDataView) {
		this.getDataView = getDataView;
	}

	@Override
	public void getData(GetDataListener getDataListener) {
		person = new Person(1, "layne", "male", 18);
		if (person != null) {
			getDataListener.onGetDataSuccess(person);
		} else {
			getDataListener.onGetDataError("请求失败");
		}
	}

	@Override
	public void clear() {
		getDataView.clearData();
	}

}

视图的接口:

public interface IGetDataView {
	
	void clearData();
}

Activity:

public class MainActivity extends ActionBarActivity implements IGetDataView,
		GetDataListener, OnClickListener {

	private Button bt_request, bt_clear;
	private TextView tv_content;
	
	private IGetDataPresenter getDataPresenter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		InitView();
	}

	private void InitView() {
		bt_request = (Button) findViewById(R.id.bt_request);
		bt_clear = (Button) findViewById(R.id.bt_clear);
		tv_content = (TextView) findViewById(R.id.tv_content);

		bt_request.setOnClickListener(this);
		bt_clear.setOnClickListener(this);
		
		getDataPresenter = new GetDataPresenterCompl(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.bt_request:
			getDataPresenter.getData(this);
			break;

		case R.id.bt_clear:
			getDataPresenter.clear();
			break;
		}

	}

	@Override
	public void onGetDataSuccess(Person person) {
		showData(person);

	}

	@Override
	public void onGetDataError(String errmessage) {
		Toast.makeText(this, errmessage, Toast.LENGTH_SHORT).show();

	}

	@Override
	public void clearData() {
		tv_content.setText("清空内容");

	}
	
	private void showData(Person person) {
		tv_content.setText(person.toString());
	}


}

最后是布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.itman.mvpdemo.view.MainActivity" >

    <Button
        android:id="@+id/bt_request"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="5dp"
        android:text="请求数据" />

    <Button
        android:id="@+id/bt_clear"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="5dp"
        android:text="清除数据" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="请求的内容" />
    </LinearLayout>

</LinearLayout>

运行结果:

      

MVP的优点:
1.更加的降低了耦合度,使得Model层和View层的完全解耦
2.模块职责划分清楚,层次分明
3.Presenter一定程度下可以复用,一个Presenter可用于多个View,前提是View的改动不影响业务逻辑。
4.单元测试更加的方便


缺点:
1.层次分明的代价是额外的代码和类,这更加的增加了学习成本
2.如果Presenter过多地与特定的视图的联系过于紧密,一旦视图需要变更,那么Presenter也需要变更了。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值