Archive for September, 2008

網頁的柵格系統設計

Saturday, September 27th, 2008

原文来自wowbox blog

柵格系統的形成

1692年,新登基的法國國王路易十四感到法國的印刷水平強差人意,因此命令成立一個管理印刷的皇家特別委員會。他們的首要任務是設計出科學的、合理的,重視功能性的新字體。委員會由數學家尼古拉斯加宗(NicolasJaugeon)擔任領導,他們以羅馬體為基礎,採用方格為設計依據,每個字體方格分為64個基本方各單位,每個方各單位再分成36個小格,這樣,一個印刷版面就有2304個小格組成,在這個嚴謹的幾何網格網絡中設計字體的形狀,版面的編排,試驗傳達功能的效能,這是是世界上最早對字體和版面進行科學實驗的活動,也是柵格系統最早的雛形。

柵格系統英文為「gridsystems」,也有人翻譯為「網格系統」,其實是一回事。不過從定義上說,柵格更為準確些,從維基百科查到柵格的定義為:柵格設計系統(又稱網格設計系統、標準尺寸系統、程序版面設計、瑞士平面設計風格、國際主義平面設計風格),是一種平面設計的方法與風格。運用固定的格子設計版面佈局,其風格工整簡潔,在二戰後大受歡迎,已成為今日出版物設計的主流風格之一。

網頁設計中的柵格系統

給網頁柵格系統下的定義為:以規則的網格陣列來指導和規範網頁中的版面佈局以及信息分佈。
網頁柵格系統是從平面柵格系統中發展而來。對於網頁設計來說,柵格系統的使用,不僅可以讓網頁的信息呈現更加美觀易讀,更具可用性。而且,對於前端開發來說,網頁將更加的靈活與規範。


柵格系統在現在的網頁設計中應用越來越多,從網絡上搜羅了一篇關於柵格系統應用的文章:30個最頂尖的基於柵格系統的博客網站設計

柵格系統的設計原理及應用

那麼如何設計一個柵格系統?接下來我們將通過實例,詳細的介紹一下網頁柵格系統的原理與應用:

在網頁設計中,我們把寬度為「W」的頁面分割成n個網格單元「a」,每個單元與單元之間的間隙設為「i」,此時我們把「a+i」定義「A」。他們之間的關係如下:
W =(a×n)+(n-1)i
由於a+i=A,
可得:(A×n) - i = W
這個公式表述了網頁的佈局與網頁「背後」的柵格系統之間的某種關係。我們拿yahoo作例,來看一下柵格系統的應用:

yahoo的網站頁面寬度為W=950px,每個區塊與區塊的間隔為i=10px;如果應用上面的公式,可以推出A=40px,既yahoo首頁橫向版式設計採用的柵格系統為:
(40×n)- 10 = W
下面我們列出當n等於不同數值時W變化的數值表格 :

從表格可以看出:yahoo首頁的佈局完全是按照柵格系統進行設計的,每個區塊的寬度對應的n值分別為:4,11,9。在這裡我們看到一個很有意思的事情:只要保證一個橫向維度的各個區塊的n值相加等於24,則即可保證頁面的寬度一定是950px。然而,950px的寬度也恰好就是當n=24的時候,W的寬度值。由此我們得出以下的應用模式:

在柵格系統中,設計師根據需要制定不同的版式或者劃分區塊,他們的依據將是上面的那張對應表進行設計。這樣,一個柵格系統的應用就從此開始了。我們看到,使用柵格系統的網頁設計,非常的有條理性;看上去也很舒服。最重要的是,它給整個網站的頁面結構定義了一個標準。對於視覺設計師來說,他們不用再為設計一個網站每個頁面都要想一個寬度或高度而煩惱了。對於前端開發工程師來說,頁面的佈局設計將完全是規範的和可重用的,這將大大節約了開發成本。對於內容編輯或廣告銷售來說,所有的廣告都是規則的,通用的,他們再也不用做出一套N張不同尺寸的廣告圖了……

