博客首页|TW首页| 同事录|业界社区
2011-09-08

摘要

之前一段时间开始使用cocos2d来开发iOS游戏应用,也有一些心得,所以打算写下这系列的教程。

正文


本篇是《cocos2d开发教程系列》的总述篇,主要简单介绍下cocos2d,然后说明本系列教程的目录。

注: 如果没有特别说明,本博客所以提及 cocos2d 都是指 cocos2d iphone.

cocos2d简介

cocos2d的介绍可参考 about cocos2d 中的介绍,我摘取比较重要的部分说明如下:

cocos2d for iPhone is a framework for building 2D games,
demos, and other graphical/interactive applications.
It is based on the cocos2d design:
it uses the same concepts, but instead of using python it uses objective-c.

所以熟悉python的同学应该对于其哲学会更加易于理解和接受。

说明

  1. 虽然题目是cocos2d,但是很多也是iOS通用的技术,这个会具体指出
  2. 虽然自己对cocos2d有一定熟悉,但不保证文中的内容是最优的,甚至不能保证可用,风险自己承担
  3. 文中的代码使用 MIT协议
  4. 更新周期大致一周一篇
  5. 系列教程完成后会集结成一个pdf供下载
  6. 欢迎交流,指教,留言

下载原文

可从 此处 查看或者下载。

2011-05-18

摘要

本文详细说明了使用 flashdevelop 基于 Robotlegs flash框架完成一个Hello World示例的 教程,这个示例包括完整的MVC(及Service)。 本示例可以 下载源代码

正文

开始之前

本文已经假定你具备下面的知识:

  1. 阅读过类似于 Essential Actionscript 3.0 这样的书籍,对于Actionscript语言和flash基本显示体系、事件等有一定了解
  2. 能够较熟练地使用 flashdevelop flash IDE
  3. 阅读过 Robotlegs 的文档 Robotlegs Best Practice

下载相关的库

  1. 下载 Robotlegs 相关的swc库,可以从 http://downloads.robotlegs.org/robotlegs-framework-v1.4.0.zip 下载
  2. 解压后,在bin目录下有个 robotlegs-framework-v1.4.0.swc 以及libs目录下有个 SwiftSuspenders-v1.5.1.swc
  3. 上面两个swc就是我们需要的

建立项目

  1. 打开 flashdevelop
  2. 建立一个名为 RobotLegsHelloWorld 的AS3 Project
  3. 将上一步中的两个swc拷贝到新建成项目中lib目录下(IDE左侧的文件结构列表中)
  4. 在拷贝好的文件上点击右键,选择Add To Library,将两个swc都加入到library中

这时基本的结构就好了,我们开始写代码。

定义需求

这个例子是个完整的说明 Robotlegs 框架的例子,所以会涉及到框架的各个主要部分,包括Model, View, Controller,为此 我们定义了这个例子所要完成的需求如下:

用户登录系统的过程,包括:用户输入名字和密码,提交表单,后端进行验证(简化起见,不作真正的验证),并返回成功与否的结果。

整个结构

根据上面的需求定义,我们可以有下面的组织架构:


具体的各个角色(actor)的功能,参考代码中的注释。

需要说明的是:

  1. 在理解代码和框架时,最好了解如单例模式等设计模式
  2. 对于 依赖注入 有一定理解
  3. 对于 MVC 有一定理解

其它应该还是比较容易理解的。

总结

从最开始使用 pureMVC 到这个项目使用 Robotlegs ,两个框架都挺好的,在整个项目的进展中,更加觉得要控制住代码质量并不 取决于框架本身,而是取决于使用框架的人,各位程序员们要加油了。

另外,也可以参考我的另一篇博客 RobotLegs简介 .

下载原文

可从 此处 查看或者下载。

下载源代码

可从 下载源代码 查看或者下载。

2011-05-10

摘要

本文简单地对 pureMVC 和 robotlegs 进行了对比,并且较详细地说明了 robotlegs 各个角色的职责, 以及使用 robotlegs 的常见的注意事项。

正文

最近开始前端的开发,即flash开发,之前使用的是 pureMVC ,也写过一篇关于 pureMVC notification 的博客;而这个项目的开发是使用 robotlegs , 所以,这篇博客主要是基于过去这几周的工作和自己的理解对这个框架的一些简单 说明,希望对看到的朋友有益。

