Updates from 三月, 2012 Toggle Comment Threads | 键盘快捷键

  • Roger 8:12 pm on March 2, 2012 固定链接 | 回复
    Tags: ,   

    阅读源代码的一些体会 

    最近一个月的时间,差不多都在看Android 4.0系统和它的WebKit实现的代码。面对庞大的代码库,复杂的系统,从一开始毫无头绪到后来慢慢厘清主要的流程和摸清系统的结构。体会到有一些方法对阅读源代码是非常有帮助的。

    Screenshot_2012-03-02-20-02-38

    需要可以编译,运行和调试

    如果只是光看代码,没办法编译,运行和调试,那对系统的理解总是只能停留在概念性的表面而无法深入。最好是能够下断点和单步跟踪,如果很难实现单步跟踪,起码也要可以输出日志,dump出一些运行时的主要对象结构和对象状态,或者直接在渲染结果上面绘制额外的示意标记。

    只有通过观察系统运行时的行为,才能深入的理解系统的运作。包括:

    • 理解系统的线程模型,线程运行的顺序,和它们之间的通讯;
    • 理解主要对象之间的关系,它们各自承担的职责;
    • 理解系统的主要流程,一些重要方法的调用链;
    • 理解系统的时间消耗分布,哪些部分对性能影响最大;
    • 等等…

    所以在开始看代码之前,先建立好一个方便调试运行的环境是十分重要的。所谓工欲善其事,必先利其器。

     

    找到那些(单元)测试或者例程

    一个好的开源项目,通常都会附带一些单元测试程序或者演示例程。它们对我们对于整个系统的研究来说,简直是无价之宝。

    • 一来它们短小精悍,比较容易理解;
    • 二来它们聚焦特定的模块,通过观察它们的运行和输出,更容易帮助我们了解一个模块的职责;
    • 并且我们很容易修改它的代码,进行各种各样的试验,进一步帮助我们了解模块运行时的行为,印证我们各种猜想;

     

    绘制类结构图

    面对庞大的类型系统和它们之间复杂的关系,一幅类结构图就像是一份地图,随时可以告诉你当前所处的位置,周围的环境,来去的方向等等。使你不会觉得放眼望去,白茫茫一片,不知何去何从。

    一开始总是比较困难的,但是更要坚持一边看代码一边绘制类图,随着你的类图越来越完善,你对系统全景也会越来越有感觉。

    使用像EA这样的工具绘制正式的UML图固然很好(还可以通过逆向工程帮助建立初始的UML模式),使用像XMind这样的工具绘制脑图也是一个不错的选择。

     

    记笔记

    不要太相信自己的记忆力,对于那些你仅仅是在阅读,而没有自己亲自实现的东西,你的大脑是很容易就把它遗忘。一瞬间的明悟,最好马上把它记下来;一些无法确认的猜想,也没有关系,记下来然后打个问号即可。即使一开始十分的散乱,后续也可以花些时间整理的更有条理,而且通过整理笔记,可以进一步加深自己的记忆和理解。使用Evernote或者类似软件是一个很好的选择,你随时都可以阅读和进一步完善它们。

     

    最后,如果你将来需要把研究的结果写成文档或者制作演讲稿,之前记录的笔记和绘制的类图就是最好的素材。

     

     

    Advertisements
     
    • 红心地瓜 1:36 下午 on 三月 22, 2012 固定链接 | 回复

      +1
      聚焦很重要,将面聚焦成线的这个过程最辛苦。

    • 匿名 4:05 下午 on 五月 4, 2012 固定链接 | 回复

      LZ分享的很好,最近也在做Chrome在IOS平台的移植工作,另一方面也在看Android4.0中webkit的源码,有机会多多交流

  • Roger 10:26 pm on September 16, 2011 固定链接 | 回复
    Tags: ,   

    新工作一年回顾 

    不知不觉到新公司已经满一年了,回想一年前,自己的确做出了一个重大的决定,而从目前来看,这个决定还是相当正确的,当前的工作比较符合自己的期望,未来职业的发展也有更多空间。

     

    回顾这一年,自己在技术方面提高较多的方面包括:

    1. 对GUI框架设计和实现的理解 —— 在UC的这一年里面,大部分工作都跟GUI框架的设计和实现有关。刚进公司的第一件比较重要的工作就是为J2ME平台设计一个类似Android这样的现代GUI框架,虽然最后没有用于实际的产品,但是自己还是从中积累了很多经验,不但在设计和实现的过程中参考了Qt,Android,LWUIT等框架,同时也加入了很多特有的设计。在转到UCMobile后,大部分工作也是跟GUI框架里面的重要模块有关,包括动画框架,样式引擎,布局引擎等等。
    2. 对系统设计的理解 —— 因为有很多机会进行底层和中间层的系统和模块设计,感觉自己对系统设计的理解又提高到了一个新的层次。尤其是设计时需要考虑很多额外的约束,包括需要考虑J2ME平台本身的性能和内存耗费的限制,或者需要考虑兼容UCMobile原来的GUI框架等等,在这么多林林总总的约束下进行设计,迫使自己不得不绞尽脑汁如何在诸多因素中取得一个较好的平衡,从而极大锻炼了逻辑推理的能力,能够对一些设计中的细微之处把握的更精准,对模块之间依赖关系的掌控也更加得心应手。
    3. 对浏览器相关技术的掌握 —— 虽然自己一直在做浏览器产品的开发,但实际的工作跟浏览器核心的部分交集较少,不过也对相关的技术,包括HTTP协议,HTML规范,CSS规范,客户端JavaScript编程等多多少少有所了解,业余的时间也研究过一下WebKit的源码,特别是CSS的部分,因为实际的工作中实现过一个CSS解析器,所以这一部分了解比较深入。

    未来需要提高的部分:

    1. GUI框架仍然是一个飞速发展的领域,特别是在以下两方面 —— 声明式UI(使用脚本描述界面,使用JavaScript处理事件),和GPU硬件加速。自己在这个领域仍然有很多需要学习的东西。
    2. 浏览器核心,毋庸置疑,这部分技术价值含量高,而且发展十分迅猛,即使在实际工作中缺少机会,自己仍然需要更多利用业余时间进行学习和实践。
     
  • Roger 3:33 pm on September 14, 2011 固定链接 | 回复
    Tags:   

    两个系统的故事 

    前一段时间,我写了一篇文章“碎片化系统”,表达了自己对参与的两个十分混乱的系统的一些感想。刚好前几天看了“架构之美 Beatiful Architecture”一书,其中的第二章“两个系统的故事:现代软件神话 ”,也表达了同样的想法,而且说的更加清晰明了。这一章的部分内容摘要如下(InfoQ出了一本迷你电子书,可以在这里下载):

     


    两个系统的故事:现代软件神话

    Pete Goodliffe

     

    软件系统就像一座由建筑和后面的路构成的城市—由公路和旅馆构成的错综复杂的网络。在繁忙的城市里发生着许多事情,控制流不断产生,它们的生命在城市中交织在一起,然后死亡。丰富的数据积聚在一起、存储起来,然后销毁。有各式各样的建筑:有的高大美丽,有的低矮实用,还有的坍塌破损。随着数据围绕着它们流动,形成了交通堵塞和追尾、高峰时段和道路维护。软件之城的品质直接与其中包含多少城市规划有关。

     

    某些软件系统很幸运,创建时由有经验的架构师进行了深思熟虑的设计,在构建时体现出了优雅和平衡,有很好的地图,便于导航。另一些软件系统就没有这么幸运,基本上是一些通过偶然聚集的代码渐渐形成的,交通基础设施不足,建筑单调而平凡,置身于其中时会完全迷失,找不着路。你的代码愿意待在怎样的“城市”中?你愿意构建哪一种城市?

     

    在本章中,我将讲述这样两个软件城市的故事。这是真实的故事,就像所有好的故事一样,这个故事最终是有教育意义的。人们说经验是伟大的老师,但最好是别人的经验,如果你能从这些项目的错误和成功之中学习,你(和你的软件)可能会避免很多的痛苦。本章中的这两个系统特别有趣,因为它们有很大不同,尽管从表面上看非常相似:

          •   它们具有相似的规模(大约500 000行代码)。

          •   它们都是“嵌入式”消费音频设备。

          •   每种软件的生态系统都是成熟的,已经经历了许多的产品版本。

          •   两种解决方案都是基于Linux 的。

          •   编码采用C++语言。

          •   它们都是由“有经验的”程序员开发的(在某些情况下,他们本应知道得更多)。

          •   程序员本身就是架构师。

     

    在这个故事中,人名都已改变,目的是保护那些无辜的人(和有罪的人)。

    (More …)

     
  • Roger 9:55 am on August 25, 2011 固定链接 | 回复  

    API 设计 – 避免使用Boolean参数 

    这篇文章描述了在API设计中使用Boolean类型作为参数基本上都是错误的,仅仅除了少量例外:
    http://ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html?utm_source=rss&utm_medium=rss&utm_campaign=hall-of-api-shame-boolean-trap

    比如,如果不看API文档,你能猜出下面的调用是什么意思吗?

    • widget.repaint(false);
    • widget.destroy(false);
    实际上,上面的两个boolean参数的含义都是是否立即执行(刷新和销毁)。
    我自己还记得以前在使用MFC的时候,MFC有一个update函数,用于同步UI组件上显示的值和它绑定的变量。它接收一个Boolean参数控制同步的方向,你可以修改变量后调用update去更新跟它绑定的UI组件上的显示,或者修改UI组件的值后调用update去更新它绑定的变量的值,但是到现在我都记不清到底true对应的是哪个方向,false对应的又是哪个方向,实际编程中也常常用错。
    下面是一些少数可以考虑使用Boolean类型做为参数的例子,主要用于Boolean类型的属性设置:
    • dialogBox.setVisible(true)
    • component.setEnabled(true)
    不过也要考虑是否有双重否定的副作用,比如到底是setEnabled(true)还是setDisabled(false),需要选择哪个更符合第一直觉的(上面的例子setEnabled比setDisabled更好),总之大部分情况下使用Boolean类型作为参数都是一个API的设计错误,只有少量含义非常清晰并且无明显歧义下才可以使用。
     
  • Roger 10:02 am on June 3, 2011 固定链接 | 回复  

    厌恶什么,才能成就什么 

    Linus Torvalds不仅因为Linux著称,也因为他的大嘴巴和Strong Opinions而闻名于世。几年前在Google总部一场介绍Git的演讲上,1个小时左右的演讲里面,差不多有一半时间是花在抨击非分布式版本管理系统如CVS,SVN上面,其中不乏像Stupid,Pointless这样非常Strong的字眼。虽然这些抨击多少有些开玩笑的意味,不过其中的不屑和厌恶的态度还是十分的明显。

     

    Linus因为自己这样的个性引起不少争议,如果不是顶着Linux之父的巨大的光环,估计早就被口水淹没了。但是否他这样的个性才是他本人巨大的技术成就的根源?从多年的工作经历获得感受,使我相信,精益求精,追求完美的道路是十分漫长而痛苦的,而对丑陋,不完美事物的不屑,厌恶甚至憎恨能够产生巨大的动力,这样才能推动自身在这漫长而痛苦的道路上继续前进。

     

    简而言之:

    • 如果要成为一个高效率的开发者,就必须对低效率的工作方式深恶痛绝,从而推动自己不断提高工作效率;
    • 如果要持续写出简洁,清晰,高效的代码,就必须对凌乱,难以理解,效率低下的代码感到深深的厌恶,才能推动自己更深入地理解语言设计的精髓,掌握更多编码的实践技巧,从而不断改进自己代码的质量;
    • 如果要能够做出简单,优雅,有着良好扩展性的系统设计,就必须要对那些杂乱无章,靠拼凑堆砌而成的系统痛恨不已,才能推动自己不断学习和模仿那些有着良好设计的系统,从而不断重构和改进自己的设计
     
  • Roger 10:35 am on May 21, 2011 固定链接 | 回复
    Tags:   

    碎片化系统 

    进入新公司也差不多超过半年的时间了,在这段时间中参与了两个不同产品的开发,这两个产品的系统设计取向虽然截然不同,但是都呈现出类似的状况,就是系统的杂乱无章和碎片化。

    中间层

    如果把应用所处的平台和基于的应用程序框架(如Android Runtime)当作系统的底层,应用所包括的各项功能实现的集合当作系统的上层,通常我们还需要一个所谓的中间层,这个中间层可以起到的作用包括:

    • 为跨平台的应用构造一个隔离层,自身可以适配不同的平台;
    • 对平台本身的一些模块进行封装和增强,解决系统兼容性问题,增加应用所需的功能;
    • 从应用所包括的各项功能中抽取通用的逻辑,为功能的实现提供强有力的支撑;
    • 为各个功能模块提供各种管理,数据传输和通讯服务;

    碎片化

    一般而言,如果是不太复杂的应用,本身又基于一个比较成熟的平台和应用程序框架,对构造一个中间层的需求并不大,大部分情况下都可以忽略掉。但是很不幸,上述的这两个产品由于自身的复杂性,或者由于所处的平台,或者由于技术方案的选择,都要求必须要有一个强有力的中间层作为支撑,而缺少这样一个设计良好的中间层,导致了上层的各个功能模块都不得不为本来应该是功能无关的通用逻辑提供自身的实现,而这些实现通常缺乏严谨的设计,并且大多为了赶工仓促而就。

    总之,这使得整个系统充斥着大量这样的小模块,它们相似但又不完全相同,设计蹩脚,实现粗糙,很多明显残留从别的地方把代码拷贝过来然后进行修改的痕迹。各个功能模块之间缺少一个统一的中间协调者,不得不自己直接跟其它模块交互,系统的相互依赖性和耦合度轻易就泛滥到无法收拾的地方。

    这样一个系统就像是由一堆碎片拼凑堆砌而成,为了整个系统不至于散架,还需要淋上一打胶水把这些碎片紧紧地粘合在一起。随着功能的增加,系统的复杂度的增长,这个系统越来越变成像是一个吞噬工程师血汗的怪兽。每一次修改,常常都波及大半个系统,每一个功能的增加,都像是要在这一堆粘的密密实实的碎片中撕开一道裂缝,然后再把自己硬塞进去。每一个身处其中的工程师都苦不堪言,咒骂着参与制造这样的怪兽的人,然而他们自己也将在不久的将来成为后续维护这个系统的工程师咒骂的对象。

    成因

    考究这样系统的成因,可以大概归为:

    • 缺少一个有经验的系统设计师在开始作出一个完善的全局设计,并在后续的演化中维护这个设计的基本架构;
    • 产品的开发以功能实现为导向,工程师只追求尽快实现功能,而无法做更长远的思考;
    • 没有分配工程师专职于系统中间层的构建,所有工程师都忙于于功能的实现和bug的修正;
    • 一些较为有经验的工程师会试图抽取一些可复用的代码,但是因为这些代码还是直接跟具体的运行上下文绑定在一起,并且API也不完备,缺少良好的文档,最终还是仅仅停留在只能供自己使用的程度;
     
    • chdz 2:40 下午 on 五月 21, 2011 固定链接 | 回复

      呵呵,确实如此。 都是边学边做,对知识半懂非懂赶上架。对手机知识的通晓的人少又少。

  • Roger 8:44 pm on May 2, 2011 固定链接
    Tags: , , Java 2D   

    把大象放入冰箱与设计抽象性的思考 

    Q:如何把大象放入冰箱

    A:把冰箱打开,然后把大象放进去,再把冰箱关上

    当然这是一个脑筋急转弯的笑话,但是对于系统设计者的启示是,如果你设计了一个足够抽象的接口,理论上这个接口可以代表近似于无限的实现,简单的说越抽象的接口,它可以衍生出来的实现就越多。

    如果我们把上面的问题当作一个软件功能上的需求并对它进行建模,我们可能会推导出下面的UML模型:

    Elephant

    在上图中,接口Containable和Placeable表达了两个非常抽象的概念,一个代表可以放置Placeable的容器,一个代表可以放入Containable的物件,而类IceBox和Elephant 分别实现了这两个接口。

    如果用代码来表示把大象放入冰箱的过程,可能的代码如下:

     1: void placeElephantInIceBox(IceBox box, Elephant e)
     2: {
     3: 
     4:     box.open();
     5:     box.place(e);
     6:     box.close();
     7: }

    上面的思考主要是受最近在看的Java 2D Graphics一书的启发,Java 2D是Java提供的2D Graphics Library,它是一个全功能,桌面操作系统级别的2D绘图库,为了能够在实现复杂功能的同时保有一个尽可能简单的编程接口,并且允许用户继续对它进行扩充,Java 2D提供了一个高抽象度的模型,很多接口的抽象度基本就跟上面的Containable和Placeable类似。

    比如说Stroke接口代表了笔画,但是它实际上是将一个输入的Shape转换成根据该笔画描绘出来的轮廓的Shape(Java 2D只处理图形填充,所谓描绘图形的轮廓实际上是先生成图形的轮廓图形,然后再对该轮廓图形进行填充)。

    下面的代码基本等同于直接调用Graphics2D中的draw(graphics.draw(shape);):

     1: void drawShape(Graphics2D graphics, Shape shape)
     2: {
     3:     Stroke stroke = graphics.getStroke();
     4:     Shape strokedOutline = stroke.createStrokedShape(shape);
     5:     graphics.fill(strokedOutline);
     6: }

    其中createStrokedShape就是Stroke接口的唯一的方法。

     
c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
回复
e
编辑
o
Show/Hide comments
t
返回顶部
l
Go to login
h
Show/Hide help
shift + esc
取消