Zhang Jiuan’ Notes

帮助思考与限制思维(框架)

如果您是第一次读我的文章,你可能想订阅到我的 RSS feed. 多谢您的光顾!帮助思维与限制思维(框架)
If you enjoyed this post, make sure you subscribe to my RSS feed!

callback(回调)与hook(钩子)机制(四)

     本篇文章笔者将继续讨论callback机制的具体应用。通过上一篇的简单阐述,读者可以了解到通过对callback的深入理解和应用,可以使程序框架具有很好的可扩展性,这个意义是普遍的。比如组件的事件模型,ajax的回调,服务器框架回调模型等等。这里只略举几个示例以帮助大家对其具体应用的理解。
    服务处理框架:比如我们使用php对外提供服务,且不论apache、lighttpd内部回调设计的精妙,只从php框架角度来看回调的简略应用吧。
框架处理程序:
#callback_func.inc.php
#回调配置文件
final class callback_func
{
    static $ARR_PRE_GET_INPUT_CALLBACK = null;
    static $ARR_AFTER_GET_INPUT_CALLBACK = null;
    static $ARR_PRE_DO_WORK_CALLBACK = null;
    static $ARR_AFTER_DO_WORK_CALLBACK = null;
}
#end conf file
#frame.inc.php
function _main()
{
    foreach (callback_func::$ARR_PRE_GET_INPUT_CALLBACK as $func_name) {
        if (func_exists($func_name)) {
             call_user_func($func_name);
            //or require_once ($func_name);
        }
    }
     get_input();
   
    foreach (callback_func::$ARR_AFTER_GET_INPUT_CALLBACK as $func_name) {
        if (func_exists($func_name)) {
             call_user_func($func_name);
            //or require_once ($func_name);
        }
    }
    ……..
}
_main();
//end of [...]

callback(回调)与hook(钩子)机制(三)

    在前面两篇文章这中,我们讨论了callback机制的特点和本质,通过这些讨论可以使我们对callback有了本质性的认识。在本篇下面的讨论中,笔者将通过前面论述的本质的理解来进行示例性的讨论。
    wordpress的应用:大概对于wordpress的爱好者而言,都知道它的可扩展性框架,比如模块的分割、插件的管理等等。其中内部有has_sidebar(), has_posts(), has_plugin()等函数,然后通过while (has_posts: the_posts)来实现了框架本身的可扩展性。我们仔细理解一下的话,这本质就是callback的一个应用。
    它通过对一个具体的点的控制,当该点有一个事件的时候,回调用户自已的代码。从这个角脚再来理解wordpress的话,对wordpress设计的理解就会有另一番景象。对于文章的控制,它将用户代码镶嵌在系统框架的内部,使用户可以方便理解在操纵哪一块,样式又是如何对外显示的,层次清晰,模块明朗。
    另外wordpress许多插件也亦如此,比如插件的安装和可扩展性等,对于每篇日志的统计和评论等,无不蕴含着这些回调的思想。对于右边的sidebar同样利用了这种思想,比如有了sidebar用户可以添加一些广告,添加分类等,这些都是 wordpress先驱者的精华所在。
    因此,如果考虑框架的可扩展性,那么callback思想是你所必须考虑的。
If you enjoyed this post, make sure you subscribe to my RSS feed!

callback(回调)与hook(钩子)机制(二)

    在上一篇文章说明了callback机志的本质,从这个层面上理解,hook和callback一点区别都没有。它描述的是现实世界中一个普遍存在的场景,就是一些事情让别人帮着做的情形。
    那么什么时候叫callback,什么时候叫hook更加贴切呢?虽没有明确的区分点,但本人还是简单根据个人的经验说明一下哪种情形用哪个词好吧。
    如果在某一件事,到某一阶段的时候,需要帮助我们做一些附加的事的时候,我们最好使用hook命名。
    第二:当另一事物有其自身的工作要作,但可以帮助你做一些附加的事的时候,也使用hook命名。
    当一个事物单纯为了这种机制而设计的,我们一般使用callback去命名。
    实际上,二者没有什么严格的区分点。当然如果你喜欢,统一叫成callback也没什么错误。只不过勾子更偏重于勾子,它局限于callback机制的串接调用;而callback则是整体的实现的一种这样回调的机制。这是一些细节的差异,需要仔细深入的体会。但本质是相同的。