整体感受

做前端的开发不多,与后端相比一些UI上的处理、事件的处理等差异比较大,但是 编程的思想(如解耦、接口编程而非实现编程等)还是一致的,与后端比没有多大 可比性,所以我想简单地说明我对于 pureMVCrobotlegs 二者的一些 感受。

pureMVC 是基于 notification 的一个MVC框架,主要目的是为了各个部分 能够解耦,当然它也基本上能够做到。 robotlegs 则是基于 message 以及 message 携带的payload(数据等)来实现解耦。 robotlegs 是基于pureMVC 的 思想,但是在一些方面更加出色,例如消息的强类型,依赖注入方式,消息携带payload 等等。

当然,任何一个好的东西(语言、框架等)最终还取决于用的人,语言和框架本身并不能 保证用户的代码清晰、解耦等,当然它只是尽可能地做到这点。

关于robotlegs


还是一个MVC的框架,它的几个角色的主要功能有:

  1. M(odel): 提供数据的存储(与后端数据库表的映射),操作等

  2. V(iew): 呈现给用户,提供对用户操作的响应等

    • 通常一个V对应于一个Mediator
    • Mediator的职责是转发来自V的事件到整个框架,或者接收来自框架的事件并更新V(通常通过调用V的方法)
  3. C(ontroller): 业务逻辑,处理M和V之间的连动

    • 通常对应于一个Command
    • Command的职责有,实现业务逻辑,发送事件等

除此之外, robotlegs 还有一个角色是 service , 主要职责是提供一些获得数据的服务接口, 它本身并不存储(和model的区别)数据和提供数据的操作。

为了更好的说明起见,我想以一个员工考勤系统为例来说明各个角色的职责。

为了简化起见这个员工考勤系统只有如下功能:

  1. 提供一个界面供员工登录
  2. 提供一个界面供员工点击来作为考勤的时间

后端也可以很简单,例如有用户表User(id, name), 用户考勤表CheckInRecord(user_id, checkin_time), 那么前端使用robotlegs的结构可以采用:

使用robotlegs的注意事项

  1. 注意各个角色初始化的顺序(顺序关系到注入及事件处理的逻辑)
  2. 注意注入的使用(提前初始化)
  3. 其它的可参考 Best Practice

总结

还是一句话,框架或者语言只能在最大限度上保证用户代码的清晰、可读、解耦、可维护等(例如python语言),但是 最终的代码质量还是取决于用户本身(程序员),所以一些编程的哲学和模式才是更为关键的(大致可看下 Code Complete 、 Design Pattern 等)。

最近一直在看 Code Complete ,一句话让我感受非常深,大致意思是说 写代码的过程就是复杂度管理的过程(Complexity Management) , 这一点之前的认识不够,我推荐给看到的朋友,将它和 DRY 原则一样牢记在心吧。

下载原文

可从 此处 查看或者下载。

2011-04-18

摘要

本文主要说明在 django 中 schema migration 的两种最常用工具 south 和 evolution, 并且说明二者的差异和为什么要使用 south 作为最主要的 migration 工具。

正文

关于schema migration

无论我们使用何种语言进行web开发,快速开发随之相伴的是需求的不断变动,也就意味着我们要不断 增加或者调整已有的数据库模式(database schema),譬如一个很常见的变动是,我们需要在用户表中 增加一个状态位来标记当前用户是否已经删除,而不是直接从数据库中删除(虽然我不支持这样的保留用户 数据的行为,可是如今大多数的应用即使你要删除自己的账号,其实也不会永久删除的,所以,只要是网上的 信息,大致你可以认为是不会消失的),那么除了应用逻辑的改动外,你需要在数据库上增加一个状态位的 字段。

上面就是一个很常见的应用场景,当然诸如字段的属性更改,增加或者减少字段等等,也都属于这个范畴。

很可惜, django 本身并不支持 schema migration (也就是当你执行 syncdb 时并不会产生任何作用, 增加和删除字段会有效,不过复杂的则不支持,如更改一个字段的属性等),这也就是 evolution 和 south 所要解决的问题。

