MVC设计模式,该模式能够将系统分成三个层面,分别是数据访问层(M)、视图层(V)、业务逻辑层(C),可以降低代码的耦合度,提高代码的内聚度。MVC是Model View Controller的缩写,是Xerox PARC在八十年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已被广泛使用。最近几年被推荐为Sun公司J2EE平台的设计模式,并且受到越来越多的使用 ColdFusion 和 PHP 的开发者的欢迎。模型-视图-控制器模式是一个有用的工具箱,它有很多好处,但也有一些缺点。MVC用一种业务逻辑、数据、界面显示分离的方法组织代码,强制性的使应用程序的输入、处理和输出分开,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
一、M-V-C介绍
控制器接受用户的输入并调用模型和视图去完成用户的需求,它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。
View层实现
public class TrackCtrlView implements View.OnClickListener{
private ImageView btnStartTrack, btnStopTrack, btnPauseTrack;
private TrackCtrlViewListener listener;
private TrackRecordInfo trackRecordInfo;
public TrackCtrlView(Activity activity, TrackCtrlViewListener listener){
this.listener = listener;
btnStartTrack = (ImageView) activity.findViewById(R.id.btnStartTrack);
btnStopTrack = (ImageView) activity.findViewById(R.id.btnStopTrack);
btnPauseTrack = (ImageView) activity.findViewById(R.id.btnPauseTrack);
btnStartTrack.setOnClickListener(this);
btnStopTrack.setOnClickListener(this);
btnPauseTrack.setOnClickListener(this);
btnPauseTrack.setOnClickListener(this);
}
/**
* 将用户请求通知Controller
*/
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.btnStartTrack:
if(listener != null){
listener.trackStatusRequest(TrackRecordStatus.Recording);
}
break;
case R.id.btnStopTrack:
if(listener != null){
listener.trackStatusRequest(TrackRecordStatus.Stoped);
}
break;
case R.id.btnPauseTrack:
if(listener != null){
if(trackRecordInfo.status == TrackRecordStatus.Paused){
listener.trackStatusRequest(TrackRecordStatus.Recording);
}else{
listener.trackStatusRequest(TrackRecordStatus.Paused);
}
}
break;
default:
break;
}
}
private void refreshView(){
TrackRecordStatus trackStatus = trackRecordInfo == null ?
TrackRecordStatus.Stoped : trackRecordInfo.status;
if (trackStatus == TrackRecordStatus.Recording) {
btnStartTrack.setVisibility(View.GONE);
btnPauseTrack.setVisibility(View.VISIBLE);
btnStopTrack.setVisibility(View.VISIBLE);
btnPauseTrack.setImageResource(R.drawable.btn_track_ctrl_pause);
} else if (trackStatus == TrackRecordStatus.Paused) {
btnStartTrack.setVisibility(View.GONE);
btnPauseTrack.setVisibility(View.VISIBLE);
btnStopTrack.setVisibility(View.VISIBLE);
btnPauseTrack.setImageResource(R.drawable.btn_track_ctrl_resume);
} else {
// TrackRecordStatus.Stoped
btnStartTrack.setVisibility(View.VISIBLE);
btnPauseTrack.setVisibility(View.GONE);
btnStopTrack.setVisibility(View.GONE);
}
}
public void setTrackRecordInfo(@Nullable TrackRecordInfo trackRecordInfo) {
this.trackRecordInfo = trackRecordInfo;
refreshView();
}
public interface TrackCtrlViewListener{
/**
* 用户点击按钮
*/
public void trackStatusRequest(@Nullable TrackRecordStatus newStatus);
}
}
Model层实现
public class TrackRecordInfo {
private static final Gson gson = new Gson();
/**
* 应该是保存轨迹数据库id,此demo中数据库操作不实现,暂时trackId一直为0
*/
public int trackId;
public TrackRecordStatus status;
public TrackRecordInfo(int trackId, TrackRecordStatus status) {
this.trackId = trackId;
this.status = status;
}
@NonNull
public static TrackRecordInfo loadTrackRecordInfo(@NonNull Context context){
String pref = SpUtil.getString(context, SpUtil.KEY_TRACK_RECORD_INFO, "");
if(!TextUtils.isEmpty(pref)){
return gson.fromJson(pref, TrackRecordInfo.class);
}
return null;
}
public static void changeTrackRecordInfo(@NonNull Context context, @Nullable TrackRecordInfo info){
SpUtil.saveString(context,
SpUtil.KEY_TRACK_RECORD_INFO,
info == null ? "" : gson.toJson(info));
//model通过消息总线,通知View刷新
EventBus.getDefault().post(new EventTrackRecordInfoChanged(info));
}
}
Controller层实现
public class MainActivity extends ActionBarActivity implements TrackCtrlView.TrackCtrlViewListener{
private TrackCtrlView trackCtrlView;
private TrackRecordInfo trackRecordInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
trackCtrlView = new TrackCtrlView(this, this);
EventBus.getDefault().register(this);
trackRecordInfo = TrackRecordInfo.loadTrackRecordInfo(this);
trackCtrlView.setTrackRecordInfo(trackRecordInfo);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Override
public void trackStatusRequest(@Nullable TrackRecordStatus newStatus) {
if(newStatus == TrackRecordStatus.Recording){
int trackId = 0; //在数据库创建一条轨迹,并获取到数据库id
trackRecordInfo = new TrackRecordInfo(trackId, TrackRecordStatus.Recording);
}else if (newStatus == TrackRecordStatus.Paused) {
if(trackRecordInfo != null){
trackRecordInfo.status = newStatus;
}
} else {
trackRecordInfo = null;
}
TrackRecordInfo.changeTrackRecordInfo(this, trackRecordInfo);
}
public void onEventMainThread(EventTrackRecordInfoChanged event){
trackRecordInfo = event.info;
trackCtrlView.setTrackRecordInfo(trackRecordInfo);
}
}
相对于把代码混在一起写来说,使用MVC可以将模型层、视图层、控制器的代码结构分离,可以很大程度上降低了代码模块之间的耦合性,提高其独立性,方便应用程序的扩展和维护。
————————————————————————————————————
模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。
————————————————————————————————————
例如,模型层用到的网络请求方式是Volley框架,现在要改成Retrofit框架。如果是“硬编程”估计要吃点苦头了,但是如果使用MVC设计模式,那么模型层会独立出来。可以把网络框架在父级中进行封装,然后在子类中具体实现和向外暴露的调用方法。或者写一个模型层的接口向外暴露调用方法,具体实现内容在模型层内部。
一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
例如,去食堂打饭用一个大碗和一个小碗,然后用一个勺子(Model)往这两个碗(View)里盛饭。道理和上面是一样的,总不至于大碗还得用大勺子、小碗还得用小勺子吧?当然可以这样,别扯皮,但是这里不就是在扯MVC的优点嘛!
由于使用MVC的项目需要付出成本去理解和设计,花费大量时间和精力将MVC应用到规模并不是很大的应用程序上,可能会得不偿失。特别是,对于一些简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性。所以完全按照MVC的设计模式去开发项目并不是轻而易举的事情,希望开发者们三思而行。
由于一个模型为多个视图提供数据,且模型层与视图层的严格分离,这样都给调试应用程序带来了一定的困难。需要每个模型在使用之前都需要经过彻底的测试。
至此,全部内容以及讲解完了,如有不足之处请留言提醒,或加入技术讨论群。
通往Android的阶梯:569614530