If you enjoyed this post, make sure you subscribe to my RSS feed!

callback(回调)与hook(钩子)机制

    许多接触了一些程序的朋友,可能对这两个词不再默生了,这些朋友可能大概知道callback是什么,也大概知道HOOK可以用来做些什么。但是如果向他们深究一些深层次的东西,却难以得到一个更清晰的答案了。对许多事物的理解我总是认为是这样的,越是了解其本质,答案也就越简单,当然认识也就越深刻,最终不会再拘泥于个别应用上的答案了。为了找到这个答案,让我们逐步进行探索。
    callback起初可以形象地这么理解,一个BUTTON存在这里,我们可以add一个action,那么如果这个button被触发的时候会调用这个action。呵呵,这样似乎有点绕了,不过还不难理解。理在再分析一下机制。button像一个事件监听器,也是一个事件源;而action则是一个动作。下面以一些简单的代码加以表示。
class button
{
    int width;
    int height;
    int color;
    int style;
    Action *paction; //动作列表
    ….
    function event() { //事件触发
    ….
    }
    function addAction(Action) {
    ….
    }
}
class Action {
    functon actionPerform(Event e) {
    ….
   }
}
    到这里这个机制相对就比较明朗了。但是总觉得button描述还不够清晰,因为事件源和监听器还没有分开。button是一个事件源这没有什么异意,但它是监听器有些牵强了。于是下面的代码产生了。
    class ActionListener {
           Action *paction;//动作列表
        functoin action(Event e) {
           ….
       }
}
class button{
    ActionListener *pactionlistener;
    …..
}
    这样无论逻辑上,还是层次上都更加清晰了。好了,通过这个小示例,我们基本上面以了解事件小模型了。对回调也有了感性的认识。
    现在让我们做进一步的分晰,抛开button具体,分析其本质。实际做的只是这样一件事:事件源包含有一个或多个事件监听器,一个事件监听器又有一个多个事件(事件就是用户添加的了,想多少就多少:-)。当事件发生的时候,监听器会挨个调用它内部的每个事件。
    实际上上述的这些东东就已经触及callback的本质了,但还不明了。那么怎么样可以用更形象的方式去说明这种机制呢?我们做如下试验:
    A:我是一个手表修理工,您有什么要做的可以找我。
    B:我的手表坏了,你帮我修一下吧,明来我来取。
    [...]

五子棋

    偶然的机会,使自已喜欢上了下五子棋。原来自已是很喜欢下象棋的,但是因为时间和工作,慢慢的把象棋给丢弃了,很少下,虽然还略知一二,但水平大不如以前。后来看到别人下五子棋,当初觉得五子棋太过简单了吧,五个下连着就赢,技巧性太低。但随着对五棋的不断深入,五子棋还是很有研究头的,里面确实蕴藏着很深的道理。
    五子棋分禁手和非禁手。当然,自已对禁手下棋了解的很少,只是略通五禁手。据说黑石可以计算出黑棋必胜,但自已还远没有达到那个高度吧。
    五子棋有充三、嵌三、充四、嵌四等很多技巧,可归结到最后就是一个争先了。死首是没有出路的,想在五子棋博弈中获取胜利只有一条,那就是冲。充的技巧很多,要看全局的每一个子力,当然,还要注意对方的任何一个局势的变化。比如充的不能是对方的一个活脚,可不然,先手棋很容易丢掉。第二,充的方向要对自已有利,特别是执黑棋的时候。第三,棋子如果容易形成梅花状,那么对自已是很有利的,因为这个时候选技是很多的,另外会有很多充四的机会,如果利用的好的话,可以一举获得胜利。
    另外,五子棋蕴藏着阴阳的道理。横竖为阳,斜为阴;充为阳,嵌为阴。注意到这些点的时候,你就很容易理解阴阳结合的道理的。阳很容易被对方看到,也容易防守,但却明了有力。阴则相反,很容易被对手乎略,但这里面技巧很深奥。阴阳结合,则力与技并进。