关于evolution

相比于下面要说明的 south , evolution 出现的比较早,它的主要思路是:在项目初始时会对所有的数据库schema 进行记录(也会存在一个数据库表中),当某个表的schema有更改时,当你执行 syncdb 时, evolution也会与当前记录的 schema进行比较,如果 evolution 认为有更改,则它会进行比较进而生成一个最新schema与上次schema所要做更改的sql,用户可选择执行来进行 schema migration.

相对而言, evolution 很容易集成到自己的项目中,并且也很容易使用,并且 通常 也能很好工作。所以,在我最初的 项目中我基本都是使用 evolution ,但是相比于 south , evolution 的不足有:

  1. 开发并不活跃(写本文时,看到的最近一次更新是2010/11/19)
  2. 没有得到 django 项目核心开发人员的推荐和认可(而 south 是推荐的选项)
  3. 不支持1.2的多数据库
  4. 不支持数据的迁移(只支持表结构本身的迁移)
  5. 不支持rollback到某个schema
  6. 通常需要从项目上线起就开始使用(也就是没有数据时),对于已经有数据的项目则不支持
  7. 跨app的迁移并不支持
  8. migration的code并不能纳入到版本控制工具中(因为 evolution 使用数据库表,而数据库本身是没有状态的)

当然它也有诸如简单易用,学习曲线低,配置较少等优点,当然 south 也并不复杂,并且有更多的优点,请参考下面的说明。

关于south

south 正是因为 evolution 有这么多的问题,作者才开始了这个项目,上面提到的8个问题, south 已经很好 地进行了解决,并且在未来可能加入到 django 的代码库中(其实1.2也差点合并进去,因为 south 作者不建议现在合并 才最终没有成形, 具体可查看 作者的说明 )。

如果你之前没有使用过 south ,那么从现在起开始用 south 会对你受益匪浅; 如果你之前使用的是 evolution ,你会发现 south 更加友好和强大。

那么,不妨从今天起在你的项目中开始使用 south 吧,如何开始,具体可以参考 south的tutorial

另外,你也可以看看 south alternatives 和 south’s design 两篇文章来了解更多。

总结

django 在不断发展,相应的周边的工具也是层出不穷,选择合适高效的工具,对于开发者而言是有很重要的意义的, 而让人头疼的 schema migration 则会因为 south 的出现而得到很好的解决。

下载原文

可从 此处 查看或者下载。

2011-04-16

在各个系统下都习惯了 vim 这个编辑器,但是基于win下的gvim的默认字体是 fixedsys,显示中文时实在是磕碜的不行,而直接在vimrc中写入中文名的 字体配置又不行(如新宋体),经过确认要么将encoding设置为cp936,要么 使用中文字体的英文名称,还好有一篇博文有详细的记录,参考: 常见系统中文字体的英文名, 简单摘录如下:

Mac OS的一些:

华文细黑:STHeiti Light [STXihei]
华文黑体:STHeiti
华文楷体:STKaiti
华文宋体:STSong
华文仿宋:STFangsong
儷黑 Pro:LiHei Pro Medium
儷宋 Pro:LiSong Pro Light
標楷體:BiauKai
蘋果儷中黑:Apple LiGothic Medium
蘋果儷細宋:Apple LiSung Light

Windows的一些:

新細明體:PMingLiU
細明體:MingLiU
標楷體:DFKai-SB
黑体:SimHei
宋体:SimSun
新宋体:NSimSun
仿宋:FangSong
楷体:KaiTi
仿宋_GB2312:FangSong_GB2312
楷体_GB2312:KaiTi_GB2312
微軟正黑體:Microsoft JhengHei
微软雅黑体:Microsoft YaHei

装Office会生出来的一些

隶书:LiSu
幼圆:YouYuan
华文细黑:STXihei
华文楷体:STKaiti
华文宋体:STSong
华文中宋:STZhongsong
华文仿宋:STFangsong
方正舒体:FZShuTi
方正姚体:FZYaoti
华文彩云:STCaiyun
华文琥珀:STHupo
华文隶书:STLiti
华文行楷:STXingkai
华文新魏:STXinwei

