基于Android的mvc、mvp以及mvvm架构分析(上)
来源:互联网 发布:mac地址获取工具 编辑:程序博客网 时间:2024/06/16 16:52
前言:
工欲善其事,必先利其器,工作一段时间后,对于以上十个字的感触是最深的。刚参加工作的时候,并没有对于要做的事情有着自己的理解,经常是上面分配了工作,自己就乖乖地跑去做,也不会思考什么。后来渐渐地发现,做着做着就会出现各种问题,对需求理解的不到位,对问题理解的不深入,会造成各种各样的问题,新功能难以实现,程序BUG非常难以定位等等。要想成为一名优秀的软件开发者,在做每一件事情之前,都应该深思熟虑,将可能出现的情况尽可能地考虑进去,这样前期虽然感觉工作变慢了,但是后来的工作会变得轻松且得心应手。好了,废话扯了很多,接下来就是正题,关于mvc、mvp、mvvm在android中的应用实践分析,本人水平不高,有错误的地方欢迎指正,不胜感激。
mvc、mvp与mvvm都是软件编程当中用以解决界面与功能关系的常见架构,好的架构自然是需要符合设计原则的,而这三个架构的目的也是在于将软件当中的界面(View)与业务逻辑(Model)进行解耦,而另外的部分,就是连接M与V的桥梁,这里多说一句,我将Model称为业务逻辑的原因:Model翻译过来是模型,很多其他文章当中对Model的解释是数据,解释的原因是程序的根本是数据,我觉得说的很对,就好像世界的本质是物质一样,只不过这里我将Model解释为业务逻辑是因为我觉得数据给人一种“死”的感觉,而且有一种误导向数据库的感觉(虽然确实会包含),所以我觉得软件的根本是业务,软件开发者的工作在于将业务转换为逻辑代码,从业务逻辑的词语这样感觉比较“活”。
那好的,接下来首先要分析的就是Android当中MVC的具体实现与应用。
一、MVC在Android当中的应用:
1、MVC的介绍:
MVC全名是Model ViewController,是模型(model)-视图(view)-控制器(controller)的缩写,其中Model代表业务逻辑,Controller是View与Model交互的桥梁,而View就是我们用户可以直接看见和操作的界面,而MVC又分为主动和被动两种,其转换图如下:
图1 被动MVC
图2 主动MVC
可以看到,主动MVC与被动MVC的结构是基本一致的,最主要的区别在于M与V之间是否可以直接沟通,所以此处的主动以及被动的主语是Model,指的是Model能否主动地与View之间进行通信。
而在Android当中,MVC的实现可以说是非常的简单,Android的V就是我们的各种Layout.xml文件,而M是我们进行具体业务逻辑实现的部分(比如计算器的后台计算方式),而C就是他们的桥梁(比如Activity)。
2、被动MVC
首先是被动MVC,被动MVC的宗旨是M与V互相不知道,完全通过C来进行数据的交互,C是整个系统中的桥梁,起到非常重要的中介作用。
这里打算用一个例子来描述MVC,我们来设想这样一个场景,在一片苍茫的草地上,有着一群鸟,鸟儿总是向往天空,但却会时而落地,我们骑上奔驰的骏马在草地上红尘作伴潇潇洒洒,拿着一种高科技的望远镜想要知道在某一时刻落地的鸟儿有几只。
好的,场景描述完毕,现在可以看到,鸟儿落地起飞的具体情况可以认为是Model,人通过“高科技望远镜”来看鸟儿的数量,可以认为高科技望远镜里看到的就是View,而看过去的过程就是Controller。
这里用一个实际的App来仿真之前提到的场景。
首先是activity_main.xml,也就是View的部分:
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/tv_birds"
android:id="@+id/tv_birds"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="143dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_see"
android:id="@+id/btn_see"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
比较的简单,就一个用来显示鸟的数量的TextView和一个用来看鸟数量的Button
然后是MainActivity,也就是Controller的部分:
package com.brick.mvctest;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener{ TextView tv_birds = null; Button btn_see = null; BirdsModelInterface birdsModel = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_birds = (TextView)findViewById(R.id.tv_birds); btn_see = (Button)findViewById(R.id.btn_see); btn_see.setOnClickListener(this); birdsModel = BirdsModel.getInstance(); } protected void onResume(){ super.onResume(); } @Override public void onClick(View v){ switch (v.getId()){ case R.id.btn_see: int num = birdsModel.getBirds(); set_Tv_Birds(num); break; default: break; } } private void set_Tv_Birds(int num){ tv_birds.setText(num + ""); }}
MainActivity的代码也比较简单,主要就是通过onClick方法来把按钮的点击事件获取,然后从BirdsModel当中来获取鸟的数量,再通过set_Tv_Birds来返回到View来显示,实现了桥梁的作用。
然后是Model部分,一个简单的类,来让鸟儿一秒钟运动一次,改变鸟的数量:
基类:
package com.brick.mvctest;/** * Created by brick on 2017/1/6. */public interface BirdsModelInterface { public int getBirds();}
实现:
package com.brick.mvctest;import java.util.Random;/** * Created by brick on 2017/1/5. */public class BirdsModel implements BirdsModelInterface { private int birds = 0; private final int MAX_BIRDS = 1000; private static BirdsModel instance = null; private static Object birds_Lock = new Object(); private BirdsModel(){ new Thread(new Runnable() { @Override public void run() { while(true){ setBirds(new Random().nextInt(MAX_BIRDS)); try { Thread.sleep(1000); }catch (Exception e){ e.printStackTrace(); } } } }).start(); } public static BirdsModel getInstance(){ if(instance == null) { synchronized (BirdsModel.class) { if(instance == null){ instance = new BirdsModel(); } } } return instance; } private void setBirds(int num){ synchronized (birds_Lock) { birds = num; } } public synchronized int getBirds(){ synchronized (birds_Lock) { return birds; } }}
这里的Model就是BirdsModel,鸟每分钟会自行运动一次,并修改当前鸟的数量。然后在MainActivity当中实际上持有的是BirdsModelInterface,也可以实现一定程度的M与C的解耦,当遇到需求修改或者说Model变化的时候,可以用相同的接口,不同的Model实现。
至此,一个简单的被动MVC框架就算搭建完成了,感觉非常简单,其实就算不了解MVC的框架,稍微重视代码规范的程序员也至少会把代码写成这样,主要是因为在Android当中,V和C已经简单地划分好了,所以在被动MVC当中,只需要注意将M的部分抽离出来,就能够实现一个合理的MVC框架。
简而言之一句话:Activity里面别写业务逻辑代码。
3、主动MVC
好吧,被动MVC看来很简单也很无趣,也就是一句话解决,把Model要做的事情丢给Model做,别让Activity里面太多代码。
接下来就是主动MVC了,主动MVC与被动MVC最大的不同就在于M与V是有直接的通信的,这样一来M的变化会直接地影响到V,通常这里通过观察者模式来实现。同样的,还是在那片苍茫的草地上,有着那一群可爱的鸟儿,不过这一次红尘作伴的我们想要实时地知道鸟儿落地的情况,而不是只有我们拿起望远镜时才能看到。好的,要做到这一点,只需要简单地修改一下之前的代码。
首先还是View的部分,activity_main.xml并没有修改:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="@string/tv_birds" android:id="@+id/tv_birds" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="143dp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_see" android:id="@+id/btn_see" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /></RelativeLayout>
依然是一个按钮,一个文本视图
然后是Controller的部分:
Observer的基类:
package com.brick.mvctest;/** * Created by brick on 2017/1/6. */public interface BirdsModelObserver { public void update(int birds);}
实现:
package com.brick.mvctest;import android.os.Bundle;import android.os.Looper;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.TextView;import java.util.logging.Handler;public class MainActivity extends AppCompatActivity implements View.OnClickListener,BirdsModelObserver{ TextView tv_birds = null; Button btn_see = null; BirdsModelInterface birdsModel = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_birds = (TextView)findViewById(R.id.tv_birds); btn_see = (Button)findViewById(R.id.btn_see); btn_see.setOnClickListener(this); birdsModel = BirdsModel.getInstance(); ((BirdsModel)birdsModel).registerObserver(this); } protected void onResume(){ super.onResume(); } @Override public void onClick(View v){ switch (v.getId()){ case R.id.btn_see: int num = birdsModel.getBirds(); set_Tv_Birds(num); break; default: break; } } private void set_Tv_Birds(int num){ tv_birds.setText(num + ""); } @Override public void update(final int birds){ android.os.Handler handler = new android.os.Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { set_Tv_Birds(birds); } }); }}
代码的改动主要是将Activity继承了BirdsModelObserver成为观察者,然后实现了update的方法并将自己注册到Model当中。
接下来是Model的部分:
Model的基类:
package com.brick.mvctest;/** * Created by brick on 2017/1/6. */public interface BirdsModelInterface{ public int getBirds();}
Observable的基类:
package com.brick.mvctest;/** * Created by brick on 2017/1/6. */public interface Observable { public void registerObserver(BirdsModelObserver birdsModelObserver); public void removeObserver(BirdsModelObserver birdsModelObserver); public void notifyObservers();}
package com.brick.mvctest;import java.util.ArrayList;import java.util.List;import java.util.Random;/** * Created by brick on 2017/1/5. */public class BirdsModel implements BirdsModelInterface, Observable{ List<BirdsModelObserver> birdsModelObservers = null; private int birds = 0; private final int MAX_BIRDS = 1000; private static BirdsModel instance = null; private static Object birds_Lock = new Object(); private BirdsModel(){ birdsModelObservers = new ArrayList<>(); new Thread(new Runnable() { @Override public void run() { while(true){ setBirds(new Random().nextInt(MAX_BIRDS)); try { Thread.sleep(1000); }catch (Exception e){ e.printStackTrace(); } } } }).start(); } public static BirdsModel getInstance(){ if(instance == null) { synchronized (BirdsModel.class) { if(instance == null){ instance = new BirdsModel(); } } } return instance; } private void setBirds(int num){ synchronized (birds_Lock) { birds = num; notifyObservers(); } } public synchronized int getBirds(){ synchronized (birds_Lock) { return birds; } } @Override public void registerObserver(BirdsModelObserver birdsModelObserver){ birdsModelObservers.add(birdsModelObserver); } public void removeObserver(BirdsModelObserver birdsModelObserver){ try{ birdsModelObservers.remove(birdsModelObserver); } catch (Exception e){ e.printStackTrace(); } } public void notifyObservers(){ for(BirdsModelObserver birdsModelObserver : birdsModelObservers){ birdsModelObserver.update(getBirds()); } }}
这里主要也就是将Model继承了Observable,使之成为可供观察的主题,在Activity当中进行注册。
这样一来,草原上的鸟儿就可以实时的被关注到,并将每一次的变化发送到View来进行显示。
简单地说一下,似乎看来主动MVC与被动MVC在这里没有很明显的区别,但是他们之间其实是有很根本的区别的,在被动MVC当中,V可以说完全与M没有任何的联系,V只是与C进行通信,当按钮被点击时,V仅仅是告诉C,按钮被点击了,我需要一个int数据,然后后面的事情全权交给了C来处理。而在主动MVC当中,M与V有着直接的联系,V注册为了M的观察者,M必须要知道V上有什么控件,需要什么样的信息,而造成了M与V的耦合,这其实是不符合设计原则的。
结合之前的例子,把被动MVC和主动MVC进行一次解释。
在被动MVC的情况下,红尘作伴的我们潇潇洒洒地拿起高科技望远镜(按下获取鸟数量的按钮),我们想要的只是能看到东西,究竟望远镜怎么合成影像,怎么调整光线来获取鸟的数量都不由我们关心,而鸟在飞起降落的过程也是完全自由的,不会知道有人在看它们,这是被动MVC。
在主动MVC的情况下,鸟儿每一次的数量变化(对应到代码中是每一秒变动一次的鸟的数量变化)我们都可以通过望远镜来看到,而不是我们每一次拿起望远镜的时候,这样,每次鸟儿飞起降落的时候,就必须要通知我们,数量变化了,并且还必须要按照我们规定的方式来告诉我们,这样鸟儿就不自由了,一旦我们的望远镜升级换代,鸟儿就需要按照新的方式来告知我们,这就造成了我们最不想看到的开闭原则的破坏。
4、总结:
MVC作为一种经典的设计模式,在多种场合与环境下被广泛使用,并已经出现了多种变种。本文提到的被动MVC在WEB开发中被有效地利用,因为web开发中Model的更新较难实时的通知到对应的具体View。而主动MVC在桌面应用也就是Native应用中被广泛使用,是因为桌面应用中数据的更新比较容易与界面进行绑定。
总的来说,MVC作为一种经久不衰的设计模式,有着非常大的实用价值,被动MVC架构完全符合设计思想当中高内聚低耦合的理念,将M、V、C三者的智能划分也很明确,相互之间的耦合性也很小。而主动MVC架构可以实时快速的使界面得到后台的数据,对M、V、C三者的作用也有着划分,但是耦合度相对被动MVC更大,不过主动MVC的开发速度快,模块划分简单易懂也是它的优势。
关于本篇中提到的app工程的下载:稍后会添加
- 基于Android的mvc、mvp以及mvvm架构分析(上)
- Android App 的设计架构:MVC、MVP、MVVM 的分析
- Android架构--MVC、MVP、MVVM
- Android App 的设计架构:MVC、MVP、MVVM与架构
- Android App的设计架构 MVC,MVP,MVVM与架构
- 探究 MVC,MVP,MVVM以及VIPER架构
- MVC,MVP,MVVM以及VIPER架构
- MVC, MVP, MVVM比较以及区别(上)
- MVC, MVP, MVVM比较以及区别(上)
- Android App的设计架构:MVC,MVP,MVVM
- Android App的设计架构:MVC,MVP,MVVM经验谈
- Android常用的MVC、MVP、MVVM架构探索
- Android架构学习MVC、MVP、MVVM(一)
- Android架构学习MVC、MVP、MVVM(二)
- Android架构之MVC,MVP与MVVM
- android MVC/MVP/MVVM架构对比
- Android架构模式:MVC & MVP & MVVM
- MVC, MVP, MVVM比较以及区别-Android
- 31. Next Permutation
- 拓扑排序(算法模板)
- 网络程序设计
- opnet之加强mm1模型之删除事件
- Linux命令记录
- 基于Android的mvc、mvp以及mvvm架构分析(上)
- 【caffe源码研究】第三章:源码篇(13) :损失层
- android studio 多渠道打包以及工程的定制化(上)
- 33. Search in Rotated Sorted Array 旋转排序数组极值的二分求法
- 【寒江雪】开启深度缓存
- 制作Plesk镜像
- JavaWeb学习之路(一)--- 数据库简单操作操作和事务
- 判断对象类型的方法
- Java集合框架