If you enjoyed this post, make sure you subscribe to my RSS feed!

写高质量的代码

    在前面我写过一篇文章:土坯和钢铁,其含意就在于告诫自已和朋友尽其力往钢铁的方向去发展。做人做事是如此,就算是平时的程序生涯也亦如此。我想可能许多人和我一样,酷看编程吧,可能说的好听点别人称我们是工程师;可仔细想想,或许叫程序员更合适吧:)当然,程序员不只是一味写程序,别人说要怎么样去实现,我们就怎么样去做,这可能就真的成了机器的。程序员也是人,也有感情,也需要思考。可能正是这些过程,才真的使一个程序员蜕变成工程师的过程吧。
    代码的质量的高低不仅仅在于代码,更重要的东西应该代码背后的思考。当然,这并不是说代码本身不重要,如果没有过硬的代码书写基本功,那么天才的设计,到了他的手里也只能算是一张普通的草稿纸。话说回来,如果只是强调为了代码而代码,那么很难在质量上达到一个新的高度。
    高质量的代码不是说出来的,也不是喊出来的,它是一天一天的思考,一天一天的严格要求锻炼出来的。当在实现一个系统之前,首先要思考许多东西,比如后台设计,后台支持,前台实现,扩展,效率等等因素,而不是一味根据经验照搬照抄。
    每个做出来的系统成品只是一个外在的物,这些是繁杂多样的,可对我们的自身影响,这些看起来很复杂多样,但是对系统程序的理解,一些深层次的“真”的东西实际很少。假以万物以休真谛。
If you enjoyed this post, make sure you subscribe to my RSS feed!

数据库大小表问题

    最近比较忙,因此有时候有些想写文章的想法,但只是在blog里面做了个标记,并没有实际的写出来。今天就要过节了,不防忙里偷闲一下,顺便把这些天的欠债给补上吧:)首先从数据库大小表来补吧。
背景:数据库大小表即一个系统后台数据库的实现有多种。对于有许多属性的一个事物,我们可以使用一张表来表示,当然也可以使用多张表进行分类进行表示。对于是使用一张表进行实现还是使用多张表来进行实现,这对于很多做后台开发的人员来讲是一个争论不体的问题了。下面我们将讨论各自的优缺点。
大表:大表实现有其自身的优点,那就是对于后台服务来讲,IO是一个主要的瓶颈。我们知道,程序和后台数据库交互是需要付出这方面的代价的,那么这就意味着和数据库多交互一次,那么就必须付出相应的IO代价。但对于大表来讲,一次可以将尽可能多的数据一次提取出来,然后对数据做分析整合,这样就可以尽可能少的和后台数据库交互,那么自然它的实现结果是IO代价小。这样系统从性能上来讲是比较好的。但其不是没有缺点,因为尽可能多的数据放在一张表里面,那么必然造成的结果就是逻辑不太清晰,不容易理解,不容易维护,特别是对于不是一对一关系的数据就更难了。
小表:我们从别一个方面看问题,可能就会有另一番景象。我们实现一个系统的时候,我们首先考虑的是系统的部署和结构,很自然下一步想到的这些结构如何由后台数据库来支撑。那么很自然我们会把一系统的某一块的东西使用一张表来支撑,这样逻辑十分清晰;而且从扩展方面,我们可以在这张表内部考虑很多可扩展的点,这是大表很难做到的。因此,这种思想最终的结果也是很明显的,那就是后台会有很多张相应的表,各个子系统会单独有自已的数据库读写和维护。那么这种实现,必然的代价就是数据库读取次数多,IO压力大。
跳出大小表的争论:现在我们想,是否可以跳出大小表的争论点来讨论大小表问题呢?究竟是大表好还是小表好,我们自已的系统的实现究竟是采用大表还是小表呢?笔者认为还是要看应用,需求决定一切。没有不变的技术,只有无限变化的应用。在我们考虑是采用大表还是小表的时候,我们应该考虑它们各自给我们可以带来哪些好处,然后做一些比较和权衡,最终得出自已需要的结论。比如,我们做的系统对象是一家企业,该企业使用此系统来管理内部员工数据,那么现在我们很清楚IO代价不是什么问题,因此甚至我们可以说大表给我们带来的优点是可以乎略的。但是采用小表就不一样了,因为它逻辑清晰,可能之后还会添加不同的考核属性,可扩展性是很重要的。但是如果我们做的应用如果是一个网部的线上服务,比如GOOGLE, BAIDU, SOHU(呵呵,口气大了些)这时必须要考此IO读取代价了,因为每一次请求,我们都需要对其服务,对于第小时千万次服务请求来讲,后台IO的压力也是很繁重的,如果一次请求可以减少一次IO读取的话,那么总的结省代价也是很显而易见的。
    下面以一个具体示例讨论一下大小表:有一家网游公司存在这样一种手机应用,它为线上游戏提供手机验证码服务、声音验证服务。手机可提供多种子服务,手机验证码服务和声音验证是手机服务的一些子服务。每个手机服务又可绑字多个游戏业务,比如游戏充点,造建筑等。现在逻辑结构也就比较清晰了。