所以我在vimrc中将guifont配置为”NSimSun”即可(新宋体)。

2011-04-05

因为工作需要,最近也开始学习和使用 pureMVC , 之前也写过一个 pureMVC Hello World教程, 算是开始入门这个框架和AS这种语言.经过两周多的学习和使用,生出了几个疑问, 和公司的做AS的同事 沟通时,发觉他也并不能清楚地说出所以然来,所以决定自行研究这些问题,并尝试给出解答.

问题大致有下面几个:

  1. 如果一个notification有多个订阅者,那么这多个订阅者之前的执行顺序是怎么样的?
  2. 一段代码在执行时,如果发出一个notification,是等待相应的notification订阅者处理结束再继续呢,还是 直接继续,相应的notification订阅者异步地执行?
  3. 一个notification的多个订阅者之间的执行是异步的,还是顺序执行的?
  4. 是否可能出现notification死循环的问题? (即notification A的订阅者X发出notification B,B的订阅者 又发出notification A,形成一个没有出口的循环)

研究和解决上述问题

为了准确研究出上述的问题和答案,我采用的方法包括两种: 实验和源代码阅读(其实只要阅读源代码即可).

实验

相关的代码可从 TestPureMVC 下载.

这个程序主要是如下的流程:

Startup -> StartUpCommand -> registerMediator(AMediator, BMediator, CMediator) -> sendNotification(Test) -> CMediator(Test) -> sendNotification(Second) ->CMediator(Second) ->sendNotification(Test)

从而形成了一个循环,另外,也可简单地在StartUpCommand类中更改来测试其它的情况.

从代码中能够说明(对应于本文最初的4个问题):

  1. notification的多个订阅者的执行顺序是按照其注册的顺序执行的,也就是全局的view中维护的mediator数组中的顺序
  2. 因为AS中没有类似于sleep的方法,所以无法确定2(具体结果参考下文,代码分析部分)
  3. 同2
  4. 会出现死循环,最终会出现栈溢出的错误

源代码分析

因为Command和Mediator都可以处理和发送notification,所以这里只以Mediator的处理为例来说明.

我们先看notification是如何通知的:

// org.puremvc.as3.core.Views.as
public function notifyObservers( notification:INotification ) : void
{
    if( observerMap[ notification.getName() ] != null ) {

        // Get a reference to the observers list for this notification name
        // 获得这个notification的所有订阅者数组
        var observers_ref:Array = observerMap[ notification.getName() ] as Array;

        // Copy observers from reference array to working array,
        // since the reference array may change during the notification loop
        //  notification的循环中可能增加新的订阅者,所以这里深度拷贝一份
        // 注意顺序并没有更改
        var observers:Array = new Array();
        var observer:IObserver;
        for (var i:Number = 0; i < observers_ref.length; i++) {
            observer = observers_ref[ i ] as IObserver;
            observers.push( observer );
        }

        // Notify Observers from the working array
        // 根据数组中的顺序依次来通知相应的订阅者
        // 注意这里是顺序执行的
        for (i = 0; i < observers.length; i++) {
            observer = observers[ i ] as IObserver;
            observer.notifyObserver( notification );
        }
    }
}

接着来看事件是如何注册的,从而形成订阅者的数组:

// org.puremvc.as3.core.Views.as
public function registerMediator( mediator:IMediator ) : void
{
    // do not allow re-registration (you must to removeMediator fist)
    if ( mediatorMap[ mediator.getMediatorName() ] != null ) return;

    // Register the Mediator for retrieval by name
    mediatorMap[ mediator.getMediatorName() ] = mediator;

    // Get Notification interests, if any.
    var interests:Array = mediator.listNotificationInterests();

    // Register Mediator as an observer for each of its notification interests
    if ( interests.length > 0 )
    {
        // Create Observer referencing this mediator's handlNotification method
        var observer:Observer = new Observer( mediator.handleNotification, mediator );

        // Register Mediator as Observer for its list of Notification interests
        for ( var i:Number=0;  i<interests.length; i++ ) {
            // 注意: 这里是根据Mediator有兴趣的notification来分别加入到对应的订阅者数组中
            // 而加入的次序正好是根据这个Mediator的注册顺序
            registerObserver( interests[i],  observer );
        }
    }

    // alert the mediator that it has been registered
    mediator.onRegister();

}