當然只要你願意,我們可以衍生出任何一種柵格系統,只要改變A和i的值,這個根據網站的實際情況來制定。那麼如何選擇合適柵格系統,主要通過「構成要素與程序、限制與選擇、構成要素的比例、組合、虛空間與組合、四邊聯繫與軸的聯繫、三的法則、圓與構成、水平構成這些設計元素規劃,來實現比例和諧的平面設計」。比較深奧,我們在這裡就不詳細闡述了。

說了一堆柵格系統的優點。大家可能會問:難道柵格系統真的是完美的麼?答案是否定的:對於內容信息不確定導致高度不確定的頁面,在高度層面上就無法做到柵格了。當然,具體的情況還需具體的分析與解決,這就需要設計師們在實際的應用中不斷的總結經驗,不斷實踐了。

跌跌撞撞的持续集成之路:J2EE+Flex持续集成开发

Wednesday, September 24th, 2008

原文来自InfoQ

“天下事头绪纠缠,兴一利必也生一弊。”

一句话,道破了改进难点所在。最近在项目中围绕持续集成做改进的时候,对这一点感受颇深。跌跌撞撞的一路走来。我们的持续集成的过程已经变得有些“个性化”,反过头来看我们一路的变化,非常有意思。

从项目的技术架构说起,我们的项目是采用的J2EE+Flex的方式进行开发的。在我进入项目组的时候,一个比较健壮的持续集成环境已经搭好了。工程分为两个,一个是Java后端的工程,一个是Flex前端的。我们的持续集成服务器是CC。整个开发工作是围绕着持续集成展开的。一周为一个迭代。

那个时候,我们采用的是比较标准的方式:

  • 后台采取TDD的方式开发。
  • 每次提交代码之前更新所有代码,然后运行所有测试用例,全部为绿色的时候才提交。
  • 前台Flex比较麻烦,所以采取了用功能测试覆盖单元测试的方式。用基于Ruby的FunFx写单元测试。工作方式与后台差不多,每次前台功能测试全部通过了才提交。
  • 持续集成的流程是每隔5分钟检测一边代码库,有更新就build。
  • build的流程是先编译后台,跑单元测试,单元测试通过了,再编译Flex,将swf和html以及后台的文件打成war包,部署到tomcat上去,跑功能测试。
  • build成功之后发布到特定的目录,形成发布列表。有war包供人下载。

那个时候,build一次大概是15分钟,因为Check In Gate环节是按照标准流程走的,所以build出错的几率也小。CC大多数时候是绿的。哪怕偶尔出问题红了,也很快被修正了。

随着项目的开发,代码规模越来越庞大。功能测试越来越慢,比起自动执行脚本那种速度,开发人员更乐意手动点两下,加之上面对工作进度的压力。更改了一些工作方式:

  • 后台的工作方式不变。
  • 前台,将功能测试脚本的工作交给几个测试人员编写。几个测试人员也坐在附近,基本可以看作团队成员(到后来编制也是我们团队的成员了)。 开发人员人肉测试一下保证没有问题了再提交。
  • 测试人员写脚本的流程是拿到上一次build成功的war包,在本地写脚本,本地测通过了再提交。
  • 持续集成服务器上功能测试不通过的时候,由测试人员提交BUG,开发人员修改。
  • 持续集成的流程依旧。

这样,从后来收集的数据看,工作效率是提升了,因为参照以前的统计,开发人员的工作一下减轻了1/3。以进度来衡量的速度自然很轻易就可以让上级满意了。

新的解决方案总会产生新的问题,测试人员在测试方面的专业性,使得他们写出来的脚本测的更细。功能测试的时间占耗,增长的更快了,短短半个月,就增长到了1个小时。每当出现问题,作出反应之后,要在1个多小时以后才能知道结果。而且,持续集成方面并没有做到,一旦出错,谁也不能提交代码这么严格。模块化的设计所带来的假想的安全感和进度的压力,使得开发人员修正问题的激励不高。于是修正问题的速度不如产生问题的速度快。持续集成服务器在那两个个礼拜里只有两头是绿的,周一早晨和周五下午。

最早,我们发觉,由开发人员重构造成的脚本失败占大多数,而测试人员每次拿到的上一个版本是没有错误的。所以会出现自动化脚本本地跑得过,服务器上跑不过的情况发生。于是我们修改了发布的逻辑,在后台单元测试通过、flash编译完成的情况下打的那个war包,复制一份,放到某特定目录指定为current build。供测试人员写测脚本使用。