对于上面的应用,我们应该怎么来设计和实现呢?思路是多种多样的。
方案一:大表方案。一个用户有唯一的id,因此数据库可以将该用户相关的所有数据存储在一张表里面的一条记录。如下:
t_mobile_service (
F_user_id UNSIGNED INT PRIMARY KEY, /* 用户id*/
F_mobile_service_flag TYNY INT UNSGINED DEFAULT 1, /* 用户是否开能手机服务,只有开能,才可能有子服务 1: 未开通 2:开通 */
F_mobile_token_service_flag TYNY INT UNSIGNED DEFAULT 1, /* 用户是否开通验证验服务 1:未开通 2:开通*/
F_mobile_sound_flag TYNY INT UNSGINED DETAULT 1, /*用户是否开能声音验证功能服务1:未开通 2:开通*/
F_mobile_token_current VARCHAR(32), /*手机当前产生的验证码*/
F_mobile_sound_fingerprint BLOB, /*声音签名*/
F_mobile_token_create_time DATETIME, /*手机验证码产生时间*/
F_mobile_sound_fingerprint_settime DATETIME, /*声音签名设置时间*/
F_mobile_token_bind_service BIG INT UNSIGNED, /*验证码绑定服务*/
F_mobile_sound_bind_servide BIG INT UNSIGNED, /*声音签名绑定的服务*/
F_create_time [...]

接口统一与功能单一

这是今天要补的最后一篇了。这个类似于数据库大小表的问题,同样是一个很长时间以来大家的争论点。在设计的时候,究竟是想着设计一个接口是功能尽可能的单一好呢,它只做一个对应名子的事,最简单的事,这样好呢;还是设计一个接口,它尽可能的通用,可以完成可能的所有的应用好呢?这个争论了很多,但最终还是没有结果。
接口统一:即是接口规范后,所有的可能的该功能的调用,都采用这一种接口调用方式完成。好处自然很明显,那就是使用者不必记一大堆在何处应该调用哪个函数了,一个接口搞定。它有很鲜明的应用实例:printf。我们几乎可以使它打印出各种格式的数据,使用很方便。当然它也存在自身的缺点,那就是函数体实现巨复杂,需要使用一大堆的if语句和一大堆的switch语句来标明所需要的分支,因此很容易出错。
功能单一:功能单一就是一个函数做的事尽可能的简单,这样实现很简洁,基本上就是一个“筒子楼”,可以说想犯错都难:)但是缺点也是显而易见的。它可能会有一些重复代码,另外就是接口巨多,当代码堆到一定量的时候,使用就更加繁锁了。
但这两都是设计需要考量的点,不存在说哪个好哪个劣,主要是你有没有看到哪个好在哪的问题了。
thx
张久安
If you enjoyed this post, make sure you subscribe to my RSS feed!