// registerObserver的具体实现
public function registerObserver ( notificationName:String, observer:IObserver ) : void
{
    var observers:Array = observerMap[ notificationName ];
    if( observers ) {
        // 顺序加入到对应的订阅者数组中
        observers.push( observer );
    } else {
        observerMap[ notificationName ] = [ observer ];
    }
}

我们再看通知给订阅者时的处理逻辑:

public function notifyObserver( notification:INotification ):void
{
    // 根据notification传过来的上下文来执行相应的处理逻辑
    this.getNotifyMethod().apply(this.getNotifyContext(),[notification]);
}

到此,从我们对源代码的分析上,我们就可以完整的回答本文初始的4个问题:

  1. 如果一个notification有多个订阅者,那么多个订阅者之间是按照注册的顺序来执行的
  2. 在AS中不存在异步的执行,所以,当前的代码的执行会等待所有的notification按照1中 的顺序执行完成后,才开始继续执行当前的代码(相当于调用一个函数)
  3. 一个notification的多个订阅者之间是顺序执行的,顺序是按照1中的说明
  4. 存在死循环的可能,因为如2中说明,sendNotification相当于根据注册的顺序来顺序执行 相应的处理逻辑,如果在处理逻辑中又包含触发sendNotification的事件,则整个执行 会成为一个死循环,从而导致栈溢出

总结

通过对本文开始4个问题的分析,弄清楚了 pureMVC 核心的notification机制的几个 核心问题,对于后续的工作和学习都是很有益处的.

2011-03-29

最近开始写一些ActionScript的代码,因为是基于 pureMVC ,所以 经常看到一处代码突然又sendNotification去通知处理另外的动作, 看了看处理的逻辑,很多竟然是有副作用的。于是想到了最初学习 程序语言时,书中云Goto语句是程序的一大罪恶来源,当时并不理解, 也不知道各个语言大师为何如何痛恨,今天当自己面对这个问题,才 更加贴切地理解了这里面的痛苦,也就明白了为什么当下主流的现代语言 都去掉了Goto语句。

本文就想探讨下类似于Goto语句这样的跳转对于程序会带来什么后果,特别 会讨论具有副作用的跳转语句。

在讨论之前,我们先定义下 程序的副作用, 根据 wikipedia 中的定义为:

In computer science, a function or expression
is said to have a side effect if, in addition to producing a value,
it also modifies some state or has an observable interaction
with calling functions or the outside world.

也就是说:

一个函数或者一段代码除了返回值外,还会更改程序的状态或与调用函数
或者外部世界有明显的交互。

简单地说就是一段代码改变了程序的状态或者调用函数的状态。举个例子,在下面的 伪代码中,

a = 10;
do_something_with_side_effect(a, other_args);
handle(a);

如果在第二行的代码中对a作了一定的修改,则在第三行的代码中总是会依赖于这次更改, 从而导致你必须了解do_something_with_side_effect这个函数究竟做了什么,会对a有什么 影响等,从而丧失了程序的封装性(即无须了解函数的细节)。

下面我想说说这种具有副作用的程序流程跳转会对整个程序和程序员带来哪些影响。

对程序的影响

副作用的代码对于程序本身的影响通常是出现运行时的错误,或者不符合我们预期的结果。 因为程序流程的改变,而这种改变又会影响到程序的状态,所以在编译时很难预估出准确的 程序结果,或者预估错误。

当然, 程序只会做程序员让它做的事 所以最终的结果对于计算机而言,也只是忠实地去 实现既定的逻辑和跳转,至于其中是否有副作用,就像牛看见林志玲与凤姐一样的感觉。

对程序员的影响

对程序员的影响是显而易见的,而且对程序员的影响是这种跳转最令人诟病的所在。程序员也是人, 当他在读取代码时,他没法像计算机那样轻易的设置断点,进入另一个上下文,继而返回断点继续 当前的上下文,程序员只能靠记忆或者简单的书签,但因为记忆的驻留性会导致这种断点的切换成本 非常高,继而导致程序员对这些代码的理解产生偏差甚至错误。久而久之,程序员在经历若干次这种 错误后,会变得对于这些代码只是试图打补丁,而不是从本质或者更深层次理解优化,从而这段代码 便成了人人不敢动的legacy code.

