基于Android的mvc、mvp以及mvvm架构分析(上)

 

前言:

工欲善其事,必先利其器,工作一段时间后,对于以上十个字的感触是最深的。刚参加工作的时候,并没有对于要做的事情有着自己的理解,经常是上面分配了工作,自己就乖乖地跑去做,也不会思考什么。后来渐渐地发现,做着做着就会出现各种问题,对需求理解的不到位,对问题理解的不深入,会造成各种各样的问题,新功能难以实现,程序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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值