代码复用、接口复用(C++精髓)

笔者还在学校的时候理解C++最终精髓是两点:代码复用、接口复用。到现在隔的时间挺长了,今天不防把它记录下来,希望不会引起各位高手的嘲笑吧。
C++的特点:封装、继承、和多态。现在我们分别从这些所谓的C++的特点上来讨论它的本质吧。
封装:封装的含意就是对一个空间下的成员变量进行保护,这可能也是class和struct的唯一区别了吧。当然不是说struct不能保护,只是一个默认值而已。但C就没有这些支持吗?答案是肯定的。比如在一个文件的全变量声明成static,那么这个全局变量就是对该文件内部是可见的,对外部是隐藏的。因此从这一点上来看,封装不能算C++特有的东西。只不过是文件和声明的struct内部而已。好,下面对继承再进行分析。
继承:继承就是子类具体父类可继承的方法的属性,那么它本质做了哪些事呢?实际说白了很简单,只是复用了你类的代码,甚至我们可以认为是父类可继承的代码原样照抄一遍。这能算C++特有的吗?C封装的那么多的lib库不同样可以完成这些事吗?因此继承我认为同样不是C++的特性。好了,被人们传颂的C++三大属性在不知觉间被我砍了俩了,可能要被许多C++爱好者猛批一把了。但是下面我还要把第三个所谓的它的特点给否认掉:)
多态:多态是什么呢?即一个抽象事物对于不同的具体事物可以有不同的动作和处理事件,这就是多态。那么多态究竟做了哪些事呢?难道C就没有这样的特性了吗?实际上如果对C有一定深度理解的话,那么如下就不难理解了。声明一个函数指针,放在一个struct内部,在不同时刻对其赋不同的函数指针值,这样就可调用不同函数,这是不是叫多态呢?从本质上讲,这就是所谓的C++的多态,而且也是这么实现的。哈哈,到这儿可以看到神奇的C++竟再也没有可被提及的自已的特点了。
是不是C++真的就没有所谓的特点和特质了呢?答案是当然存在,而且文章题目也很醒目:代码复用、接口复用。现在我们给其抛开那些好听的继承名子,以复用取而代之。复用C库即是,但C++则采用了另外一种复用形式,重要的不是于表象,而在于对事物的抽象理解的复用。如果从这个角度去再解C++的复用,那么你才能真正去理解C++的一点点内涵吧。它可以将宏观世界的物可以很形象的用语言的形式对外再现。把是什么,归属这种性质以符号的形式进行加工进而实现复用。这便是C++的第一个精髓点吧。因此看你对继承这个词的理解程度了,不要人云亦云。
下面再进行接口复用进行更进一步的论述。刚才论讨了C++的继承即是代码复用的特性,下面将说明多态就是接口复用的性质。的确,在C里面使用指针完成可以做所谓C++的所有这些特性。实际C++的胜过C的点并不在于所谓的多态,仍旧在于对编程思想的理解。比如使用C++可以帮助理解这样的事物,同一类动物,对于不同的具体实际,可能做出不同的动作,使用C++可以更加简单形象的描述而已。因此多从更高层次上去理解C++,就可以帮助我们理解具体事物的特性在哪。接口复用即同一个接口,对于不同的使用者可以使用同样的接口调用方式可以得到不同的调用结果,这样方便了代码和系统的统一。
因此,如果读到这儿,所有事情就都很清晰了。所谓的封装、继承、多态要完全抛开了,而应该从另外一个更高的层次去理解它。当你理解到这个层次,C、C++只不过是一个工具,功能没有什么差别。就不再会有C++实现的系统使用C难以实现的困惑了。
笔者愚见。
多谢
张久安
If you enjoyed this post, make sure you subscribe to my RSS feed!

返回顶部