程序是写给程序员而不是电脑的 这是一个合格程序员基本的意识,所以程序的功能和性能相比于可读性, 大多时候后者是更重要的,特别是如今CPU的进步以及我们通常应用场景的普通。

教训

那么我想程序员在进步和成长过程中总是会面对这样类似的问题,例如从喜欢全局变量的强大性到 痛恨全局变量的不可维护性,从追求代码的trick到追求代码的易读,我们总是在不断学习和进步。

那么具体到这一点,我的想法是:

  1. 让子程序(例如跳转,函数等)无副作用(参考上面对副作用的定义)
  2. 减少导致程序出现流程异常跳转的可能(如Goto语句)
  3. 减少具有副作用的代码

当然,上面的三条是逐条递近的,当达到上述的几点时,维护你的程序的程序员他会打心眼里感谢你的。

2011-03-21

PureMVC 是大量使用于flash开发的一个框架,它很好地对处理对象进行了 最常见的MVC模式划分,清晰的逻辑和功能使得开发者能够很好地开发 新的功能,阅读旧的功能等,所以很多的flash的前端代码都是基于 PureMVC 开发的.

最近因为工作上的需要,我开始学习 PureMVC ,之前也简单地学习过,也按照 官方的教程完成过一个示例,并且也仔细阅读过 相关的教程 ,但是对于 PureMVC 的理解还是相对粗浅,这次在之前理解的基础上做出了一个最简单的 Hello World式的教程,希望加深自己对于这个框架的理解的同时,也能够帮助到 其他人.

PureMVC Hello World

这个示例的主要场景如下:

  1. 在UI上显示一个登录用的表单
  2. 表单上有name, password,和登录的按钮(代码中使用的还是TextField)
  3. 点击登录后,如果name,password匹配则显示登录成功,否则显示登录失败

基于上面的描述,再根据 PureMVC 的框架,我们大致可分为如下几个部分:

  1. 启动用的单例AppFacade

  2. Proxy:

    • LoginProxy: 用于验证用户提交的表单是否合法
  3. Command:

    • StartUpCommand: 用于启动整个应用的Command, 也兼做一些初始化的工作
    • initMVCCommand: 用于初始化并注册相关的MVC,如mediator, proxy等
    • LoginCommand: 用于处理Login时的逻辑,如调用LoginProxy来进行验证
  4. Mediator:

    • ViewMediator: 用于UI的显示

整个应用的流程为:

AppFacade(register command, send notification to start)-> StartUpCommand(execute) -> initMVCCommand(execute, register MVC)
-> ViewMediator(construtor, init the UI)

ViewMediator(Login Button Clicked) -> Login(Notification) -> LoginCommand(verfiy login using LoginProxy) -> Login_Succ/Login_Fail
-> ViewMediator(show the login result message)

那么下面我们分别看下重要的几个模块.

AppFacade

这是用于启动和关联整个 PureMVC 通信过程的辅助性类,单例的. 在这个类中主要注意,初始时的notification发送,注册初始时的Command等即可.

具体代码见: https://github.com/topman/blog_code/blob/master/HelloPureMVC/src/AppFacade.as

Proxy

Proxy主要功能是处理数据相关的信息,也即Domain logic(域逻辑),通常它只处理数据,保证数据的正确,完整性等. 而与此不同的是Command,它处理的是Business logic(业务逻辑),它更加关注应用所在业务的相关逻辑.

参考 Business Logic和Domain Logic的区别.

在示例中的Proxy, 它的主要功能是提供相关的数据, 因为登录验证是一个非常常见的功能,在实际的登录验证中, 通常是后端进行验证,返回一个成功或者失败即可,所以此例中将验证也放入了Proxy的逻辑中.

具体代码见: https://github.com/topman/blog_code/blob/master/HelloPureMVC/src/model/LoginProxy.as

Command

