MVC架构简介
MVC是一种架构设计模式,是一种设计理念。是为了达到分层设计的目的,从而使代码解耦,便于维护和代码的复用。MVC是3个单词的缩写,全称:Model-View-Controller(模型-视图-控制器)。
举一个例子,MVC就好比我们的鞋柜。当没有鞋柜的时候,鞋子是这样摆放的:
有了鞋柜之后,我们的鞋子是这样摆放的:
一眼就能看出,有了鞋柜之后,鞋子的摆放明显的整齐和有序很多,这样也很方便我们找到自己想穿的鞋子,不用将大量的时间花在寻找鞋子上。如果把我们的成千上万行代码和各种复杂的业务逻辑看作是各式各样的鞋子,那我们的MVC就是鞋柜。MVC让你的代码结构更加清晰明了。
没有使用MVC的时候,我们的代码结构如下:
上图那一坨“剪不断、理还乱”的乱麻就是你没有使用分层设计的代码结构。如果这时让你找你代码中的某一段逻辑估计是很费劲的,更别说将代码中的某一段代码进行复用或者替换了。
使用MVC分层设计之后,我们的代码结构如下:
上面的图示可能有点夸张,但是这样可能更好的理解。MVC其实就是提供一种规则,让你把相同类型的代码放在一起,这样就形成了层次,从而达到分层解耦、复用、便于测试和维护的目的。
以上说了一堆,其实就是想让大家理解MVC是什么,有什么作用。接下来,我们结合我们实际开发中的代码类型来解释一下MVC。
MVC(Model-View-Controller,模型-视图-控制器)模式是20世纪80年代Smalltalk-80出现的一种软件设计模式,其后来得到了广泛的应用。它用一种业务逻辑、数据、界面显示分离的方法组织代码,在改进和个性化定制界面及用户交互的同时,无须重新编写业务逻辑。
Android中MVC的角色定义如下:
1、Model
模型层,可以简单理解就是数据层,用于提供数据。在项目中,(简单理解)一般把数据访问和操作,比如将对象关系映射这样的代码作为Model层,也就是对数据库的操作这一些列的代码作为Model层。比如代码中我们会写DAO和DTO类型的代码,那这个DAO和DTO我们可以理解为是属于 Model层的代码。(我们针对业务模型,建立的数据结构和相关的类,就可以理解为Model。Model是与View无关,而与业务相关的)
2、View
视图层,就是UI界面,用于跟用户进行交互。一般所有的JSP、Html等页面就是View层。(一般采用XML文件或者Java代码进行界面的描述,也可以使用JavaScript+HTML等方式作为View层)
3、Controller
控制层,Controller层的功能就是将Model和View层进行关联。比如View主要是显示数据的,但是数据又需要Model去访问,这样的话,View会先告诉Controller,然后Controller再告诉Model,Model请求完数据之后,再告诉View。这样View就可以显示数据了。(Android的控制层通常在Activity、 Fragment或者由它们控制的其他业务类中。)如下图:
Android中的MVC也有它的缺点:
- Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,接受并处理来自用户的操作请求,进而做出相应。随着界面及其逻辑的复杂不断提升,Activity类的职责不断增加,以致变得庞大臃肿
- View层和Model层相互耦合,不易开发和维护
为了解决以上问题,产生了MVP以及MVVP这两种框架。
Controller控制器
/** * 该MainActivity持有了WeatherModel模型的对象,当用户点击Button的时候,MainActivty作为Controller控制层读取View * 视图层cityNOInput的数据,然后向WeatherModel模型发起数据请求,也就是调用getWeather()函数。当WeatherModel模型 * 处理数据结束后,通过接口OnWeatherListener通知View视图层数据处理完毕,View视图层该更新界面UI了。然后View视图层调用 * displayResult()方法更新UI。至此,整个MVC框架流程就在Activity中体现出来了。 */ public class MainActivity extends Activity implements OnWeatherListener, View.OnClickListener { private WeatherModel weatherModel; private Dialog loadingDialog; private EditText cityNOInput; private TextView city; private TextView cityNO; private TextView temp; private TextView wd; private TextView ws; private TextView sd; private TextView wse; private TextView time; private TextView njd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); weatherModel = new WeatherModelImpl(); initView(); } /** * 初始化View */ private void initView() { cityNOInput = findView(R.id.et_city_no); city = findView(R.id.tv_city); cityNO = findView(R.id.tv_city_no); temp = findView(R.id.tv_temp); wd = findView(R.id.tv_WD); ws = findView(R.id.tv_WS); sd = findView(R.id.tv_SD); wse = findView(R.id.tv_WSE); time = findView(R.id.tv_time); njd = findView(R.id.tv_njd); findView(R.id.btn_go).setOnClickListener(this); loadingDialog = new ProgressDialog(this); loadingDialog.setTitle("加载天气中..."); } /** * 显示结果 * * @param weather */ public void displayResult(Weather weather) { WeatherInfo weatherInfo = weather.getWeatherinfo(); city.setText(weatherInfo.getCity()); cityNO.setText(weatherInfo.getCityid()); temp.setText(weatherInfo.getTemp()); wd.setText(weatherInfo.getWD()); ws.setText(weatherInfo.getWS()); sd.setText(weatherInfo.getSD()); wse.setText(weatherInfo.getWSE()); time.setText(weatherInfo.getTime()); njd.setText(weatherInfo.getNjd()); } /** * 隐藏进度对话框 */ public void hideLoadingDialog() { loadingDialog.dismiss(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_go: loadingDialog.show(); weatherModel.getWeather(cityNOInput.getText().toString().trim(), this); break; } } @Override public void onSuccess(Weather weather) { hideLoadingDialog(); displayResult(weather); } @Override public void onError() { hideLoadingDialog(); Toast.makeText(this, "获取天气信息失败", Toast.LENGTH_SHORT).show(); } private <T extends View> T findView(int id) { return (T) findViewById(id); } }
Model模型:
public interface WeatherModel { void getWeather(String cityNumber, OnWeatherListener listener); }
/** * 1、这里设计了一个WeatherModel模型接口,然后实现了接口WeatherModelImpl类。 * 2、Controller控制器MainActivity调用WeatherModelImpl类中的方法发起网络请求, * 然后通过实现OnWeatherListener接口来获得网络请求的结果通知View视图层更新UI 。 * 3、至此,MainActivity就将View视图显示和Model模型数据处理隔离开了。MainActivity担当Controller完成了model和view之间的协调作用。 */ public class WeatherModelImpl implements WeatherModel { @Override public void getWeather(String cityNumber, final OnWeatherListener listener) { /*数据层操作*/ VolleyRequest.newInstance().newGsonRequest("http://www.weather.com.cn/data/sk/" + cityNumber + ".html", Weather.class, new Response.Listener<Weather>() { @Override public void onResponse(Weather weather) { if (weather != null) { listener.onSuccess(weather); } else { listener.onError(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { listener.onError(); } }); } }
/************************************************************************************************************ *1、这里为什么不在MainActivity中设计一个getWeather()方法直接请求网络数据? * 你考虑下这种情况:现在代码中的网络请求是使用Volley框架来实现的,如果哪天老板非要你使用Afinal框架实现网络请求, * 你怎么解决问题?难道是修改 getWeather()方法的实现? no no no,这样修改不仅破坏了以前的代码,而且还不利于维护, * 考虑到以后代码的扩展和维护性,我们选择设计接口的方式来解决着一个问题,我们实现另外一个WeatherModelWithAfinalImpl类, * 继承自WeatherModel,重写里面的方法,这样不仅保留了以前的WeatherModelImpl类请求网络方式, * 还增加了WeatherModelWithAfinalImpl类的请求方式。Activity调用代码无需要任何修改。 *2、利用MVC设计模式,使得这个天气预报小项目有了很好的可扩展和维护性,当需要改变UI显示的时候, * 无需修改Contronller(控制器)MainActivity的代码和Model(模型)WeatherModel模型中的业务逻辑代码,很好的将业务逻辑和界面显示分离。 *3、在Android项目中,业务逻辑,数据处理等担任了Model(模型)角色,XML界面显示等担任了View(视图)角色,Activity担任了Contronller(控制器)角色。 * contronller(控制器)是一个中间桥梁的作用,通过接口通信来协同 View(视图)和Model(模型)工作,起到了两者之间的通信作用。 *4、什么时候适合使用MVC设计模式? * 当然一个小的项目且无需频繁修改需求就不用MVC框架来设计了,那样反而觉得代码过度设计,代码臃肿。一般在大的项目中, * 且业务逻辑处理复杂,页面显示比较多,需要模块化设计的项目使用MVC就有足够的优势了。 *5、在MVC模式中我们发现,其实控制器Activity主要是起到解耦作用,将View视图和Model模型分离, * 虽然Activity起到交互作用,但是找Activity中有很多关于视图UI的显示代码,因此View视图和Activity控制器并不是完全分离的, * 也就是说一部分View视图和Contronller控制器Activity是绑定在一个类中的。 *6、MVC的优点: * (1)耦合性低。所谓耦合性就是模块代码之间的关联程度。利用MVC框架使得View(视图)层和Model(模型)层可以很好的分离,这样就达到了解耦的目的,所以耦合性低,减少模块代码之间的相互影响。 * (2)可扩展性好。由于耦合性低,添加需求,扩展代码就可以减少修改之前的代码,降低bug的出现率。 * (3)模块职责划分明确。主要划分层M,V,C三个模块,利于代码的维护。 ***********************************************************************************************************/