博客首页|TW首页| 同事录|业界社区
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机制的几个 核心问题,对于后续的工作和学习都是很有益处的.

每日的生活大致分为三个部分即: 锻炼, 阅读和工作。

锻炼我也想分为两部分, 一部分是室外的锻炼,一个是室内的锻炼。 前者的最佳时间 也许不是早上 但是早晨通常是比较容易坚持的,所以锻炼的时间我还是选择在 早餐前的时间。 室内的是在休息前的锻炼,如做几十个仰卧起坐。这样即使不能减肥, 应该也能保持体重。

阅读,无论是阅读电子书,或者纸质书,通常都能让人平静下心来,不至为社会的浮躁 而累,看书时内心通常是比较安静地,能够与作者一起徜徉在思想,故事,行走之中, 聆听作者反思或者研究后的著述,总之这个过程都是你在静静地聆听,正如我们日常的 聆听一样,聆听总是能够让人安静,反思与更加有耐心的。 我的阅读时间分为两段,一段是技术类的阅读, 这想这个大致可放在晚上晚餐后的时间, 另一段是非技术类的阅读,如文学,哲学,管理等方面,这个大致可在午餐后的时间完成。 两段阅读时间正好作为上,下午两段工作时间的调剂。

再就是工作, 每日大多数的时间会隶属于这一类, 而编程相对比较耗费精力,容易累, 所以需要调剂好整个工作计划和执行过程。 事无巨细, 总是要按照优先级来确定执行的 顺序,而今天要做的事情,通常在昨天已经确定,至少80%已经确定。 数量不要太多, 以能够完整地完成为最重要的目标,这样的循环也能让人更加自信于这样的工作流程。

整个的工作计划与执行,我将会采用ticket的方式来管理,基本的流程如下:

  1. 第一天晚上根据当天ticket的完成情况来制定第二天的工作计划与安排
  2. 第二根据上面的工作计划来执行
  3. 第二天晚上对当天的工作进行总结
  4. 第二天晚上临下班前制定出第三天的工作计划与安排,回到1

总之,需要达到的一个目标是, 能够比较好地完成既定的工作计划与安排, 不把工作 带回家(将生活和工作能够有效区分), 不至于太过受到社会浮躁的影响,能够较好地 保持一个平静的心。

具体的安排为:

关于上图的注解:

  1. 时间结点都是事件结束的时间
  2. 上述的生活和工作安排是针对工作日
  3. 休息日的流程大致会与这个图表相同,只是会在工作量和工作内容的安排上有所调整

检验

无法检验结果的计划根本就称不上计划,那么我的这个工作计划如何来检验呢? 我想主要 还是通过两个方面:

  1. 自检(这是最主要的检验方式,确认自己每日计划的执行情况)
  2. 他检(希望老婆也能够监督)
2011-04-03

缘由

订阅了 Matt Cutts 的博客,在多次的阅读后,喜欢上了他的 30天挑战系列,例如他自己过去的几个挑战就包括:


Matt Cutts 是Google的webspam组的头,他的一些反思和每月的挑战系列 对于我们来说是很有借鉴意义的,至少这种方式对我们也是具有很好的参考价值.

所以从本月起,我也决定效仿 Matt Cutts 的方法,也做出自己的30日挑战,并且 在30日后,对这30日的完成情况做出结果的更新.

个人觉得,这种方法至少有下面几种益处:

  1. 提高自己的意志力(个人觉得很多时候,一个人的成败就取决于意志力是否坚定)
  2. 涉猎更多有意思的事物
  3. 让生活更加有序,精彩,提高生活质量
  4. 反思(这样才不至于迷失自己,直到无法回头)

4月的30日挑战

本次是我的第一次挑战,所以希望能够顺利和完整的得以实现,经过老婆的建议后,我的 挑战是:

阅读完林达的《近距离看美国之四-如彗星划过夜空》

P.S

最终在4月20日完成了阅读,相关的总结可见 这里.

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-17

当你Google这个 标题 时, 你能看到很多有价值的讨论和信息,这里也不再赘述和引用. 本文旨在从一个用户的角度来分析两者的差别和低劣势.

作为 StackOverflow 的重度用户,主要是因为它其上能够提供给我很多有价值的信息, 当遇到一个需要了解方法,思路的问题,我也总是首选尝试在其中搜索来获得答案.

Quora 用得稍微晚点,目前还没有拿到注册码,不过这也不防碍我从中查看一些问题答案的快乐.

因为后来基于 StackOverflow 成熟的模型,它的创始人又组建了类似的其它领域的QA站点,从而 组成了一个QA的基于领域的矩阵 StackExchange ,所以在下文中不会区分 StackExchange 与 StackOverflow , 都是指StackExchange 的整个QA矩阵.