Command是处理应用的Business Logic(业务逻辑),与Proxy的区别参考上面Proxy的部分. 本例中的Command主要是包括三个,即:

  • StartUpCommand: 应用启动的命令,调用其它的初始化命令即initMVCCommand
  • initMVCCommand: 用于初始化整个MVC,注册MVC各个部分
  • LoginCommand: 用于处理登录逻辑的命令

具体的代码见: https://github.com/topman/blog_code/tree/master/HelloPureMVC/src/controller

注意在 PureMVC 中命令有MacroCommand和SimpleCommand两类,前者调用后者,后者则实现具体的业务逻辑.

当然所有的逻辑都可以使用SimpleCommand来完成,但是通常MacroCommand命令可以简化整个代码结构,使得 逻辑更加清晰易读.

Mediator

由于示例比较简单所以只有一个Mediator来处理相关的UI显示, Mediator通常的功能是显示, 接收UI的事件, 响应来自业务逻辑的UI变动等.

示例中主要是两个功能,一个是初始化时显示登录对话框,一个是对于Login_Succ和Login_Fail事件的响应. 相应的代码参考:https://github.com/topman/blog_code/blob/master/HelloPureMVC/src/view/ViewMediator.as

结语

我个人之前更多的是做后端开发,没有系统地接触过前端的开发,如Javascript, flash等. 第一次使用和了解 PureMVC , 发现其理念与后端的MVC框架是一致的,只是在更细层面进行了MVC的划分, 例如一个项目前端是flash,后端是 DjangoDjango 本身是MVC,而 PureMVC 又是在 Django 的V层上进行了更加细致的MVC划分.

Django 的MVC是简洁,清晰和优美的,了解 PureMVC 后,同样的特征也适用,我想在后续开发中可以比较熟练地加以应用, 也能够更加顺手地控制工作的进展,不至于项目的进展完全驱动于自己不熟悉的前端.

2011-03-19

Google Contact是大家最常用的通信录之一,而我所有的通信录都 是使用Google来管理的,并且使用于Gmail, 手机等通信录客户端。

那么经常会遇到下面的需求,就是将其它格式的通信录导入到Google Contact中, 当然Google Contact已经提供了常见的导入界面,很方便操作,但是对于程序控制 下的导入,则需要相关的脚本来完成,而我写的这个脚本就是为了这个目的。

相关的代码可以在 https://github.com/towerjoo/shared_contact 下载或者checkout, 目前的主要功能包括:

  1. 支持导入
  2. 支持导出
  3. 支持删除

注意:

  1. 只支持Google的Domain用户
  2. 24小时后才能在通信录和发邮件时的自动补齐中出现
  3. 具体说明参考代码库的文档(可使用 sphinx 来生成文档)

实现方面

主要是基于已有的 http://code.google.com/p/google-shared-contacts-client/ ,使用 fields mapping(映射)的思路来完成。

To Do

  1. 增加对Gmail的支付(非Domain)
  2. 增加常用Contact的映射模板,如outlook等
2011-03-09

接着 上文 的内容,本文将基于自己的理解尝试回答 上文 中研究列表中的3和4,即:

  1. 理解和分析Admin系统的设计和代码书写值得学习和注意的问题
  2. 总结

理解和分析Admin系统的设计和代码书写

Admin系统具有多种特征,首先它是一个普通的 Django 应用,其次它又承担 作为各个其它Djago应用的基础应用而存在,所以我们可以从上面两个方面 来加以分析。

作为一个普通的应用

Django 本身是一个基于MVC的web框架,各个层次非常清楚,当然在 Django 中,更多地被 称为MTV,即Model, Template, View,对应于MVC的Model, View, Controller。 其中Template和View的衔接是通过url映射完成,而url映射也是 Django 架构中最为巧妙的 地方。

http://towerjoo.blog.techweb.com.cn/wp-content/blogs.dir/14215/files/blog/django_layers.png对于一个 Django 的应用,我们通常可以从url映射入手,找到url处理的函数,和渲染出的页面, 从而理清整个的应用逻辑。

Django 的Admin应用的url映射大致如下:

