基于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工程的下载:稍后会添加

1 0
原创粉丝点击