架构师:一个架构师的一些观点(转)

A 从指针到引用
地址(Address)是现代计算机体系架构中的核心概念,它在程序设计语言上的体现就是C语言中的指针(Pointer)。在C语言中,所有的高级技巧都和指针这个概念相关。指针只是一个存放了一个地址的变量,但是C语言中提供了一个方便的间接访问方式,p->x, 它使得拥有指针在概念上就等价于拥有了指针所指的全部内容。在这种诱导下,我们渐渐模糊了地址和地址所存储的内容之间的区别。这也是指针的指针这样的概念总是让初学者迷惑不解的重要原因。
指针是对地址的符号化。它所带来的第一大好处是使得我们摆脱了对绝对地址空间的依赖。如同Newton第一定律所阐述的:物理规律与所发生的惯性坐标系无关。同样,数字空间中发生的的事件与所处的绝对地址也是无关的。在符号化的方向上更进一步,如果我们专注于指针的关联语义,而放弃指针的指针这样的混杂概念,就会得到具有独立价值的引用(Reference)概念.
从表面上看起来,数字空间只是一个无限延展的一维地址空间,每一地址处只能存放一个有限大小的离散数值,似乎它的几何学是贫瘠的。但是因为在软件设计中,一般是不考虑寻址时间的。这意味着在拥有指针的情况下,我们可以“立刻”访问到数字空间的任意遥远的地方。这种超时空的信息传递过程使得我们可以利用“引用”概念轻松的构建一个多维的表示空间。在面向对象的技术背景下,x.y.z这样的形式表示暗示着x,y,z是同时存在的。当z发生变化的时候,通过y.z和x.y的信息传导,x对象本身也发生了某种变化。
随着web技术的流行,独立的状态/地址空间的存在性逐渐成为系统不可回避的假设, "同时性"的物理约束越来越难以维持. 相对论规定了物理现象的定域性, 在数字空间我们一直忽视了它.但有趣的是, 网络上的传输时延却迫使我们重新发现了"引用"形式下必然存在着的物理过程. 引用本身只是标记了某种信息关联, 并不一定意味着同时性约束. 并发编程领域的所谓的Future对象是对传统引用概念的一种有趣扩展.
result = obj.callMethod(args) ==> future = obj.callMethod(args)
future对象可以被自由传递, 只有当实际访问到它的属性的时候, 才会触发时序约束.

(地址由最初到现在,在空间和时间上都发生了很大的变化
最显著的特点就是更耦合了
我这个理解没错吧?)

(从指针到引用,虽然在C++中显得只是写法上的简化。但是仔细体会能够发现概念层面发生的一些变化。我们不再依赖取址这个具体过程,而在某种符号化的层面上理解引用所表达的信息关联。耦合不是一个合适的描述。)

B 面向集合的框架设计
判断和循环是程序中最基本的语句结构。而在vonNeumann体系架构下,循环是对集合进行操作所需的基本步骤。一个有趣的事实是,函数式语言所宣称的生产力的来源很大程度上在于集合操作的便捷性。在数学中我们通过张量分析,泛函分析等可以清楚地意识到集合之间的相互作用是可抽象的,是可以独立理解的,即我们可以在不涉及具体基元结构的层面上独立的定义并执行集合运算。如何将这种概念独立性在框架层面展开是一个非常深刻的命题。
在基元结构上应用基础操作p(d)这一微观场景一般情况下是容易理解并实现的, 但通常程序中所定义的大量边界是基于集合变量的, 因此很多代码都是封包和解包操作, 在层层嵌套的循环结构深处我们才能发现真正具有业务价值的基元结构. 将集合操作提升到系统层,减少或简化在应用层需要显式编制的循环结构是框架设计层面需要考虑的问题.
一个最基本的方法是尽量定义通用的同构操作, 避免构造中间集合. 例如前后台之间通过json等通用协议交换复杂结构的对象, 避免定义特殊的中间处理过程. 一个与之相配合的重要技术手段是通过类查询语句(描述方式)直接构造特定的集合. 例如prototype.js中提供的$$('div div.myclass').each(op)这样的处理方式显然要比在循环过程中基于特定条件过滤要方便的多. 而在AOP操作中切点的集合定义方式也是其提供的核心价值之一. 与集合操作相适应的一种代码风格是流式设计(stream), 这正是jQuery试图鼓吹的主要价值(虽然我个人认为它有些走极端). 流式设计的核心结构实际上是 x += dx, 它不需要集合是一次性构造的, 便于支持一种逐步部分修正的概念模型.
为了支持集合的隐式构造, 我们需要以通用的方式定义元素到集合的组装规则. 在Witrix平台的前台js框架中我们定义了由独立的html组件到复合查询条件的拼接规则, 定义了由每个html组件的数据校验函数到整个form的数据校验函数之间的组装规则, 定义了由单个html组件提交参数到整个form提交参数之间的组装规则. 在Witrix平台的标准界面上, 框架本身的编制基于js.query.buildCondition(frmQuery), js.validate.validateForm(frmUpdate), ajax.addForm(frmUpdate)等少量集合操作进行, 在不同的应用场景下, 我们只需要关心每一个字段如何显示, 提交哪些属性, 而由系统负责将它们自动组装并在后台进行分派. 面向不同的应用, 框架代码不需要做出任何修改, 确保了系统结构的可重用性.
Witrix平台的后台处理模型中定义了实体化过程, DaoWebAction基于CRUD等原子操作定义了批量提交, 数据导入导出等复合的甚至是嵌套的集合操作. 在不同的应用中, 我们通过修改bizflow文件中<action id="ViewDetail-default">, <action id="Update-default">等针对单实体的业务规则即可适应不同的业务场景, 而不需要为特定的应用重复编制集合处理过程.
面向集合+通用组装规则是Witrix平台设计中采用的基本设计手法之一, 它使得我们在一般应用中只需要考虑单实体,单字段等基元结构上发生的特定业务, 大大简化了系统构造过程. 但是也需要认识到从个体到集合的扩张(p(d) -> P(D) )是非平凡的, 集合比个体的简单加和要更多, 为此架构中需要保留对集合边界的识别能力, 例如需要允许在数据导入完成之后执行特定的业务规则而不是仅仅针对每一数据行执行业务规则.

