前端之Android入门(5):MVC模式(下)

ct

在上一篇文章中,我们将 View 类单独出来并完成了设计和编写。这次我们将完成 Model 类,并通过 Controller 将两者连接起来,完成这个计算器程序。

模型(Model)就是程序中封装了数据,并定义了操作和处理这些数据的逻辑的对象。在计算器的例子中,就是处理输入的操作数和运算符,并计算返回结果。Let’s Go
(注意:示例中直接使用 double 类型来处理数据,但严格来说很多语言的浮点数计算都是不精确的)

一,设计模型的接口

在程序构建之初,我们首先考虑的应该是各模块间的封装和扩展,设计好模块的接口,然后再实现模块的细节,最后把模块组合起来构成整个程序。这就是面向接口编程的概念。

思考一下 Model 类,它主要实现三个功能,1,接受操作数输入;2,接受运算符输入并返回计算结果;3,重置。这些功能主要都是被外部使用的,所以基于此来设计接口,对其它类而言,只需知道该接口定义了什么方法然后使用就行,而不需要了解实现细节。

创建 com.test.interfaces 包,并右键选择 New -> Interface 创建一个名为 ICalculator 的接口(一般接口命名都以大写字母 I 开头)

android_5_interface1

创建 com.test.model 包,并创建 CalModel 类,同时实现 ICalculator 接口。

android_5_calmodel

二,通过栈和递归函数实现计算器算法

计算器的计算规则很简单,从左到右计算,不考虑运算符的优先级,例如:2 + 1 * 2 - 3,相当于: (((2 + 1) * 2) - 3) = 3;这样保证编写的简易性。

我们知道,栈是一种后进先出的数据结构,我们通过一个栈 dataStack 记录输入的运算数和操作符。如图所示:android_5_datastack

所谓的递归函数,就是接受有限的自然数,通过重复调用自身,最终得到一个自然数结果的函数。算法的核心就是设计这样一个递归函数 popOpOffStack 来对 dataStack 进行求解,过程如图:

android_5_popstack

三,编写程序

按上面的思路,popOpOffStack 可以单独写出,并不需要实例化后才能使用,所以将 popOpOffStack 设为 CalModel 的静态方法则可,如图:

android_5_javapop

完成编写后,可以构建一个栈来测试一下,因为返回的是 double 类型,所以结果是 3.0,如图:

android_5_test1

android_5_test2

核心算法完成后,完成对运算数和操作符的输入的处理并补充其它细节。这样我们的 CalModel 就完成了。

android_5_javamodel

四,通过Controller将Model和View连接

在 Android 的文档中,Activity 是被推荐作为 Controller 的角色,负责视图和模型的协调、沟通。而从 Activity 所拥有的功能的角度看,也是最合适的选择。至此,代码结构也清晰了。MainActivity 将作为 Controller 协调视图和模型,并对一些数据及细节进行处理。其代码如下:

android_5_controller

通过 MainActivity 将模型和视图连接起来后,我们的计算器也最终完成了。可以运行测试一下。

五,结束及回答

本次要点:
1)通过 MVC 设计,使计算器程序有了较好的扩展和维护能力,假如使用另一种计算器算法,只需创建另一实现 ICalculator 接口的 Model 类,并代替原来的 CalMode 则可。当需要增加功能时,可以将代码增加到对应的模块处而不用随意写在一起。

2)这次只介绍了最基本的 MVC 知识,而至于 MVC 模式更高级的使用以及模块间的通信会在后面再讲。从代码的编写中,大家可能会发现 MainActivity 其实很难进行抽象,因为都是一些连接的逻辑和细碎代码。这也就是为什么 MVC 概念提出这么久以来,Model 和 View 的封装技术有了长足进步,而 Controller 却没什么突破的原因之一。作为前端,这方面大家可以对比一下现时流行起来的 MVC 库,对 Controller 角色的处理。而苹果在 Cocoa 中对 Controller 的封装也是很有特色,有机会会介绍对比一下。

3)例子中的细节处理还可以处理得更好,就留给大家修改。另外,有时间的话可以尝试使用经典的双栈算法来实现这个计算器,替换 CalModel 类。

在上几篇文章中大家提到的問题在这里总结回答一下:
1)什么时候用回调和 MVC 的设计为什么可以提高维护和扩展性:对于前者,当我们设计一个模块时,模块除提供方法外,自身发生的事是需要被外界了解时,或外界对这个模块发生的事是“渴望”得知的,这个时候就需要进行回调处理了。对于后者,在这次的文章中讲得比也较清晰了。

2)创建工程时,为什么会多出一些 support-v4、 support-v7 等这些包,这是 Android 的一些高级 API 或组件为在低版本中向下兼容实现而自动加载到工程中的,对自身程序并没有什么影响。

再次感谢大家支持!

Tags: , , ,

ct
ct
Axure7响应式进阶
上一遍
“走出屏幕"的体验
下一遍
11,493
QQ空间 新浪微博 Facebook Google+

相关推荐

留下你的想法吧

  1. Leo says:

    不错,第一时间学习。

  2. 陈琳 says:

    提示有错误,求源代码对比

    • ct says:

      我看看找个地方把源码打包上来,可以先看看错误提示,尝试修复一下 :)

  3. Jin says:

    谢谢你的回答,网上关于android开发的文章很多,但是结合设计模式,把面向对象讲得如此透彻的却凤毛菱角,期待您下一篇文章。好人一生平安! 大神您有微博吗,能粉你一个吗?

  4. BaykalMan says:

    不得不说,结合MVC讲得很透彻了

  5. Leo says:

    过了一个多月了,不再更新了吗?

  6. Wenson says:

    希望出新的啊~~ IOS的MVC啊!!!

  7. 刘晓聪 says:

    谢谢分享,刚接触android。
    程序有2处可以优化的地方
    1 是popOpOffStack添加stack.add(String.valueOf(result))在return result之前,这样“=”操作后可以继续用
    2 是operateIn加一个判断:
    if(number != "0"){
    calModel.pushOperand(number);
    }
    这样就不会出现点击2次运算符,显示结果变成“0”的bug

  8. voidC says:

    1 是popOpOffStack添加stack.add(String.valueOf(result))在return result之前,这样“=”操作后可以继续用

    这个我感觉应该在 if(operate.equals("=")){
    result = CalModel.popOpOffStack(dataStack);
    dataStack.add(String.valueOf(result));
    }else{
    .....
    }
    更好一点.. 否则 直接两次之后在 popOpOffStack 里面 的 return operand 那边也会需要重新入栈一次, 但是那样会导致算法的错误...

  9. 成葛格 says:

    可否分享下计算器代码?

  10. geek says:

    很好的文章,我想知道您还有其他文章么?
    能关注下您的博客什么的么?
    非常感谢!!

  11. says:

    作为前端,这方面大家可以对比一下现时流行起来的 MVC 库,对 Controller 角色的处理。而苹果在 Cocoa 中对 Controller 的封装也是很有特色,有机会会介绍对比一下。
    ----------
    你好,看完你的文章对MVC领悟了不少。可以看出你对mvc挺有研究,上面提到的部分是否有你的其他文章?

  12. says:

    第一次对mvc有了感觉

  13. 润哲 says:

    例子虽小,五脏俱全,解释的很好