过程改进之后,测试人员可以快速的修正脚本了,虽然对于开发人员重构造成测试人员工作的返工无疑是一种浪费,但是毕竟自动化的测试省了回归测试的不少时间,还是可以接受。

脚本的修正速度解决之后,工作似乎有了些起色,但很快,问题的本质就暴露了出来–build的时间太长了,修得速度还是跟不上问题产生的速度。尤其是中间缺少当build失败时强制阻止代码提交的环节。这之后依然是周一和周五两头绿,中间都是红的。于是,我们觉得问题还是出在build速度上。我们人工的将功能测试脚本分到四个suite里去,然后以多线程的方式运行。速度被提高了4倍。于是又消停了两天。

好景不长,多线程的测试似乎不太稳定。很多本地可以跑通的测试用例,到了服务器上就失败。险些一个礼拜都没有build出一个版本。最后不得不改回单线程。这时,build一次已经占到了100分钟。第一期的产品Backlog还没有完成1/3。

持续集成走到这里已经进入一个困境,有必要做一些更深一步的改进。经过多次讨论,归纳出了几套方案:

  • 分冒烟测试和alltest两套测试用例集是我们当中呼声最高的一种方案,当我的代码提交之后在跑完所有单元测试和基本的冒烟测试之后就发布beta版,由测试人员接到beta版,进行更细致的自动化测试并带一些人肉测试。但是反对的声音认为,不跑完全部的测试用例就失去了持续集成的意义。而且会更降低开发人员修正Bug的积极性。于是作为修正,支持的声音则提出,在Check-InGate处把关,恢复每个人提交代码之前跑测试用例的实践。可这明显会给开发人员带来更大的工作负担,估计以此时的进度压力,开发人员的安全感肯定会大幅下降。很可能会推行不下去。
  • 另一个方案是从细节处调优,把WEB应用部署到另外一台机器上去,或许就会稳定一些了。但是反对的声音认为,以测试用例的这个增长速度,他早晚会不稳定的,而且可能撑不过两周。作为修正,想考虑分布式,但是我们所有人的知识储备中,并没有一个人清楚CC有没有分布式能力。所以想的是购买Cruise,但是价格的障碍就摆在眼前了,在项目前景还不是很明朗的情况下,估计很难申请到资金,但也不是不可能,只要我们敢于冒这个风险。
  • 第三,便是更为高级的分支式开发,将版本库划分一下分支,以分支来搭配持续集成,以分支合并来触发自动构建。这样做,开发的过程就更加有板有眼,粒度可以划分的更细。可是分支的划分,一时想不清楚。但是假设想清楚了,似乎这也使得我们的工作流程更加复杂了,做如此之大的改变,风险有多大?效果有多大?成本有多大?到底是值不值得?一时也想不清楚。

方案有了,听起来都很有道理,也都有问题。该如何做出决策,是个问题。现在大家众说纷纭,都有理,就变得难以抉择。而且到现在了是不能随便尝试的,这种尝试也是一种风险,一旦出问题造成的成本上升都会加大我们身上的压力。

迷茫之下到AgileChina上跟大家讨论了一下。非常高兴的收集到了几方面的建议:

Jeff Xiong觉得可以将测试分级,并将build分为两个环节,一个跑基本的用例,一个跑全部的用例。这跟我们的第一套方案的思路吻合。但是这种行为是不是失去了持续集成的意义呢?他也不是很确定,他说:

我也不确定……不过,不全面的持续集成至少比不能用的持续集成要好。哪怕一个quick build(只?)能抓到80%(70%?60%?50%?)的defect,如果它只要很少的时间就能跑一遍,似乎值得这样做。

不过同时就需要(可能是专门的)人来关注slow build的健康状况,不然broken functional tests可能被忽视并累积。

因为是在论坛上,互相之间的交流容易造成理解上的偏差,我便阐述了一下我的理解:

嗯。。。也就是说分一个fast build和一个slow build然后有专人关注slow build。