C 软件与建筑
建筑学的隐喻在软件业中一直很流行。开发软件和建筑楼房从某种意义上说都是一种构造过程,而建筑学的成熟无疑让很多软件工程师非常羡慕。Design Pattern的始作俑者坦承受到建筑学著作的影响更是让一些人对建筑学的精深顶礼膜拜不已。不过,建筑决不只是表面上的形象/功能设计,也决不是民工就可以干的活计,在现代建筑设计背后是土木工程的蓬勃发展。正是框架结构的出现和材料工艺的进步才使得批量生产的大型现代建筑成为可能。
虽然建筑学有着它不为人知的复杂性的一面,但是与软件开发相比,它也有着简单贫瘠的一面。现在架构师言必称设计模式,但是估计很少有人读过Christopher Alexander的原著"A Pattern Language"。在Alexander的概念中,所谓的模式"describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice". 关键在于这些模式在建筑学中往往映射为某种独立的原子化的实体(entity), 因此可以把它们作为一种语言的基础组分,构成所谓的Pattern Language. 例如现在要开发一个门廊,你可以从"私家的沿街露台(140)"开始,在"有阳光的地方(161)"做成一个"有围合的户外小空间(163)", 选择"6英尺深的阳台(167)", 保留"小路和标志物(120)", 注意"天花高度变化(190)"和"角柱(212)"的位置,在"各式座椅(251)"的旁边安排一个"高花台(245)". Alexander共定义了253个模式,括号中的便是模式的编号。很明显,物理空间的不可重入性直接规范了建筑设计空间的有限性。
在软件设计中,类似VB/Delphi的可视化界面设计的操作过程与此类似:理想情况下界面开发基本就是用各种界面元素填满一个Form的过程。但是软件的一个本质复杂性在于它的基本结构单元是函数,而设计空间中同一个功能点对应的实现函数的个数和形式并不存在有限性的约束,函数的组合形式也不是线性延展的。建筑基本上依赖的是静力学,而软件无疑需要用动力学来刻画。即使是界面开发,我们所关注的也决不仅仅是静态摆放问题,而更重要的往往是界面元素动态相关和动态变化的问题。
在Web开发领域,一直有人鼓吹模仿VB/Delphi的快速开发工具,但是我并不认为这其中的设计哲学是与软件的本质相匹配的。软件中所蕴含的无限的动态变化不应该仅仅通过有限的配置过程来应对,我们需要更加强大的结构抽象和结构构建手段。

D 结构的消解
程序中大量的工作其实都是在定义结构以及结构之间的关系. 一般情况下我们应该识别出结构,并把它们封装到函数,对象和组件中去. 但是封装并不永远都是有利的. 将某个结构独立出来, 在某种程度上也就割裂了它和其他元素之间的关系, 这会引发结构融合的障碍, 也会造成思维上的负担. 事实上如果程序整体具有足够的可理解性和概念稳定性, 我们并不需要独立识别出什么子部分. 一个简单的例子是数组循环. 一般情况下我们应该尽量把循环查找等操作封装到函数中, 避免多重循环嵌套时产生过于复杂的代码块. 但是如果数组或者语言本身提供了each, map等函数式操作符,则这种封装需求就大大减弱了.
随着系统结构的日益复杂化, 在系统中会积累大量的背景知识.此时当我们需要完成一个功能的的时候, 往往不再需要指定所有的信息, 而只需要指定背景知识之外的部分信息即可. 例如在界面上通过一个分页表格来显示实体列表这样一个功能, 在Witrix平台中通过模型驱动的标准页面即可自动完成. 一般的定制需求往往是过滤显示部分数据, 在表格行上增加一些操作按钮, 定制表格的表头等. Witrix平台实现这些需求并不需要封装出一个独立的表格组件, 调用它的属性修改方法等, 而是把定制部分嵌入到BizFlow的配置中, 这里并没有明确的结构界限.
<biz id="default">
<filter>
<eq name="status" value="1" />
<filter>
<tpls>
<tpl id="thead>
<thead>
<tr rowspan="2">...</tr>
<tr>...</tr>
</thead>
</tpl>
<tpl id="rowOps">
<ui:FlatButton .../>
</tpl>
</tpls>
其他与表格无关的信息
</biz>
注意到对于我们理解业务而言, 我们并不需要知道表格具有分页, 排序, 隔行变色等功能. 所有和业务相关的代码聚集到BizFlow文件中, 它们构成不
Tags:  网站架构师

延伸阅读

最新评论

发表评论