# sites.py
urlpatterns = patterns('',
    url(r'^$',
        wrap(self.index),
        name='index'),
    url(r'^logout/$',
        wrap(self.logout),
        name='logout'),
    url(r'^password_change/$',
        wrap(self.password_change, cacheable=True),
        name='password_change'),
    url(r'^password_change/done/$',
        wrap(self.password_change_done, cacheable=True),
        name='password_change_done'),
    url(r'^jsi18n/$',
        wrap(self.i18n_javascript, cacheable=True),
        name='jsi18n'),
    url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
        'django.views.defaults.shortcut'),
    url(r'^(?P<app_label>\w+)/$',
        wrap(self.app_index),
        name='app_list')
    )

# Add in each model's views.
for model, model_admin in self._registry.iteritems():
    urlpatterns += patterns('',
        url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
                include(model_admin.urls))
    )

从上面的代码,我们可以看到,Admin应用的landing page,登出,密码修改等相应的逻辑, 重点查看最后几行代码,将已经注册的应用的model(数据库的表)相应url映射指向了其自身的方法。

我们来看model_admin.urls这个逻辑:

urlpatterns = patterns('',
    url(r'^$',
        wrap(self.changelist_view),
        name='%s_%s_changelist' % info),
    url(r'^add/$',
        wrap(self.add_view),
        name='%s_%s_add' % info),

    # added by Tower Joo At 2011/2/27
    # have to be ahead of change_view unless it will be overrided
    url(r'^export/$',
        wrap(self.export),
        name='%s_%s_export' % info),
    url(r'^aggregate/$',
        wrap(self.aggregate),
        name='%s_%s_aggregate' % info),

    url(r'^(.+)/history/$',
        wrap(self.history_view),
        name='%s_%s_history' % info),
    url(r'^(.+)/delete/$',
        wrap(self.delete_view),
        name='%s_%s_delete' % info),
    url(r'^(.+)/$',
            wrap(self.change_view),
            name='%s_%s_change' % info),
    )

从上面的url映射来看,已经包含了增加记录,删除记录,修改记录等操作,当然上面也包含了 在 上文 中我做的一些url映射的修改。

看过上面的url映射后,心里就会有数,知道整个Admin系统大致有多少个页面,各个页面的作用是什么。

然后,我们可以通过url映射知道处理这个url的函数,例如,处理/add/的self.add_view函数。 通过阅读self.add_view函数的代码,除了相比于我们通常的逻辑更多的边界和条件处理外,其它的 也是相同的,例如数据库的操作,页面渲染所需要的数据准备等。

最后,我们看到的是页面渲染所需要的Template, change_form.html,查看对应的代码,则与通常的
Django 的Template并无二异。

其它的逻辑也相同。相比于我们自己的 Django 应用,这个逻辑中有更多的边界条件的处理和异常的处理 等,你可能会说它过于复杂,这也就引入了我们接下来要说明的第二个问题。

作为其它应用的通用基础应用

作为一个基础的应用,它就需要适应所有基于其上的其它应用的需要。 通用性通常意味着更多的复杂性,我们来看 Django 是如何有效且优美地处理这个问题的。

  1. 充分使用Python的内省,也即model的元数据,如app_label, module_name等,使得动态地构造url映射 和合适的显示成为可能
  2. admin = AdminSite()是一个全局的变量,来维护所有的注册应用的列表
  3. 两级的处理结构:admin site级和table级,分别由两个类来处理,并完成相应的url映射
  4. 可配置性:对于其上层的应用,都提供了完善的可override的属性,如list_display,list_filter等等
  5. 使用类而非 Django 默认推荐的函数作为view的处理,这样就提供了用户基于Admin系统建立自己的Admin系统的可能

总结

Admin系统可谓是 Django 最强大的功能了,它也大大方便了数据库驱动的应用的开发难度,为应用的数据输入,管理等 提供了一个稳定,可靠,方便的管理界面.

从 上文 和本文的介绍中,我们可以从中学习到一些 Django 常用的开发技巧:

  1. 基于类的view实现(当然在 Django 已经完全支持class view了, 参考 Class-based generic views )
  2. 分级的数据处理
  3. 可配置性
  4. 灵活性

会使用 Django 的Admin系统那可以让你的工作时间节省30%以上(基于数据库的应用),如果能够弄清楚 Django Admin 的实现原理并从中学习到可用于自己实际开发的经验与方法,则可运筹帷幄,谈笑风生地写代码了.