那我的理解,这个fast build应该是反映了后台的健康和前台与后台的基本集成的健康。主要用来完成保障集成的角色。

而slow build则是反映了从用户接口来看的软件的健康状况,定期回归,防止发生过的错误再次发生。

这样逻辑上看着很清晰,但是两者之间的同步。。。会不会有什么问题呢?

Jeff Xiong认可了我的理解,并提出了更进一步的解释和建议:

slow build实际上是运行完整的回归测试套件。当然理想的情况是slowbuild基本上不出错,因为*逻辑*用单元测试都覆盖到了,功能测试只是在描述*表现形式*。那么因为slowbuild基本上不出错,就没有价值每次都去运行它,让它在后台慢慢的跑着,过一段时间(半天或者两小时)去关注它一下,没问题就好,偶尔出了问题就马上解决并且加上对应的单元测试。这样你既节约了时间又不会严重降低对质量的保障力度。

实际上的情况可能比较难这样理想,但是和所有好的环境一样,这个环境不是说一下子规划好就万事大吉的。你可能大概的分一分,然后不断的维护,在两组build之间交换测试案例,一些覆盖到大量功能的、经常出错的案例也许要换到fastbuild的冒烟套件里面,一些看起来永远不会出错的案例也许可以换到slow build去。一直琢磨这个事,它才会变得越来越好。

(题外话:最近我觉得稍微大一点的项目应该有比较专注的build master,developers往往并不是特别认真的考虑build和CI的持续改进。)

而胡凯则提出了一些基于CC采用分布式的解决思路:

CruiseControl是支持一个叫做Distribute的Contrib,它的Idea是:因为CruisControl支持叫做Composite的build方式,那么你可以起多个buildserver一起来build同一个项目,当且仅当所有的子build通过,整个build才算成功。

对于每个build server,你是在单线程测试,对于整个项目,你却是并发测试,因为有多个server同时在跑测试。

但是他也说到CC毕竟是开源的东西,配置起来十分麻烦,于是最后也提出了采用Cruise的方案^_^:

或者你也可以尝试一下ThoughtWorks新发布的Cruise, 基本的理念很相似, 都是把测试分布到不同的机器上执行。

在试用期内可以同时跑6个Agent.你可以在每个Agent上执行不同的Target比如

Agent1 : ant ft.suite.1
Agent2 : ant ft.suite.2
Agent3 : ant ft.suite.3

你可以拿来玩儿下,根Open Source的那个比起来,应该容易设置很多。

缺点是:

  • 你必须使用 hg或者svn 作为 SCM
  • 试用期过后,你只有2个免费的Agent

另外,糖醋鼻子还提到了分支式开发,大家围绕这个还展开了激烈的讨论,不过我考虑到分支式开发对我们的利可能要远小于弊,最终还是放弃了这个方案。

在吸取了两方面的建议之后,经过一番思考,决定开始两方面的准备,一方面,对测试用例的分级方面做了一些工作,重构了一部分用例的结构。另一方面我去调研分布式构建的实现手法。CC的配置果然非常麻烦,调研期间发现了有RemoteAnt这个东西,试了一下基本满足我们的需求,考虑到一个Agent不用做的太过重型,于是就采用了这个方法。在分布式的技术调研已经完成的情况下,测试分级要不要做成了一个问题,但考虑到目前需求只作了1/3,如果这个都抗不住要分级的话,后面的工作就没法做了,所以分级的事情,虽然做了,但是也暂缓实行。

现在实践已经采用,会不会产生新的问题呢?前文说过“兴一利必也生一弊”,这个事情是肯定的。那么问题就来了,既然弊端肯定会滋生,我们怎么知道作出的决策是正确的呢?

其实,倒回去看这一路走来的过程,除了一个可以运行的过程以外,还有一个很重要的收获,那就是:如何进行决策。而所谓决策,并不是在黑白分明的事情之间做出选择,而是在都有理的事情中做出选择。就像我们都知道做软件设计的时候,设计是没有好坏之分的,只有适不适应你的具体情况之别。所以当我们面临几套解决方案的时候,就好象面对几套设计方案一样,真的是很难选择。如何做?各自就有各自的思路了。像我们就吸收了敏捷开发的思想中所强调得不做过度设计。选择立杆见影的改进去做。同时,像前文所说,抱着拥抱变化的态度,相信兴一利必生一弊并不是坏事。相反,他可以从一定程度上,带领我们找到真正的问题。