Quora 的官方说明如下(http://www.quora.com/about):

Quora is a continually improving collection of questions and answers created, edited,
and organized by everyone who uses it. The most important thing is to have each question
page become the best possible resource for someone who wants to know about the question.

One way you can think of it is as a cache for the research that people do looking things up
on the web and asking other people. Eventually, when you see a link to a question page on Quora,
your feeling should be: "Oh, great! That's going to have all the information I want about that."
It's also a place where new stuff--that no one has written about yet--can get pulled onto the web.

StackExchange 的官方说明如下(http://stackexchange.com/about)

Stack Exchange is a fast-growing network of 47 question and answer sites on diverse topics
from software programming to cooking to photography and gaming.
We are an expert knowledge exchange: a place where physics researchers can ask each other
about quantum entanglement, computer programmers can ask about JavaScript date formats,
and photographers can share knowledge about taking great pictures in the snow.

基于过去一段时间的使用,我想说说我个人的一些体会和看法.

比较点 StackExchange Quora
问题 通常是相关领域的从业人员的提问, 问题的暴光率和回答率取决于问题本身的 质量,传播等.在整个问题的生命周期里, 管理员会作一些质量的控制. 相关从业人员
问题质量控制 质量参差不齐,流量大了后,问题水的比较 多.问题的质量控制主要由相关的管理员 来完成.编辑会重新编辑问题等. 质量相对较高,因为是邀请式的加入(可以从http://quora.com/hackernews注册),增加了门 槛同时也增加了问题的质量,如何能够控制住 开放后时的问题质量,值得关注.
答案质量控制 答案的质量由所有的注册人员来投票和提问 者来完成, 得票高的人的答案会在前,被提问 者接纳的答案会出现在第一个. 关系和投票两大因素决定答案的排次.关系是指 与这个问题相关的社会关系,而投票是由用户来 投票完成.
信任机制 提问和回答都会得到reputation,高的 reputation会在社区中有更高的地位, 相应其问题和答案有更好的响应. 与答案相关的社会关系是非常重要的信任来源, 现场的作用对于事实的复现具有最高的价值. 类似于一个真实的环境,每个提问者和回答者 都具有与现实相同的社会关系,所以他自身在现 实中的信任体系也同样会带来社区中来.
问题内容 不欢迎主观性问题,鼓励客观性问题,也就是 有确定答案的问题. 相反,更多的是主观性的,发散性的问题,有更多 的发挥空间
获取信息 通常有明确的目标,搜索直达,偶尔会看投票 最多的问题 通常可能你心中并没有带着问题去,而是有期待 地查看,来拓展自己的知识

当然, StackExchange 依旧是我获取相关信息的重要途径,特别是程序相关的,但是一个程序员的成长 需要了解的不只是所谓的硬知识(程序技术相关),很大一部分也是软知识(非程序),所以我最近也在常常访问 Quora ,从中获知了很多的信息如一些技术公司的典故(技术选型的依据,为什么myspace的员工持股不值一文等).

我的想法是 Quora 建立起的基于社会关系的问答具有更大的市场和更高的包含度,也就是长远来看, Quora 的问题和答案能够包含 StackExchange ,而基于自身机制的限制 StackExchange 则很难形成 Quora 这样的 良性发展氛围.

当然技术也是二者发展的一个重要因素,在流量巨大时,如何能够减少质量低,重复问题,如何能够提供更加准确的搜索和答案, 如何能够提供更好的用户体验,这些都是左右二者发展的巨大考验.

基于问答的应用也是有很多可以畅想的,如类似于 linkedin 的职业类网站, 当然 StackExchange 已经开始尝试.

P.S. 经过确认后,Quora可以从http://quora.com/hackernews注册。

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 的实现原理并从中学习到可用于自己实际开发的经验与方法,则可运筹帷幄,谈笑风生地写代码了.

2011-03-08

最近看了小说 笑傲江湖 和电视剧 你是我的兄弟 ,喜欢之余,发现了一个共有的特征, 也就是 无论主题是否凝重,喜剧元素是很重要的亮点. 例如,在 笑傲江湖 中, 贯穿整个小说的桃谷六仙,或者是 你是我的兄弟 中的蒙山三兄弟.在阅读或者观看 过程中,这些喜剧元素总是能够平和我的心情,或者总是在心中萦绕着,有一种期待, 甚至一种乐见的愿望.或许这就是所谓的”喜闻乐见”.

扩展开来,还有很多的文学作品或者电影电视也都有类似的手法,例如西游记中的八戒, 水浒中的李逵、鲁智深,三国演义中的张飞等等. 总是有个别的喜剧人物在那儿帮衬着 主题,让主题更加丰满,或者让爱众更多的情感选择与缓冲.

不仅是中国,国外作品也类似,如哈利波特中的 Rubeus Hagrid 等,他们不时插科打诨,或者 不影响主题地让你发笑,让你能够缓和自己的心情使得主题更容易接受.

所以说喜剧元素的效果和作用也是明显的,但是,个人认为它又不能越权去影响整个主题的 阐释,所以必须要注意几点:

  1. 适可而止
  2. 不能越权
  3. 合适的时机(受众感到压抑,或者很累,或者凝重等)

当然,一部分文学作品也可归作是娱乐产业,电影和电视那也自不用说.当我们放大到娱乐产业时, 社交游戏自然也隶属其中,社交游戏的创意是否也能够从这个有效的方式中学习些经验呢?这想 这是一个很好的出发点,在未来的几个产品中,我会考虑将这个理念尝试溶入到产品的设计中去.