记得刚进公司的时候,咱们除了作常规的Training Project外,天天还要上课,接受各类技术培训和公司业务介绍。当时第一次知道QA和SQA的区别。Training Project时间其实比较紧张,给咱们的就是一个英文的需求文档。咱们要作的就是数据库设计、结构文档、用例文档、项目搭建、代码编写、单元测试,每一个阶段Leader会Review。除此以外,还要E-R图、时序图、用例图。麻将虽小,五脏俱全。哦,对了,全部输出必须英文完成。最后要求项目能一键安装使用。html
不感兴趣?请直接跳到第二部分,哈哈~android
Training Project其实就是作一个购物网站,作一个WinForm,最后用上WCF。对我来讲,不是什么大问题,在学校已经作过相似的了。Scott Mitchell 60多篇的ASP.NET教程被我打印出厚厚的四本小书,代码从头敲了个遍。学完后就基本熟悉ASP.NET主流控件的使用,明白数据绑定、缓存以及三层架构的做用了。那么这时候,你能够达到初级的水平了。想进一步提高本身,就要懂得必定的代码封装、自定义控件、理解Pager的生命周期等等。关于书籍,理论方面我的推荐《你必须知道的.NET》、Jeffrey Richter《深刻理解 .NET》和《Windows核心编程》。博客园内也有Scott Mitchell文章的中文翻译:Scott Mitchell的ASP.NET2.0数据指南中文版索引。上个图怀念一下:git
因此个人Training Project基本就是这个风格。固然,还有鼎鼎大名的PetShop。当时的水平,也就勉强明白抽象工厂,但这个宠物商店的架构图是长这样的:github
震住菜鸟有没有?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,发现代码基本不用写什么注释,方法名就是一条短语,自我解释。有的人还把方法名写成一条句子,也是够离谱的。安全
上面说了这么多,什么意思呢?知识储备、动手能力很重要。代码看过了也许你就忘了,敲过了才算学过,能总结出来才叫理解。网络
设计模式是从编码的层面提炼出来的一种总结,而架构则着眼于全局,对系统的高层次抽象。因此,若是你尚未编写过必定量的代码(几千行、几万行或十几万行,视我的而定),哪来的代码重用、代码可扩展?设计模式基本就是前人经验的总结,有了必定的代码基础能更好地理解;架构则站在更高的维度,要求的就不仅仅是代码经验了,你还要懂硬件、操做系统、网络环境等等,实践和理论的结合。同时你还得了解技术的边界,能作什么,不能作什么。
下面终于轮到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也有关联的,因此更接近的图示应该是这样的:
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的关键点:
有人实现了一个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) { // 一、调用LoginPresenterImpl进行校验 presenter.validateCredentials(username.getText().toString(), password.getText().toString()); } @Override public void navigateToHome() { // 五、回调结果,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(); } // 二、开始调用LoginInteractorImpl中的方法 loginInteractor.login(username, password, this); } @Override public void onSuccess() { // 四、回调,通知LoginActivity if (loginView != null) { loginView.navigateToHome(); } } }
public class LoginInteractorImpl implements LoginInteractor { @Override public void login(final String username, final String password, final OnLoginFinishedListener listener) { // 三、TODO,登陆逻辑。成功后回调给上层调用者 listener.onSuccess(); } }
好了,就这样。画图太累了,如对你有帮助,就推荐一下吧。