作者简介:仝键,网名咖啡屋的鼠标,06年大学毕业,普通程序员,专注于Java、Flex方面的开发、Agile等软件开发方法论的学习。爱好参加社区活动。

Flex中的控制反转和依赖注入

Tuesday, September 23rd, 2008

原文来自ericfeminella.com

Within the vast catalog of Design Patterns available to softwaredevelopers today, one of the most important to consider when designingan enterprise class RIA is the Dependency Injection Pattern.

Dependency Injection, a term originally coined by Martin Fowler in his well known article Inversion of Control Containers and the Dependency Injection Pattern, is a more specific term for what is otherwise known as Inversion of Control or IoC.

Fowler’s assessment of Inversion of Control containers concludedthat the name itself - Inversion of Control - was too generic, thus asa result from his discussions with various IoC advocates they settledon the more specific term Dependency Injection, also known as DIfor short. The terms Inversion of Control (IoC) and DependencyInjection (DI) are commonly used interchangeably to describe the sameunderlying design principle of separating configuration fromimplementation.

There are three basic forms of Dependency Injection, which aregenerally referred to as type 1 IoC (Interface Injection), type 2 IoC(Setter Injection) and type 3 IoC (Constructor Injection). Beforediving into the specifics of how to implement the various forms of DI,I will first discuss what Dependency Injection is on a conceptual levelas well as what each specific form means. The examples outlined hereare in ActionScript 3, however it is important to keep in mind thatlike most Design Patterns Dependency Injection applies to any languagewhich supports an Object Oriented Model.

At the most basic level Dependency Injection can be explained as away of decoupling classes from their dependencies by injecting thedependencies into them rather than having the classes directlyreference specific implementations. A class which directly referencesother classes is coupled to those classes - these are the dependencies.However a class which does not reference any other classes wouldprobably not be very useful. At some point the dependencies need to bemade. Dependency Injection is a solution to how those dependencies aremade, and the manner by which they are provided.

For example, consider the following class which illustrates a typical example of a class’s dependency on another class:

public class ConfigurationManager
{

//defines the configuration to use

private var config:XMLConfiguration;

public function ConfigurationManager()

{

config = new XMLConfiguration();

}

public function getLogLevel() : String

{

return config.getConfig(“logLevel”);

}
}

From looking at the code above the dependencies are pretty obvious;the ConfigurationManager class is dependent on the XMLConfigurationclass. Now this type of dependency is quite typical so at this pointyou may be asking what is wrong with doing this?

The first problem is that the config property is defined as a concrete implementation:

private var config:XMLConfiguration

This violates a fundamental OO principle:

Program to interfaces, not implementations.

More importantly and perhaps pertinent to the topic at hand is thatit also isn’t very hard to imagine that at some point we may want toload a configuration from some other means, such as a properties file,a remote service and so on. In order to do so we would need to modifythe class, and from this we can deduce that the class does not scalevery well.

So we could begin improving our current implementation by simplyrefactoring the ConfigurationManager class to define the configproperty as an abstraction, say IConfiguration:

public interface IConfiguration
{

function getConfig(name:String) : *;
}

public class ConfigurationManager
{

//define the configuration as an abstraction

private var config:IConfiguration;

public function ConfigurationManager()

{

config = new XMLConfiguration();

}

public function getLogLevel() : Array

{

return config.getConfig(“logLevel”);

}
}

As you can see this is certainly a step in the right direction,however the underlying problem still remains; we are stillinstantiating an instance of XMLConfiguration directly in theConfigurationManager - and that is exactly what Dependency Injection isall about: providing a solution to the recurring problem of managingdependencies between classes, and how those dependencies are provided.

When implementing the Dependency Injection Pattern in an applicationyou do so by creating a context (configuration) which defines alldependencies in an application as well as an Assembler which isresponsible for assembling the mappings and associations betweenobjects and their dependencies. This is done by utilizing anycombination of the three forms of DI; Interface Injection, SetterInjection and Constructor Injection. Below is a brief description ofeach form:

