zoukankan      html  css  js  c++  java
  • Android MVC MVP

    从.NET的宠物商店到Android MVC MVP

     

    1 一些闲话

      记得刚进公司的时候,我们除了做常规的Training Project外,每天还要上课,接受各种技术培训和公司业务介绍。当时第一次知道QA和SQA的区别。Training Project时间其实比较紧张,给我们的就是一个英文的需求文档。我们要做的就是数据库设计、结构文档、用例文档、项目搭建、代码编写、单元测试,每个阶段Leader会Review。除此之外,还要E-R图、时序图、用例图。麻将虽小,五脏俱全。哦,对了,所有输出必须英文完成。最后要求项目能一键安装使用。

      不感兴趣?请直接跳到第二部分,哈哈~

      Training Project其实就是做一个购物网站,做一个WinForm,最后用上WCF。对我来说,不是什么大问题,在学校已经做过类似的了。Scott Mitchell 60多篇的ASP.NET教程被我打印出厚厚的四本小书,代码从头敲了个遍。学完后就基本熟悉ASP.NET主流控件的使用,明白数据绑定、缓存以及三层架构的作用了。那么这时候,你可以达到初级的水平了。想进一步提升自己,就要懂得一定的代码封装、自定义控件、理解Pager的生命周期等等。关于书籍,理论方面个人推荐《你必须知道的.NET》、Jeffrey Richter《深入理解 .NET》和《Windows核心编程》。博客园内也有Scott Mitchell文章的中文翻译:Scott Mitchell的ASP.NET2.0数据指南中文版索引。上个图怀念一下:

      所以我的Training Project基本就是这个风格。当然,还有鼎鼎大名的PetShop。当时的水平,也就勉强明白抽象工厂,但这个宠物商店的架构图是长这样的:

      震住菜鸟有没有?Talk is cheap, show me the code。废话少说,放码过来!代码下载下来,光是Library就近20个,菜鸟完全有点无从下手了。你说让我放码,你却退避三舍。

      扯远了,回到我的Training Project。如果光是完成这个Training Project,倒没什么。但是要求一个接一个。说一下印象中还算深刻的编码规范。

      当时接受培训的编码规范是PHILIPS C# Coding Standard,没听说过?那也没什么,微软官网的一些Internal Coding Guidelines总要了解一下吧。我们的代码被Leader要求得用上PHILIPS的规范。我记得我很认真地在每个接口和方法都加上注释,该换行换行,该加括号加括号,变量命名也合乎英文语法(呵呵~你应该见过一半英文一半拼音的命名方式吧)。最后代码Review的时候,Leader挑了一个让我无语的地方:

    /// <summary>
    /// Gets product info by product id.
    /// </summary>
    /// <param name="id">The product id</param>
    public Product GetProductById(long id);

      “你看,所有的语句结束后面应该跟一个英文的“.”,你这个The product id后面没有。”

      我想肯定是我的代码注释写得很规范,他挑不出其它的毛病来。你可能还看不出来的一点,Gets必须加s,呵呵。后来,我发现这样的注释简直多余,一看方法就知道干嘛的好吗。后来写了点Objective-C,发现代码基本不用写什么注释,方法名就是一条短语,自我解释。有的人还把方法名写成一条句子,也是够离谱的。

      上面说了这么多,什么意思呢?知识储备、动手能力很重要。代码看过了也许你就忘了,敲过了才算学过,能总结出来才叫理解。

    • 设计模式是用来解决代码重用的
    • 设计模式是用来隔离变化的
    • 架构是保证软件的可用性、可扩展性、安全性

      设计模式是从编码的层面提炼出来的一种总结,而架构则着眼于全局,对系统的高层次抽象。所以,如果你还没有编写过一定量的代码(几千行、几万行或十几万行,视个人而定),哪来的代码重用、代码可扩展?设计模式基本就是前人经验的总结,有了一定的代码基础能更好地理解;架构则站在更高的维度,要求的就不单单是代码经验了,你还要懂硬件、操作系统、网络环境等等,实践和理论的结合。同时你还得了解技术的边界,能做什么,不能做什么。

    2 MVC

      下面终于轮到MVC和MVP登场了,刚接触这个概念的同学可能会问:他们应该是一种设计模式吧。还真不是。那你上面还说了这么多设计模式和架构?不要紧张,这不是对比学习嘛。这个问题理清还真是需要费点力气,还好已经有人把这个问题搞明白了:为什么MVC不是一种设计模式

      从Android的角度看一下MVC:

     

    Model

      模型层就是一些基础数据源,通常是数据库SQLite、网络请求的JSON、本地XML或Java对象数据。它代表了一些实体类,用来描述你的业务逻辑怎么进行组合,同时也为数据定义业务规则。

    Controller

      控制器是与应用程序相关联的动作集合,它负责处理待响应的请求。它通过界面响应用户输入,通过模型层处理数据,最后返回结果给界面。控制器扮演着模型和界面的粘合剂角色。

    View

      界面就是各种UI组件(XML布局或Java自定义控件对象)。它只负责展示数据,同时接收控制器传过来的结果。

      所以在Android中,activity界面就是View,本地数据或网络数据就是Model,至于Controller嘛,看项目代码怎么组织了。一般来说,activity可以认为是Controller,一方面它负责视图的呈现,一方面控制业务逻辑(先从本地取缓存数据,再从服务端刷新;等等)并处理相关数据。做得好一点,无非再封装一层BusinessLogic,activity再去调用这个BusinessLogic,从而减轻activity的代码负担。但也逃离不了BusinessLogic+activity就是Controller的范畴,因为两者之间存在直接依赖,而且是依赖于具体实现。

      上图模拟了界面可能被用户点击,通过事件传递到控制器,接着控制器发起一个网络请求,响应结果经过转换到了模型层,最后控制器取得模型层的数据并通知界面进行刷新。Android用到MVC的具体实现很多,如ListView,Adapter就是典型的Controller,它在数据变化的时候,就是这样通知界面的:

    adapter.notifyDataSetChanged();

      让我们简化上面的图示:

      当然,这是一种理想状态。在Android中,View和Model也有关联的,所以更接近的图示应该是这样的:

    3 MVP

      MVP(Model-View-Presenter),你可以把它看作MVC的一个变种,用来隔离UI、UI逻辑和业务逻辑、业务数据。

      Presenter代表界面负责处理UI事件,它也需要通过界面来获得用户的输入,然后通过模型层处理数据,再返回结果给界面。跟View和Controller不同的地方在于:View和Presenter利用了接口机制,所以他们完全解耦。所以MVP比MVC更利于后期的扩展和维护,是因为它针对了接口编程。看看上面的PetShop架构图,是不是看到了众多的Interface?了解我放那张图的用心良苦了吧,面向接口编程和合理的层次划分。看一个简单的C#例子。

      接口定义:

    复制代码
    public interface IProduct
    {
        /// <summary>
        /// 获取所有的产品信息
        /// </summary>
        /// <returns>产品信息集合</returns>
        List<Product> GetAllProducts();
    
        /// <summary>
        /// 通过产品编号获取产品信息
        /// </summary>
        /// <param name="productId">产品编号</param>
        /// <returns>产品实体具体信息</returns>
        Product GetProductById(long productId);
    }
    复制代码

      从SQL Server数据库获取数据

    复制代码
    public class SQLServerProvider : IProduct
    {
        public List<Product> GetAllProducts()
        {
            // TODO
        }
    
        public Product GetProductById(long productId)
        {
            // TODO
        }
    }
    复制代码

      从Oracle数据获取数据

    复制代码
    public class OracleProvider : IProduct
    {
        public List<Product> GetAllProducts()
        {
            // TODO
        }
    
        public Product GetProductById(long productId)
        {
            // TODO
        }
    }
    复制代码

      使用

    复制代码
    class Program
        {
            static void Main(string[] args)
            { 
                IProduct productProvider= new SQLServerProvider();
                productProvider.getAllProducts();
    
                // 或者
                IProduct productProvider= new OraclerProvider();
                productProvider.getAllProducts();
            }
        }
    复制代码

      只要接口不变,以后你想从SQLite、DB2获取数据,只需要再写个类实现IProduct接口就行了,完全不需要修改原有的类,然后在实例化的时候换一下new的对象。是不是有点开闭的意味?对扩展开放,对修改关闭。这就是设计模式中的开闭原则(OCP:Open Closed Principle)。利用接口编程,也方便了后期进行单元测试。

      回到我们的Presenter,总结一下MVP的关键点:

    • 用户与View进行交互
    • View和Presenter是一对一关系
    • View持有Presenter的引用,但View对Model没有引用 

    4 MVP在Android中的实现

      有人实现了一个demo,我们来学习一下吧 。以下是类图:

      四个接口:OnLoginFihishedListener, LoginPresenter, LoginInteractor, LoginView,两个实现类:LoginPresenterImpl和LoginInteractorImpl,一个登录界面LoginActivity。看起来有点费劲?我再把它抽象一下:

      代码核心如下,注释中的1->2->3->4->5就是具体的调用流程

    复制代码
    public class LoginActivity extends Activity implements LoginView, View.OnClickListener {
        private LoginPresenter presenter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ......
            presenter = new LoginPresenterImpl(this);
        }
    
        @Override 
        public void onClick(View v) {
            // 1、调用LoginPresenterImpl进行校验
            presenter.validateCredentials(username.getText().toString(), password.getText().toString());
        }
    
        @Override
        public void navigateToHome() {
            // 5、回调结果,LoginActivity作跳转
            startActivity(new Intent(this, MainActivity.class));
            finish();
        }
    }
    复制代码
    复制代码
    public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {
        private LoginView loginView;
        private LoginInteractor loginInteractor;
    
        public LoginPresenterImpl(LoginView loginView) {
            this.loginView = loginView;
            this.loginInteractor = new LoginInteractorImpl();
        }
    
        @Override 
        public void validateCredentials(String username, String password) {
            // 回调,通知LoginActivity,显示加载中提示
            if (loginView != null) {
                loginView.showProgress();
            }
            // 2、开始调用LoginInteractorImpl中的方法
            loginInteractor.login(username, password, this);
        }
        
        @Override
        public void onSuccess() {
            // 4、回调,通知LoginActivity
            if (loginView != null) {
                loginView.navigateToHome();
            }
        }
    }
    复制代码
    复制代码
    public class LoginInteractorImpl implements LoginInteractor {
        @Override
        public void login(final String username, final String password, final OnLoginFinishedListener listener) {
            // 3、TODO,登录逻辑。成功后回调给上层调用者
            listener.onSuccess();
        }
    }
    复制代码

      好了,就这样。画图太累了,如对你有帮助,就推荐一下吧。

  • 相关阅读:
    服务器状态码
    QuerySet中添加Extra进行SQL查询
    django配置一个网站建设
    MySQL数据库查询中的特殊命令
    125. Valid Palindrome
    121. Best Time to Buy and Sell Stock
    117. Populating Next Right Pointers in Each Node II
    98. Validate Binary Search Tree
    91. Decode Ways
    90. Subsets II
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5128475.html
Copyright © 2011-2022 走看看