|
|
置顶随笔
2010年1月17日
html主要通过内置的<script>,<link>, <img>等标签引入外部的资源文件,一般的Web框架并没有对这些资源文件进行抽象,因此在实现组件封装时存在一些难以克服的困难。例如一个使用传统JSP Tag机制实现的Web组件中可能用到js1.js, js2.js和css1.css等文件,当在界面上存在多个同样的组件的时候,可能会生成多个重复的<script>和<link>标签调用,这将对页面性能造成严重的负面影响。资源管理应该是一个Web框架的内置组成部分之一。在Witrix平台中,我们主要借助于tpl模板引擎来输出html文本, 因此可以通过自定义标签机制重新实现资源相关的html标签, 由此来提供如下增强处理功能:
1. 识别contextPath
tpl模板中的所有资源相关标签都会自动拼接Web应用的contextPath, 例如当contextPath=myApp时
<script src="/a.js"></script> 将最终输出 <script src="/myApp/a.js" ...>
2. 识别重复装载
<script src="a.js" tpl:once="true"></script>
tpl:once属性将保证在页面中script标签实际只会出现一次.
3. 识别组件内相对路径
开发Web组件时,我们希望所有资源文件都应该相对组件目录进行定位,但是直接输出的<script>等标签都是相对于最终的调用链接进行相对路径定位的. 例如在page1.jsp中调用了组件A, 在组件A的实现中, 输出了<script src="my_control.js"></script>
我们的意图一般是相对于组件A的实现文件进行定位, 而不是相对于page1.jsp进行定位. tpl模板引擎的相对路径解析规则为永远相对于当前文件进行定位. 例如
<c:include src="sub.tpl" />
在sub.tpl中的所有相对路径都相对于sub.tpl文件进行定位.
4. 编译期文件有效性检查
在编译期, tpl引擎会检查所有引入的资源文件的有效性. 如果发现资源文件丢失, 将直接抛出异常. 这样就不用等到上线后才发现文件命名已修改等问题.
5. 缓存控制
浏览器缺省会缓存css, js等文件, 因此系统上线后如果修改资源文件可能会造成与客户端缓存不一致的情况. 一个简单的处理方式是每次生成资源链接的时候都拼接文件的修改日期或者版本号, 这样既可利用客户端缓存, 又可以保证总是使用最新版本. 例如
<script src="a.js"></script>将会输出 <script src="/myApp/myModule/a.js?344566" ...>
6. 字符集选择
为了简化国际化处理, 一般提倡的最佳实践方式是坚持使用UTF-8编码. 但是很多情况下可能使用系统内置的GBK编码会更加方便一些, 另外集成一些既有代码时也存在着不同字符集的问题. 在Witrix平台中, 所有输出的资源标签都会标明对应的字符集, 如果没有明确设置就取系统参数中的缺省字符集.
例如 <script src="a.js"></script> 将会输出 <script ... charset="GBK"></script>
7. 缺省theme支持
为了支持多种页面风格, 往往不是简单的替换css文件即可实现的, 它可能意味着整个组件的实现代码的更换. Witrix平台中通过一系列缺省判断来简化这一过程. 例如如下代码表明如果设置了ui_theme系统参数, 并且对应的特殊实现存在, 则使用特殊实现, 否则系统缺省实现.
<c:include src="${cp:ui_theme()}/ctl_my_ctl.tpl" >
<c:include src="default/ctl_my_ctl.tpl" />
</c:include>
2009年12月13日
AOP(Aspect Oriented Programming)早已不是什么新鲜的概念,但有趣的是,除了事务(transaction), 日志(Log)等寥寥几个样板应用之外,我们似乎找不到它的用武之地。http://canonical.javaeye.com/blog/34941
很多人的疑惑是我直接改代码就行了,干吗要用AOP呢?AOP的定义和实现那么复杂,能够提供什么特异的价值呢?
Witrix平台依赖于AOP概念来完成领域模型抽象与模型变换,但是在具体的实现方式上,却与常见的AOP软件包有着很大差异。http://canonical.javaeye.com/blog/542622
AOP的具体技术内容包括定位和组装两个部分。简化切点定位方式和重新规划组装空间,是Witrix中有效使用AOP技术的前提。
在Witrix平台中,对于AOP技术的一种具体应用是支持产品的二次开发。在产品的实施过程中,经常需要根据客户的特定需求,修改某些函数的实现。我们
可以选择在主版本代码中不断追加相互纠缠的if-else语句,试图去包容所有已知和未知的应用场景。我们也可以选择主版本代码和定制代码独立开发的方
式,主版本代码实现逻辑框架,定制代码通过AOP机制与主版本代码融合,根据具体场景要求对主版本功能进行修正。AOP的这种应用与所谓的横切概念是有所
区别的。典型的,一个横切的切点会涉及到很多类的很多方法,而函数定制则往往要求准确定位到某个业务对象的某个特定的业务方法上。传统AOP技术的切点定
义方式并不适合这种精确的单点定位。在Witrix平台中,我们通过直接的名称映射来定义切点。例如,修正spring中注册的MyObject对象的
myFunc方法,可以在app.aop.xml文件中增加如下标签
<myObject.myFunc>
在原函数执行之前执行
<aop:Proceed/> <!-- 执行原函数内容 -->
在原函数执行之后执行
</myObject.myFunc>
[spring对象名.方法名]这种映射方法比基于正则字符串匹配的方式要简单明确的多。spring容器本身已经实现了对象的全局管理功能,spring对象名称必然是唯一的,公开发布的,相互之间不冲突的,没有必要再通过匹配运算重新发现出它的唯一性。
对于一些确实存在的横切需求,我们可以通过Annotation机制来实现切点坐标标定,将复杂的切点匹配问题重新划归为[对象名.方法名]。
@AopClass({"myObject","otherObject"})
class SomeClass{
@AopMethod({"myFunc","otherFunc"})
void someFunc(){}
}
针对以上对象,在app.aop.xml文件中可以定义
<I-myObject.I-myFunc>
.
</I-myObject.I-myFunc>
2009年12月6日
结构的稳定性,直观的理解起来,就是结构在存在外部扰动的情况下长时间保持某种形式不变性的能力。稳定意味着小的扰动造成的后果也是“小”的。在数学中,Taylor级数为我们描绘了变化传播的基本图景。
F(x0 + dx) = F(x0) + F'(x0)*dx + 0.5*F''(x0)*dx^2 + 
扰动dx可能在系统F中引发非常复杂的作用过程,在系统各处产生一个个局部变化结果。表面上看起来,似乎这些变化结果存在着无穷多种可能的分组方式,例如 (F'(x0)-2)*dx + 2*dx^2, 但是基于微分分析,我们却很容易了解到Taylor级数的每一级都对应着独立的物理解释,它们构成自然的分组标准。某一量级下的所有变化汇总归并到一起,并对应一个明确的整体描述。在抽象的数理空间中,我们具有一种无所不达的变化搜集能力。变化项可以从基础结构中分离出来,经过汇总后可以对其进行独立的研究。变化本身并不会直接导致基础结构的崩溃。
在软件建模领域,模型的稳定性面临的却是另一番场景。一个软件模型一旦被实现之后,种种局部需求变更就都会形成对原有基础结构的冲击。一些局部的需求变化可能造成大片原有实现失效,我们将被迫为类似的需求重新编写类似的代码。此时,软件开发并不像是一种纯粹的信息创造,而是宛若某种物质产品的生产(参见从编写代码到制造代码 http://canonical.javaeye.com/blog/333167 )。显然,我们需要一种能力,将局部变化从基础结构中剥离出来,经过汇总归并之后再进行综合分析和处理。这正是AOP(Aspect Oriented Programming)技术的价值所在。
M1 = (G0+dG0)<M0+dM0> ==> M1 = G0<M0> + dM
AOP本质上是软件结构空间的自由修正机制。只有结合AOP技术之后,软件模型才能够重新恢复抽象的本质,在时间之河中逃离随机变化的侵蚀,保持实现层面的稳定性。在这一背景下,建模的目的将不是为了能够跟踪最终需求的变动,而是要在某个独立的层面上能够自圆其说,能够具有某种独立存在的完满性,成为思维上可以把握的某个稳定的基点。模型的真实性将因为自身结构的完备性而得到证明,与外部世界的契合程度不再是价值判断的唯一标准。 http://canonical.javaeye.com/blog/482620
2009年10月7日
说到软件建模,一个常见的论调是模型应该符合实际需求,反映问题的本质。但是何谓本质,却是没有先验定义的。在成功的建立一个模型之前,无论在内涵上还是在外延上我们都很难说清楚一个问题的本质是什么。如果将模型看作是对领域结构的一种显式描述和表达,我们可以首先考察一下一个“合适”的结构应该具备哪些特征。
按照结构主义哲学的观点,结构具有三个要素:整体性,具有转换规律或法则(转换性),自身调整性(自律性)。整体性意味着结构不能被简单的切分,其构成要素通过内在的关系运算实现大范围的关联与转换,整体之所以成为整体正是以转换/运算的第一性为保证的。这种转换可以是共时的(同时存在的各元素),也可以是历时的(历史的转换构造过程),这意味着结构总要求一个内在的构造过程,在独立于外部环境的情况下结构具有某种自给自足的特性,不依赖于外部条件即可独立的存在并保持内在的活动。自律性意味着结构内在的转换总是维持着某种封闭性和守恒性,确保新的成分在无限地构成而结构边界却保持稳定。注意到这里对结构的评判并不是来自外在规范和约束,而是基于结构内在的规律性,所强调的不是结构对外部条件的适应性,而是自身概念体系的完备性。实际上,一个无法直接对应于当前实际环境的结构仍然可能具有重要的价值,并在解决问题的过程中扮演不可或缺的角色。在合理性这个视角下,我们所关注的不仅仅是当前的现实世界,而是所有可能的世界。一个“合理”的结构的价值必能在它所适应的世界中凸现出来。
在信息系统中,我们可能经常会问这个模型是否是对业务的准确描述,是否可以适应需求的变更,是否允许未来的各种扩展等等。但是如果换一个思维方向,我们会发现这些问题都是针对最终确立的模型而发问的,而在模型构建的过程中,那些可被利用的已存在的或者可以存在的模型又是哪些呢。每一个信息模型都对应着某种自动推理机,可以接收信息并做一定的推导综合工作。一个可行的问题是,如何才能更有效的利用已有的信息进行推导,如何消除冗余并减少各种转换成本。我们经常可以观察到,某一信息组织方式更充分的发掘了信息之间的内在关联(一个表象是它对信息的使用不是简单的局域化的,而是在多处呈现为互相纠缠的方式,难以被分解),这种内在关联足够丰富,以至于我们不依赖于外部因素就可以独立的理解。这种纠缠在一起的信息块自然会成为我们建模的对象。
如果模型的“覆盖能力”不再是我们关注的重点,那么建模的思维图式将会发生如下的转化
最终的模型可以由一系列微模型交织构成。模型的递进构造过程并不同于组件(Component)的实物组装接口,也不是CAD图纸堆叠式的架构概念所能容纳的。在Witrix平台中,模型分解和构造表达为如下形式 http://canonical.javaeye.com/blog/333167
Biz[n] = Biz[n+1] aop-extends CodeGenerator<DSLx, DSLy>。
在软件发展的早期,所有的程序都是特殊构造的,其必然的假设是【在此情况下】,重用不在考虑范围之内,开发可以说是一个盲目试错的过程。随着我们逐步积累了一些经验,开始自觉的应用理论分析手段,【在所有情况下】都成立的一些普适的原理被揭示出来,它们成为我们在广阔的未知世界中跋涉时的向导。当我们的足迹渐渐接近领域的边界,对领域的全貌有一个总体的认知之后,一种对自身成熟性的自信很自然的将我们导向更加领域特定的分析。很多时候,我们会发现一个特化假设可以大大提高信息的利用率,推导出众多未被显式设定的知识。我们需要那些【在某些情况下】有效的规则来构成一个完备的模型库。这就如同有大量备选的数学定理,面对不同的物理现象,我们会从一系列的数学工具中选择一个进行使用一样。
2009年7月11日
软件开发技术的技术本质在于对代码结构的有效控制. 我们需要能够有效的分解/重组代码片断, 凸显设计意图. 面向对象是目前最常见的代码组织技术. 典型的, 它可以处理如下模式
A1 --> B2, A2 --> B2, A3 --> B3 ...
我们观察到A1和A2之间, B2和B2之间具有某种概念关联性, 同时存在某种抽象结构 [A] --> [B].
对于这种情况, 我们可以定义对象 [A], [B], 它们分别是 A1和A2的聚合, B1和B2的聚合等. 举例来说, 对于如下表格描述, <ui:Col>所提供的信息在映射为html实现的时候将在多处被应用.
<ui:Table data="${data}">
<ui:Col name="fieldA" label="labelA" width="20" />
<ui:Col name="fieldB" label="labelB" width="10" />
</ui:Table>
这里<ui:Col>提供的信息对应三个部分的内容: 1. 列标题 2. 列样式(宽度等) 3. 列数据
面向对象的常见做法是抽象出 UiCol对象, 它作为UiTable对象的属性存在, 在生成表头, 表列样式和表格数据内容时将被使用. 但是我们注意到面向对象要求多个方法通过this指针形成状态耦合
,在某种意义上它意味着所有的成员方法在任一时刻都是同时存在着的。它们所代表着的存在的代价必须被接受(存储空间等)。即使并不同时被使用,我们仍然需要同时持有所有成员函数指针及
共享的this指针。实际上, 我们并不一定需要A1和A2同时在场. 在这种情况下, 编译期技术可以提供另一种不同的行为聚合方式.
<table>
<thead>
<sys:CompileTagBody cp:part="thead" />
</thead>
<cols>
<sys:CompileTagBody cp:part="cols" />
</cols>
<tbody>
<sys:CompileTagBody cp:part="tbody" />
</tbody>
</table>
只要<ui:Col>标签的实现中针对编译期的cp:part变量进行分别处理, 即可实现信息的部分析取.
2009年5月30日
html最早的设计目标只是作为某种多媒体文档展现技术,其设计者显然无法预料到今天Web应用的蓬勃发展,一些设计缺陷也就难以避免。特别是html规范中缺乏对于复杂交互式组件模型的支持,直接导致企业应用的前台开发困难重重。AJAX技术可以看作是对这种困境的一种改良性响应,它试图通过javascript语言在应用层创建并维护一系列复杂的交互机制。很多完善的ajax框架走得相当遥远,最终基本将html作为一种底层“汇编”语言来使用。例如,一个很整齐美观的类Excel表格可能是由一个个div拼接而成,与html原生的table元素已经没有任何关系。
Witrix平台中对于前台html模型也作了一定的增强,但基本的设计思想是尽量利用原生控件,并尽量保持原生控件内在的数据关系,而不是重新构建一个完整的底层支撑环境。采用这种设计的原因大致有如下几点:
1. 前台技术目前竞争非常激烈,我们优先选择的方式是集成第三方组件,尽量保持原生环境有利于降低集成成本。
2. 通过javascript构造的控件可能存在性能瓶颈和其他浏览器内在的限制。例如一般Ajax框架提供的Grid控件都无法支撑大量单元格的显示。
3. Witrix平台的tpl模板技术可以非常方便的生成html文本,并提供强大的控件抽象能力,因此在前台动态创建并组织界面元素在Witrix平台中是一种不经济的做法。
4. Witrix平台提供的分解机制非常细致,存储于不同地方的不同来源的代码会在不同的时刻织入到最终的页面中,基于原生环境有利于降低平台快速演进过程中的设计风险。
Witrix平台中对于html模型的增强主要关注于以最少的代码实现界面控件与业务逻辑的自然结合。基本结构包括:
1. 通过ControlManager对象在前台建立一种container结构,统一管理控件的注册和获取。js.makeControl(elmOrId)返回特殊注册的控件对象或者根据原生html元素生成一个包装对象。
2. 通过js.getWxValue(elm)和js.setWxValue(elm,value)这两个函数统一对控件的值的存取过程。
3. 通过js.regListener(elm,listenerFunc)统一管理控件之间的相关触发,实现控件之间的相互监听。当js.setWxValue(elm,value)被调用时,注册在ControlManager中的listenerFunc将被调用。
4. stdPage.setFieldValue(fieldName,value)和stdPage.getFieldValue(fieldName,value)统一针对业务字段的值的存取过程,这里fieldName对应于实体上的业务字段名。
5. 通过ajax.addForm(frmId)等函数统一前台提交参数的提取过程,通过stdPage.buildAjax()等函数统一后台服务的调用方式。
6. 通过stdPage对象统一封装业务场景中的"常识"。
基于以上一些基础机制,Witrix平台即可提供一些复杂的业务组件封装。例如<input name="productCode" onkeypress="stdPage.keyPressToLoadRefByCode({objectName:'SomeProduct',queryField:'productCode'})" .../>通过简单的调用一个js函数即可实现如下功能:
a. 在文本框中输入回车的时候自动提交到后台查找对应产品代码的产品,并更新前台多个相关字段的值
b. 如果没有查找到相应产品,则弹出对话框根据界面上已有的部分字段信息提示客户添加新的产品信息。
c. 如果找到多个对应产品,则弹出列表允许客户选择其一。
d. 具体的处理过程可以通过函数参数进行精细的控制。
在meta文件中,结合上下文环境中的元数据信息,我们在缺省情况下可以直接使用 <ds:LoadRefByCodeInputor objectName="SomeProduct" />标签,不需要任何其他附加参数。
Witrix平台中一般利用原生控件来保存数据值,而不是将数据保存在分离的js对象中。例如对于一个选择控件,经常要求选择得到的是实体的id,而显示在界面上的是某个其他字段的值。Witrix平台中一般的实现结构是
<input type="hidden" name="${fieldName}" value="${entity[dsMeta.idField]}" id="${id}" textId="text_${id}" />
<input type="text" value="${entity[dsMeta.nameField]}" id="text_${id}" />
通过textId等扩展属性即可明确定义控件多个部分之间的关联关系,同时保证控件的实现完全与html规范相兼容。
Witrix平台中目前使用的"标准化"的扩展属性有textId(对应文本显示控件的id), showName(某些无文字显示的选择控件需要保留显示字段值), op(字段作为查询条件提交时的比较算符),validator(字段值对应的检验函数),setWxValue/getWxValue(重定义控件值的存取行为),serializer(特殊处理前台控件的提交参数)等。扩展属性不仅可以引入说明信息,还可以引入丰富的控件行为。
2009年3月22日
分层是最常见的软件架构方式之一。分层之后可以区分出横纵两个维度,纵向往往表现出一种隔离性。出于有意无意的各种原因,层次之间传递信息很容易出现模糊甚至丢失的现象。B/S多层体系架构下的程序因为浏览器和服务器之间的状态空间相互独立,相对于共享全局状态空间的C/S程序,更容易出现信息传递不畅的问题。实际上,我们经常可以观察到B/S程序中存在着大量的"接力"代码,即在交界处,总是存在着大量用于读取变量,拼接变量,转换变量等与主体业务无关但却又不可或缺的代码。在多层架构程序中,信道构建应该是一个需要给予足够重视的问题。
在系统规划中,多层结构应该内置与具体语义无关的通用信道,它跨越多个层次,允许信息透明的通过,并以未预期的方式在不同的层面激发各种相关的行为。在Witrix平台中,平台代码与特定应用中的业务代码处于高度交织的状态,一个特定业务功能的实现往往需要多处业务代码相互协同,平台必须成为某种透明的背景。例如,假设我们编制了一个通用的列表选择控件,它封装的逻辑是从一个实体列表中进行选择
<app:SelectOne objectName="MyEntity" />
如果现在要求选择时只列出某个类型的实体,则调用形式为
<app:SelectOne objectName="MyEntity" extArgs="$bizId=select&$type=1" />
在调用入口处补充必要的信息之后会推动系统在遥远的状态空间中应用一个特定的过滤条件。这里$bizId负责指示平台应用特定的元数据配置,而其他的参数则由元数据中的逻辑负责处理。平台与特定业务代码各取所需,相互配合,将尽可能多的逻辑剥离为通用机制。
2009年2月28日
现代数学是建立在等价类这一概念的基础之上的。同构是对等价关系的一种刻划。简单的可以把它理解为两个系统之间的一种“保持”运算规则的一一对应关系。在
数学中一个符号所代表的是所有能够互相同构的对象。例如整数3可以看作是与某个元素个数为3的集合可以建立一一对应关系的所有的集合所构成的整体。所以在
数学中,如果我们解决某个特定的问题,它同时也就意味着我们解决了一系列相互等价的问题。
同构关系对于认知可以起到本质上的简化作用。如果通过一个推理链条,确认了A == B == C == D,则可以直接从概念上推导出 A
== D,
这一关系有可能被直观理解,而不需要理会中间的推理步骤。(注意到以上元素两两建立同构关系的时候可能要采用不同的对应手段,因此上面的等式并不是平凡
的。)另一方面,我们可以确定一个模型元素M, 将系统简化为 A == M, B == M, C == M, D ==
M。只要理解了元素M就理解了等价的其他元素。
Witrix平台中PDM定义作为基础的结构模型,它同时映射成为数据库表,以及hbm, java,
meta等多个代码文件,此外还对应于约定的WebObject名称和BizFlow文件名称,相应的报表文件目录等。我们只要理解了pdm模型,即可通
过推理自然的掌握各个层面上对应的结构。这意味着只要知道实体名称,就知道如何通过Web访问这个对象,知道数据在数据库中对应的数据库表,而不需要知道
后台是如何读取前台提交的参数以及如何执行保存数据指令的。不仅仅是在模型驱动领域,在系统设计的各个方面,我们都应该尽量充分的利用当前的信息通过推理
得到系统其他部分的结构,而不是通过手工关联或者判断在程序中动态维持这种对应关系。例如在flow-cp机制中,biz的id,
action的id等都根据step配置的id推导得到,这样在工作列表跳转的时候就可以根据规则推导出跳转页面对应的链接,而不需要手工编写页面重定向
代码。

同态(homomorphism)关系相对于同构关系,只强调单向映射的可行性,它是一个舍弃属性的过程。同态作为最基础的认知手段之一,它不仅仅是用一
个符号来置换一组元素,而是同时保留了某种全局的运算关系,因此同态映像可以构成某种独立的完整的研究对象。通过同态映射,我们可以在不同的抽象层面上研
究原系统的一个简化版本。例如meta中的layout是一种典型的领域特定语言(DSL)。
userName userTitle
emailAddress
每一个字段表示了一个可能任意复杂的inputor或者viewer,
字段之间的前后关系描述了最终显示页面上显示内容的相对关系。当viewer根据需求发生改变的时候,并不影响到layout层面上的关系,因此
layout可以保持不变。如果我们在系统中把问题分解为多个抽象层面上,多个观察视角上的同态模型,则可能实现更高的软件复用程度。
在Witrix平台的设计中,很多细粒度的标签都定义为tpl文本段,这样平台只要理解某一层面上的交互关系,实际应用中可能出现的细节在标签内部进行局
部处理,不会突破原始设计的形式边界,不会影响到原先设定的主体系统结构。例如BizFlow中的tpls段,action的source段等。
上世纪50年代以前,生物学家做梦也想象不到具有无限复杂性的生物遗传过程,竟然可以被抽象为ATGC四个抽象符号的串联。生命竟然不理会各种已知的或是
未知的物理化学作用,被抽象的三联码所驱动。一种抽象的本质似乎成了生命世界的本原。在软件的世界中,可以被识别的抽象元素绝不只是语言本身所提供的那些
机制。
2009年2月21日
有一个心理学实验,要求被试者将青草,公鸡,牛三个东西分
成两组,结果多数中国儿童将青草和牛分成一组,而多数美国儿童将公鸡和牛分成一组。中国人的思想中青草和牛之间存在现实的关系,牛吃草,而西方人的典型逻
辑是公鸡和牛都属于动物这一范畴。通过分类将物体类型化,这是西方人从小就接受的训练。据说美国婴儿学习名词的速度要快于动词,而中国的婴儿则相反,这并不是偶然的。
中国人的传统哲学认为世界是普遍联系的,事物之间存在着祸福相依的辩证转化关系。而古希腊人强调个体意识,以两分法看待世界,他们将世界看成是孤立的物体组成(原子论)构成,然后选择一个孤立物体(脱离背景),开始研究它的各项属性,接着将属性泛化,构成分类的基础。西方语言中大量抽象概念都是从作为属性的形容词直接转化而来,例如
white
-->
whiteness
。而中文中很少有精确的类型定义,而多半是富有表现力的,隐喻性的词语,例如我们不谈论抽象的白,而只说雪白,没有抽象的
size
,而只说具体的大小。
亚里士多德认为铁球在空气中下落是因为它具有“重性”,而木块在水中漂浮是因为木块具有“轻性”。这种将一切原因归结为事物内在属性的传统在一定程度上妨碍了西方人认识到背景的存在和作用,但使得他们可以把问题简化。
古希腊人对于类型的热衷源于他们对于永恒的迷恋。静态的亘古不变的世界才是他们的思想栖息的场所。具体的物体是易逝的,多变的,只有抽象的类型才是永恒的存在,也只有抽象概念之间的关系才是永真的联系。而具体实例之间的关联在某种程度上被认为是不重要的,甚至是不可靠的。

将具有某一属性的所有物体定义为一个集合,这一做法在上世纪初被发现会引起逻辑悖论,动摇了整个数学的基础,它绝不像表面上看起来那么单纯。但确定无疑的是,通过类型来把握不变的事实是一种非常重要且有效的认识策略。面向对象语言强调名词概
念,从引入类定义以及类之间的继承关系开始,这符合西方一贯的作风。而
Ruby
这种强调实例间关系的动态语言首先由日本人发明,可能也不是偶然的。虽然现在大家都在玩高科技了,可实际贩卖给你的多半仍然是包治百病的祖传秘方。文化可能造成认知上的一种偏执,在技术领域这一现象并没有被清楚的意识到。
|