|
单元测试随着agile的流行已经家喻户晓了,这正反映了软件的一个本质特征:软件是Man-Made的,而人是不可靠的。软件出错的高频率必然导致控制
间隔的缩短。我最早是在编写matlab程序的时候独立的发现了单元测试的作用。因为matlab是弱类型的,横纵矢量也不区分,很容易犯错误,我就为每
一个matlab函数编写了测试脚本,约定了命名规范为xxx_test.m, 在测试的时候通过
can_assert来做出判断而不是输出变量内容进行人工检查。
每次作了修改的时候,运行一下can_test_all.m就自动搜索测试脚本并运行一遍。 后来xp出现了,
我也第一次听说了单元测试这回事,看来英雄所见略同吧 ^_^
单元测试可以看作是对编译器的补充。编译器只能进行语法检查(形式),而单元测试可以进行语义检查(内容),它其实维护了程序的一个语义框架。如果单元测
试程序编写出来了,即使一行业务实现代码也没编写,我们手中也已经拥有了一笔宝贵的财富,因为程序的语义已经在某种意义下确定下来了。重构一般是在维护单
元测试不变的情况下进行的。即我们发现在维护语义不变量的情况下,系统具有相当的调整余地。
坚持单元测试的另一个好处是它倾向于良好分离的模块,因为高内聚低耦合的模块才更容易进行测试。为了测试,我们会在不知不觉中在对象职责的分离上投注更多
的心力。在witrix平台中,虽然基于EasyMock提供了mock支持,在实际中却很少使用,因为模块功能一般很独立,不需要复杂的mock对象,
而对于一般的对象,eclipse有代码生成功能,可以非常轻易的生成简单的测试对象。
虽然JUnit非常流行,我们的单元测试也是基于JUnit进行的,但是我们还是进行了一个简单的封装,将JUnit框架的特定要求与具体测试代码剥离开来。具体的,测试类从
test.UnitTest继承,而不是从JUnit的TestCase继承。使用Debug.check()来做判断,而不是JUnit的assertEquals等。
class MyTest extends UnitTest{
public MyTest(String caseName){ super(caseName); }
public void testMy(){
MyObject my = new MyObject();
Object value = myObject.myFunc();
Debug.check(value.equals("aa"));
// 可以同时提供一个出错消息
Debug.check(value.equals("aa"),"myFunc return invalid : "+value);
}
public static void main(String[] args){
// 不需要IDE或者其他外部的支持就可以直接调用测试代码,将会自动输出运行时间等
UnitTest.exec(new MyTest("testMy"));
}
}
关系数据库提供的是集合存储模型, query(fields, condition) ==> list of records, 可以从条件集合映射到记录集合。
当condition退化为单一的key, 而fields采用默认值的时候,我们就退化到Map语义, 从key对象映射到value对象,而不是从集合映射到集合。
很
多时候我们只需要这种简单Map语义的存储模型,例如用户偏好设置的存储。在这种受限的模型下我们也可以更直接的实现cache支持。如果我们希望在
Map的基础上稍微扩展一些集合操作的特性,可以通过key的结构扩展来实现。即规定key采用类似url格式的字符串,实现key空间的树形结构。在
witrix平台中,这种树形结构的映射关系通过IVarValueSet接口来实现。
interface IVarValueSet{
IVariant getVar(String name);
// 得到前缀为prefix的所有变量构成的子集合,注意这里自然退化的特点
IVarValueSet getSubSet(String prefix);
}
变量名的格式规定为 a.b.c 或者/a/b/c. 这种变量结构的组织和划分方式其实与JBoss项目中的TreeCache结构类似。
级列设计理论中我们谈到一般和特殊的关系, 但这是否指的是“相对抽象” 以及 “相对具体”之间的关系,
而“一般”到“特殊”和“特殊”到“一般”这两个方向是否指的是具化过程和抽象泛化过程?
我猜测有这种想法的人大概是受到软件设计中所谓抽象封装思想的影响. 很显然, 我并不是这样认为的. 一般性(普遍性)与抽象性是不同的概念.
在物理学中相对论是比Newton力学更加一般性的理论,但它和Newton力学一样都是关于我们这个世界的真实的理论,都是非常具体的。虽然我们有的时
候会说相对论更加抽象一些,这不过是暗示这个理论所描述的情形与我们的日常经验距离遥远而已,并不意味着它是某种只存在于概念空间的东西。实际上我很少谈
到抽象与泛化过程,这对于物理学而言并不是一个合适的命题.
有些人认为"service层, data object层,
dao层只是对程序职责的描述并不是实现,在实现中应该根据实际情况进行合并与取舍"。在我看来,
持有这种看法的人已经把自己的思想限定在了某一个复杂性层次上, 认为这些职责是天然的,必然的存在于程序中的. 但实际上,
我们肯定可以想见更加复杂的情形, 仅仅三层并不足以充分表达程序的结构, 而另一方面, 在极端简单的情形下,
例如只有一个数据库,只有CRUD操作, 此时根本就不存在这种职责.
一种所谓的职责从来就不曾存在过,我们自然也不应先把东西搞复杂起来,再合并取舍回去。
级列理论是分析学中常见的一个思维框架,我只是把它从我最熟悉的物理学中借用到软件设计领域而已,
它本身并不是我所创造的一种概念(创造是艰难的)。在某些领域,这一分析框架可以表现出完美的数学特性,如时频分析领域的小波分析(wavelet),
统计学习理论中的支持向量机(Support Vector Machine),
分子动力学中的BBGKY级列等等。在其他一些领域它的精确性可能要弱很多,但其思想内核是一致的.
级列理论的基本内容如下:
http://canonical.blogdriver.com/canonical/562888.html
首先, 级列理论需要定义一个最一般的普遍模型, 但这并不意味着对系统的一种过分的限制. 实际上,
我们所讨论的任何问题都有一个总体的框架限制, 它的作用只在于揭示出我们所研究的问题的基本要素并勾勒出一幅全景式的图像.
最一般的情况与我们的求解能力的距离可以是非常遥远的,例如真正实作的时候在物理中往往我们只是研究一阶或者二阶情况。而在计算机领域也是存在着最一般的
模型的, 那就是Turing Machine. 在一般情况下, 我们是能够建立一个足够普遍的模型以囊括绝大多数变化可能的,
虽然我们极有可能没有能力去求解这个模型.
对于系统的完备性, 物理学与数学的态度是有着深刻区别的.
在物理学中我们只需要在研究问题的范畴内维持概念的稳定性即可, 这往往意味很多简化与隐含条件,并不是一种终极的完备性. 而在另一方面,
数学的完备性并不能保证它描述现实世界的完备性. 最明显的, 从经典力学到狭义相对论, 再到广义相对论,
在每一个层次上都是存在着非常完美的数学描述的, 其数学空间都是完备的, 但是很显然, 即使是广义相对论, 它也不是对于物理世界的完备描述.
目前物理学界仍然在量子引力的黑暗中摸索. 所有这一切都不影响我们在建筑工程中以足够高的精度应用力学原理.
级列理论所描述的绝不是一种从一般到特殊的思想的应用, 而是同时包含着从一般到特殊和从特殊到一般两个方向. 在级列理论中,
我们首先研究的一般是最特殊的情况, 即最简单, 对称性最高的情况. 此时模型中特征元素个数很少, 而且界限分明, 很少交互或者发生关联.
我们得到简单模型的解之后, 就可以将其作为初始解去求解更高阶的模型. 这是从特殊到一般的一种进展. 而在另一方面,
我们可能以创造性的方式得到某个更高阶模型的解, 此时我们需要研究高阶模型与较低阶模型之间的关联, 考察当更高阶的模型退化到低阶模型的时候,
它们的解是如何自然的实现退化的. 虽然迭代这个名词在当前软件工程领域如日中天,
但我想很多开发人员却从未花上一刻时间去真正的体味这个概念本身的内涵, 而陷入了人云亦云的窘境. 从数学上说, 迭代过程的基本问题是收敛问题,
而且往往收敛过程中的控制策略要远比迭代初始值的选择更重要. 那么什么样的控制策略才是保持探索性但又倾向于收敛的呢? 连续性是最基本的要求.
参见 反问题的级列求解 http://canonical.blogdriver.com/canonical/974280.html.
级列理论最关键的部分其实是两个存在性: 一是复杂性级列的客观存在, 二是级列之间连续性的客观存在.
注意到级列理论所描述的只是存在性,在一般情况下,
我们并不能直接得到从低阶解推导出高阶解的构造方法。而复杂性的级列的存在意味着总存在超越我们当前认知范围的信息,
从低复杂性层次上升到高复杂性层次可能是需要非凡的创造力的,
决不是显然的。此时我们唯一的选择只能是从后验的角度去检验这种存在性。模型的可退化性正是连续性的一种自然推论。这在物理学中是一件不言而喻的事情也是
一种强制性的要求:狭义相对论可以在低速情况下退化为Newton力学,而广义相对论可以在引力均匀的情况下退化为狭义相对论。在软件领域,
似乎只是到了近几年,
可退化性才得到了一些重视,至今很多人对它的思想实质仍然没有充分的了解。退化是否是指系统中不同职责部分的合并但是这些职责依然存在? NO,
NO, NO. 精简机构的原因难道是为了裁减人手,增大人均工作量吗? 精简的原因首先在于人浮于事, 本身就没有那么多的职责,
却要生造出那么多的处理步骤来.
我们或者合并部门,或者干脆撤销部门,一些专门因为内部协调而生的部分更是要被毫不留情的裁减掉。在常见的软件开发中,
明明是非常简单的数据库访问操作, 偏偏要在service层, data object层, dao层都把同样的接口代码重复一遍,
美其名曰多层体系架构, 可以保持系统的灵活性. 可是谁需要这种灵活性, 它到底是设计的灵活性还是设计的脆弱性? 在简单的情况下, 选择meta
driven的方案,从描述文件直接驱动多个层次的运作往往能够极大的提高系统的稳定性和灵活性.
假设现在有一个网络应用,
我们首先考虑最简单的情况, 例如我们假设只存在一种通信协议:打电话. 我们可以通过电话通知另一方的接线员,
让他把信息记录下来再录入系统当中,以此维持系统的运转. 如果永远都只有一个固定的接线员X通过唯一的一部电话M来完成通信过程,
则系统中的多个概念就会发生简并: 通信将等价于打电话,等价于打电话给X, 等价于使用电话机M打电话给接线员X. 随着系统的复杂性逐渐增大,
系统的对称性出现破缺, 原本被认为同一的概念出现了微妙的不同, 并可能演变成差异巨大的两个分支. 假设现在多了另外一种通信协议:硬盘交换.
我们可以把数据拷贝到一块硬盘上, 然后携带到远处的机房, 在那里把数据导入系统, 以维持系统的运转.
如果我们的系统演化到了这个阶段是否意味着我们原先的软件模型已经彻底崩溃了? 并不是如此的, 在一个宏观的粒度上,
我们的系统所需要的可能只是一种端到端的通信手段, 只是现在通信这一更高层的抽象概念不再等同于更加细节化的具体通信手段(打电话)了.
如果原先系统设计中的各个主要模块之间只存在高层抽象之间的依赖, 则只需要增加一些数据导入导出模块,
我们的系统就可以平滑(连续)的接纳一种新的通信协议. 一种宏观的高层视图如果直接映射到某种实现, 则它所对应的就是一种最简单的系统模型.
系统的不断发展相当于是给这个最简单的模型不断补充细节, 使它不断的复杂化. 这种变化是有脉络可寻的, 很多时候是局部的, 轻微的. 当然,
持续累积下去, 也许有一天我们会突然发现系统已经面目全非了, 甚至原先的高层视图(简单模型)也无法维持了. 但是即使这样,
是否意味着我们原先的设计失败了呢? 答案仍然是否定的. 软件系统如同其他系统一样, 处在不断的演化状态当中, 但是什么叫做系统的演化?
很多人有一种错觉, 以为软件结构完全不变, 完全不需要源代码级别的修改, 只需要通过外部配置导入扩展对象的设计才是好设计,
才是成功的可扩展设计. 这是以一种静态的僵化态度来看待程序的演化, 没有理解演化的实质.
演化(evolution)首先是一种变化(variation). 按照级列设计理论, 当系统沿着复杂性的级列发生演化的时候,
因为不同复杂性层次之间存在着本质性差异, 我们不能奢望现在的简单设计能够一直包容越来越复杂的模型,
更加现实的态度是能否在未来的复杂的模型中为现在的简单设计找到位置. 我们不预言未来, 也无法保证我们现在的行为能够容纳将来的选择.
在适度重构的意义下实现部分重用已经是我们能够做到的最好程度了. 我们需要理解世界本身是在变迁的, 即使现在我们能够预测到未来,
现在就为其作准备也未必是适当的, 因为现在的最优不等价于将来的最优, 同样将来的最优也不等价于现在的最优.
在上文中的网络应用的例子中, 也许系统最终发展成为一个非常庞大的系统, 而我们最初设计的功能成为了该系统的一个子模块,
这不是完美的可扩展性吗? http://canonical.blogdriver.com/canonical/1002861.html
级列理论的思想是非常通俗的, 并没有丝毫神秘的地方.
复杂性的级列可以在空间中呈现出一种复杂性递增的形态,也可以沿着时间轴展开,构成发展的形态。在传统设计理论中也在频繁的处理着这些问题,
因而可以看到很多相近的思想, 但是我们也需要注意到一些细节性的不同.
传统的设计中也讲层次,但是多半说的是stratify而不是hierachical,是同一时刻可以看到的一种层次堆垒关系,而不是在不同复杂性层次上
才揭示出的级列关系. 传统设计中也讲高级抽象与低级抽象之间的关系,但没有提供级列理论中完整的视图, 也很少强调每个层次上元素之间的连续性。
关于级列设计理论在软件设计中的适用性, 我只想提一下witrix平台中的jsplet开发框架, 参见
从级列理论看MVC架构 http://canonical.blogdriver.com/canonical/579747.html
jsplet:对Model 2模式的批判 http://canonical.blogdriver.com/canonical/591479.html
在2002年左右, 我是先得到级列设计理论, 然后才根据其思想设计的jsplet框架. 在整个witrix平台的设计中,
级列设计理论也是重要的指导思想. 不过, 以我的经验来看, 一般人是很难理解他人的思想的, 即使是对别人的想法有些兴趣,
实际思考的东西与作者的原意往往也有着很大的差异. http://canonical.blogdriver.com/canonical/1014773.html
最后我还是强调一下一件最最基本的事情, 一件每个人都应该时刻牢记的事情: 不要孤立的看待问题, 而是寻求一种概念之间的连续性. http://canonical.blogdriver.com/canonical/1016684.html
近代数学和物理学的发端是从微积分的发现开始的,人类第一次系统化的将连续性的思想推向了极限,也开创了崭新的思维方式。记得高中自学微积分的时候我也花
了很多时间去思考连续性的问题,但是后来渐渐习以为常了。研究生的时候重新开始考虑连续性的问题,只是关注的方向是概念体系中的连续性。
all or
nothing是我们的思维中经常出现的一个误区。很多时候我们的讨论局域在一个封闭的既定的体系中,最后的结论也是在原地转圈圈。就像是“人性本善”与
"人性本恶"的争论一样,数千年无所结论。也许我们真正的问题应该是猪有善恶吗,猩猩有善恶吗,何谓小善,何谓大恶?
有些时候,某些简单的概念被其复杂的外表所遮蔽。例如提起网格(grid),
很多人的第一印象大概是高深,是一个宏大的体系架构。但是我说如果现在只允许你写下1000行代码,你打算为网格写些什么。我们在网格出现之前所作的工作
与网格之间有什么关系,将所有的概念逐个从网格这个体系中剥离出去之后,最后会剩下些什么。
我们需要对概念有一个连续性的理解,而不是将它们作为一个不可破的黑箱来看待。这是分析学的基本态度。
敏捷思想的流行使得很多人对可扩展设计产生了一种怀疑的态度。这有几方面的原因,一方面是J2EE平台本身提供的分布式机制等技术因素很容易诱导你定义不
必要的扩展需求,第二是基于目前的技术手段对于程序结构的分解仍然有着很大限制,具体的程序实现中往往会引入某种强制依赖,削弱了潜在的可扩展性,第三则
是设计者本身对于技术和业务的把握不够深入,在考虑设计的可扩展性时经常做出错误的判断。但是一个只满足当前需求的系统一般不是个好系统,也很难在多次迭
代生命周期后继续生存。XP(extreme
programming)强调简单化,其实质在于简单的东西可以在未来被重构(refactor),从而适应未知的变化,它本身并不排斥可扩展设计。
从基本的常识出发,我们都知道现在应该为将来做些事情,准备些资本。可扩展设计的价值观不应是现在解决将来的问题,而是寻求未来发展之后现在的解是否仍然
部分有效,是否仍然可以部分被继承。即我们考虑的不是将未来的解纳入到现在的体系中,而是考虑现在的解在未来的体系中的位置。不是在现在如何支持我们所预
想到的几种未来的扩展方式,而是无论未来如何变化,怎样才能保证现在工作的有效性。这里所关注的重点是现在而不是将来!面对演化我们所能采取的最好的策略
就是尽量有所积累,尽量不放弃我们的过去,而不是把宝押在对未来的准确预测上。一个厚重的设计往往在后期会因为预料的太多反而在遭遇未预料到的变化时不知
所措,结果造成系统整体架构的失效,必须做更多的工作打补丁来使得它勉强工作。象EJB这样distribution
ready的技术现在已经公认有过度设计之嫌,因为这些已经ready的特性一般并不会被应用但是我们却不得不为这些无用的特性付出代价。
可扩展设计所依赖的基本原则之一是IoC(Inversion of Control)。IoC是目前轻量级容器(lightweight
container)的核心设计思想,但其实它的应用远不止在轻量级容器这一领域。基于IoC设计,大量的知识(依赖)被剥离出业务对象本身,对象对于其
生存环境和应用场景的假设大大减弱,而我们的期望正在于无论未来的应用环境如何变化,只要提供必要的知识,业务对象就能工作。可以说,IoC是可扩展性的
一种基本要求。
可扩展设计所依赖的另一个原则是连续性(continuous),
这可比IoC要复杂和深刻的多了。如果说现代设计的核心观念是演化(evolution), 那么在我们的思想中演化到底有着什么样的图景?
至少需要一个方向加上一条连续的途径,evolution才能发生。在级列设计中,一个简单的系统架构需要能够scale
up,而一个复杂层次上的系统架构也需要能够以优雅的方式scale down。这种变化是自然的因为它们是连续的。
实际观测到的结果是系统内在结构的外在表现,而软件开发是从需求分析开始,经历系统分析,设计并实现的过程,即从用户需求逆推出软件的结构。这种根据外在
表现求解内部结构的模型的过程,在数学上称为反问题(inverse
problem)。关于反问题,一个众所周知的难点在于解的不适定性。因为不同的结构可以有类似的外在表现,因而反问题的解是不稳定的。在一个既定的情况
下,我们按照某种粗略的外在度量标准,从反问题的众多近似解中选择了一个。但是当所需的外在表现发生微小变化后,我们第一次选择出来的结构可能无法适应这
一微扰,而我们再次求解出来的结构可能与原先的结构有着巨大的差别。因而原先选择的解在结构上是不稳定的。在数学上,我们称之为奇异解(singular
solution)。在数学上,在求解反问题的时候为了避免选择到奇异解,经常采用的技术手段就是类似于级列理论的所谓镇定方法。即我们提出一系列的模
型,对它们进行一维参数化。当参数较大时相当于对原有模型的一种近似,原有模型的细节被淹没在正定泛函的大范围结构中,整体呈现出一种简单的结构,而当参
数越来越小时,原有模型的细节被逐渐识别出来,整体模型逐渐复杂化,最终参数为0时恢复到原始情况。常见的模拟退火算法(simulated
annealing)就属于这一策略族。通过模型的连续性,我们建立了一个复杂模型与一个简单模型(因而物理意义明确)之间的一条连续的纽带,沿着这条可
退化的途径,我们才有可能回避奇异解,保证复杂模型的物理有效性。
在软件设计中,我所提出的级列设计思想正是这样一种渐进演化的设计思想。我们极力维护模型的可退化性,保证复杂的模型不至于锁定在错误的角落中。而基于模型的连续性,我们对于未来的发展进行外推才有了一定的根据。
架构的可退化性(degragation)指的是架构的结构可以从元素比较丰富,层次比较多,比较复杂的情况退化到比较简单的情况,
而架构的无侵入性(non-invasive)指的是架构对于外部接入对象没有特殊的形式要求, 一般通过依赖注入(dependency
injection)向接入的外部对象推送信息. 这两个概念之间存在着紧密的关联, 但并不等同. 无侵入性可以看作是架构的一种局部可退化性,
例如一个业务对象在正常工作的时候需要是完整的EJB对象形态, 而在编写的时候退化到普通的java对象(POJO).
架构的可退化性是一个比无侵入性更加广泛的概念:一个架构对外可以是无侵入性的, 但是它的实现本身可能相当复杂, 是不能退化的.
例如在一般的web表现层设计中, 很多人都试图提供一个RPC层, 将Web请求解析后映射为对java对象方法的调用. 通过一系列的描述文件,
java对象本身可以完全不知道web层的存在, 因而这种设计在某种程度上可以看作是无侵入性的.
但是假如现在出现了性能问题,或者RPC层本身出现一些bug, 或者我们需要一些RPC层很难有效实现的映射规则,
web层设计应该允许我们越过RPC层, 很方便的直接处理request和response,
这意味着在我们的架构设计中需要把边界划在web接口上(需要在这里定义基本的交互规范),而不仅仅是对象接口上.如果一个架构设计强制规定了一个不可越
过的RPC层, 则意味着该架构在这一点上是不可退化的.
架构的可退化性是级列设计理论的一个自然推论,
它是对架构整体的要求, 需要同时考虑到架构本身实现的复杂性以及与外部接口的复杂性, 而不是仅仅考虑到对外部接入对象的复杂性的要求.
整个架构需要能够沿着复杂性级列scale down, 而不仅仅是scale up!
系统架构通俗的说起来就是系统的结构组织方式.原则上说, 架构只有好坏之分,而不存在有无的问题.
软件的体系架构可以直接体现为代码的类结构, 也可以表现为文档性的编码规范和全局约定等. 如果软件架构中能够抽象出一些稳定的元素,
那我们就可能得到一些所谓的框架代码. 一般业务架构是很难重用的, 目前常见的框架代码所描述的多半是与业务无关的技术架构.
良好的系统架构应该体现出应用本身的结构要求. 所谓各个为自己, 架构为大家. 只要各个局部符合规格,
应该由架构负责在合适的时刻按照合适的方式把它们组装在一些. 一个良好的架构中, 应该很少出现结构性的if语句,
不需要应用代码自己通过动态判断来定义某个特殊的触发时刻. 架构是一种规范, 当然也就是一种局限. 架构的可退化性是非常重要的,
否则一旦出现抽象泄露, 需要超出原有架构设计做出编码补充的时候, 往往无法将代码自然的融入原有的框架结构, 则整个框架出现大面积的失效情况.
而有的时候更糟糕的情况是一些关键性的资源处在原有技术架构的私有控制之中, 我们为了克服架构限制不得不采用各种trick来hack原有框架,
造成错误的累加和传播, 而补丁的补丁是最难维护的.
架构问题并不是一成不变的. 在一些情形下无关紧要的问题在另一种情形下可能会成为灾难性的架构问题.
例如在多层B/S架构下, 如果现在要求为每一个表增加一个对应的历史表, 并对其进行查看和维护操作. 为了最大限度的重用代码,
这要求我们的多层结构中的每一层都能够参数化, 这样我们才能用同样的代码处理不同的数据表. 如果我们的money很足, 小弟够多,
有足够的人月砸上去, 那么我们完全可以把业务表和历史表分开处理, 但如果反之,我们就会遇到一个典型的架构问题.
架构师未必有自己的框架, 因为设计不等价于创造,
架构师只要知道如何把系统中的各种元素按照可行的方式组装在一起就可以了. 但是一个架构设计是非常依赖于我们所能采用的技术手段的,
当现有各种可用的技术元素都无法满足我们的需求的时候, 某些架构师可能会选择创造一种技术元素. 当然, 创造是艰难的,
它所要求的甚至是不同的技能. Sun的Green项目创造了java语言, 从而开启了一个伟大的时代,
这绝对不会是大多数架构设计师的选择(有趣的是,Green项目本身失败了). EJB现在还有多少人在真正使用,
想想当年多少架构师在吹嘘这些东西. 他们对于技术的把握真的就那么幼稚吗? 架构设计并不是凭空出现的, 当时可选的东西就是如此,
而spring和hibernate这些都不属于架构设计本身的内容.它们是一种创造.
架构师未必是团队的领导者. 确实,他的工作类似于编剧, 负责执行的一般是导演.
事实上,一个建筑设计师是极少直接领导一个工程队的.架构师也未必比高级程序员要高明, 他们负责的是不同的内容.
至于产品的"商标及商标的相关元素"和"技术市场架构"等也不属于架构师的工作范畴, 他不能去抢产品经理的饭碗. 当然,在国内的现实情况下,
很多所谓的架构师所做的最重要的工作可能是公关工作, 向客户秀出所谓的理念, 与实际开发是不搭嘎的.
理论上说, 架构师可以不是编程的强者, 也可以不决定一些具体数据结构的选择,
但他不能不了解各种技术抉择潜在的影响. 这就如同一个建筑设计师可以不精通工程力学,但是他不能愚蠢到藐视重力, 设计出倒三角式的大厦.
与建筑不同的是, 在软件中我们所面临的不是一种"凝固的艺术", 我们无法以完全静态的方式理解代码,而必须在头脑中把它们运行起来.
架构师应该写下一些实际的代码, 以检验各个接口的可配合性并获得对于代码结构的直接感觉. 实际上, 按照现在软件业的成熟度,
一般我们无法实现建筑中建筑设计师与土木工程师的分工, 很多时候软件架构师都需要直接面对实现的细节. 如果组内缺乏非常强悍的coder,
有编程能力的架构师亲自操刀实现关键性代码的时候也是很多的.
架构师必须有经验, 但他所依赖的不能只是经验. 只要算一算架构师的年纪,
就会知道以他们在这个世界上的存在时间, 并不足以使得他们经历各种技术细节. 架构设计更多的是依赖我们对于系统结构原理的理解,
而经验可以让我们规避那些原理失效的地方(例如系统级bug). 君子非异能也, 善假于物也.
很多时候,我们更应该从有经验的朋友或者技术支持那里搜集技术细节, 以确保它们能够满足我们在架构上的原理性需求. Know
Why而不仅仅是Know How是非常重要的. 一个农民发明家也许可以得到某个巧妙的机械设计, 但是没有系统的掌握工程力学,
他们是无法去开发精密的导弹控制系统的.当然, 软件开发还处在非常原始的阶段, 掌握一些设计原理和设计模式多半也不过是五十步笑百步而已,
经验的地位是无可替代的.
架构师不是预言家. 在多变的业务环境中, 架构师的目标不应该是预测到所有的变化可能,
并把它们表达到系统架构中. 这个世界上不乏一些耗资数十亿,设计三四年,但最终每个谈到它的人都要说一句shit的产品开发项目.
架构设计所能做到的最好的程度是自然的标注出系统的结构边界,成功的delay各种技术抉择.
架构师不是超人, 他所考虑的东西也许要远一些, 所需要平衡的利益也许要多一些,
但是单独一个人是无法对整个产品或者项目的成败负责的. 如果ThoughtWorks的Martin Follower来处理国内的某些项目,
我估计他会死得很难看.架构师也是人, 也会犯错误,甚至是很低级的错误, 而每个人都会有一些独特的想法. 经历的多了, 你就会回归到终极的认识,
一切都只是浮云, 只有money才是硬道理.
现在MDA建模的宣传多集中于可视化的表现形式, 鼓吹通过平面图标的摆放来传达信息.
图形的方式是否一定比文本表现要优越呢? 图形的表现能力确实是要强于普通文本的.文本对于信息的组织方式基本上是一维的,
而平面图形本质上是二维的(如果考虑颜色因素, 平面图形可以说是2.5维的). 人的视觉对于图形有着天然的并行处理能力,
通过图形我们有可能更有效的获得信息. 但是程序中细节的关联可能是复杂的, 多维的, 二维图形同样难以直接描述这些关联,
而一维的文本对于所有维度的描述是对称的, 可能在描述多维关系时更加容易维持简单性和一致性. 当描述复杂的关联时,
我们不可避免的需要采取多层次封装的方式, 在图形界面上我们可能要通过多次点击才能到达某一细节层面, 这样反而不如纯文本方式更加"并行化":
在纯文本方式下, 我们在一个文件中能够同时看到所有高层次和低层次的信息, 并能够通过查找和翻页沿着一个固定的维度迅速定位到所需要的章节处.
受限于人类视野的大小, 我们所看到的图形不能过大也不能过小, 这样在一定程度上也限制了图形方式所能够传递的信息量.
有的时候图形上标注了过多的关联反而使我们更加迷惑. 所谓可执行的UML要真的运行起来, 需要大量的信息隐蔽在图形表象之下,
在我看来这是一个没有什么实际意义的概念.
|