Interface Injection
Interface Injection is the process by which all dependencies areinjected into an object via an interface. For example, theConfigurationManager example above could implement an interface whichdefines the operations needed to inject the appropriate Configurationimplementation.

Setter Injection
Setter injection as you may have guessed is the process of injectingdependencies via public setters; both explicit or implicit. UsingSetter Injection the ConfigurationManager could provide public settersfrom which an Assembler could inject the appropriate Configurationimplementation.

Constructor Injection
Again as you may have guessed Constructor Injection is the process ofinjecting dependencies via arguments in the class constructor. UsingConstructor Injection the concrete Configuration could just as easilybe injected.

Both Constructor and Setter Injection are by far the preferred formsof Dependency Injection. Interface Injection has some major drawbacksas it somewhat leads to convoluted code since multiple additionalinterfaces need to be defined and implemented. The fact that “special”types need to be created and implemented in order to facilitate DIusing Interface Injection greatly limits the potential for its use.

There are numerous frameworks for various platforms which provideout of the box Dependency Injection implementations for all three formsof DI. All of these frameworks handle the wiring necessary for easilyimplementing Dependency Injection in an application, the most notablebeing the Spring Frameworkfor Java/J2EE. There are also quite a few DI solutions for Flex andActionScript applications as well. Optionally you could choose to rollyour own however I would first suggest investigating some of theframeworks which are currently available as they more than likelyprovide what you need. The Prana Framework by Christophe Herreman is a good choice as it is one of the most prevalent DI solution available at the moment for Flex.

Using the ConfgurationManager example from above I have provided a basic exampleapplication which demonstrates how to implement Dependency Injectionutilizing the Prana framework. The example application uses constructorinjection to provide a concrete Configuration to theConfigurationManager, however I encourage you to experiment with theother mechanisms of injection as well. The example is intentionallykept very simple in that it is only intended to convey the basicconcepts of DI and how to use it in Flex with Prana, from this youshould have a good understanding of how to implement DI in a largercontext.

养成编程的好习惯:AS3声明变量时的一个小提示

Monday, September 22nd, 2008

private function myFun1():void

{

var myArr1:Array = new Array(”1″,”2″,”3″,”4″,”5″);

var myArr2:Array = new Array(”6″,”7″,”8″,”9″,”10″);

for each(var str:String in myArr1)

{

trace(str);

}

for each(var str:String in myArr2)

{

trace(str);

}

}
注意上面的两个变量,如果这样写的话,编译器会发出警告:”3596:变量重复定义”。
所以,我们这样写:

private function myFun2():void

{

var myArr1:Array = new Array(”1″,”2″,”3″,”4″,”5″);

var myArr2:Array = new Array(”6″,”7″,”8″,”9″,”10″);

var str:String = “”;

for each(str in myArr1)

{

trace(str);

}

for each(str in myArr2)

{

trace(str);

}

}
可以在Flex SDK源文件中看到很多这样的写法:
例如:Grid.as中的measure()方法:
override protected function measure():void

{

// 1. Determine the number of grid columns,

// taking into account rowSpan and colSpan

var numGridRows:int = 0;

var numGridColumns:int = 0;

var columnOccupiedUntilRow:Array = [];

var gridRow:GridRow;

var gridItem:GridItem;

var i:int;

var j:int;

var colIndex:int;

var rowList:Array = []; // GridRows

for (var index:int = 0; index < numChildren; index++)

{

gridRow = GridRow(getChildAt(index));

if (gridRow.includeInLayout)

{

rowList.push(gridRow);

numGridRows++;

}

}

for (i = 0; i < numGridRows; i++)

{

colIndex = 0;

gridRow = rowList[i];

// Cache the number of children as a property on the gridRow

gridRow.numGridItems = gridRow.numChildren;

………

Optics Express——光学快递

Thursday, September 18th, 2008

http://www.opticsexpress.org/
Optics Express 是由美国光学协会发布, Sydney大学的Martijn de Sterke编辑的电子版光学期刊。
更多信息可以看这里http://www.opticsexpress.org/journal/oe/about.cfm