posts - 176, comments - 240, trackbacks - 0, articles - 7

     分布式有几个核心问题。首先不存在全局的状态空间,各个节点上的状态可能不统一,节点之间的通信需要通过序列化等方式跨越状态空间边界。一般只有只读数据 才能做到真正的分布式,因为只有只读数据才能维持各个节点之间的一致性。第二,节点之间的信息传递都是单向的,不存在test_and_set这样的锁原 语(test_and_set需要同时信息双向流)。如果需要建立分布式锁,必须存在中心协调者,即存在着瓶颈。
     最近很流行的几个概念涉及到分布式的不同方面。

 。grid技术主要处理分布式节点的自组织结构

。 web service采用xml技术,实现异步分布式调用,并且因为采用xml这种元语言,将人的智能整合到系统调用层面。即远程调用的可能是一段程序,也可能是通过一个用户界面接收用户输入,然后将用户输入作为web service调用结果返回。

。workflow 捕获自治节点行为之间较为短暂的联系。存在一些程序步骤之间的“瞬态"关联,它们没有稳定到可以抽象出一个程序中的对象,但可以建立一个过程处理模型(整 合的关键是无参数调用,或者更准确地说是只存在一个全局变量空间,都从该空间中得到输入,并输出到该空间)

posted @ 2005-11-15 12:19 canonical 阅读(191) | 评论 (0)编辑 收藏

     理论物理的优美在于从少量基本原理(如最小作用量原理)出发推导出整个理论大厦。而在软件设计领域却充斥着林林总总的"最佳实践". 太多的规则只会意味着没有规则。软件设计领域的现状说明这个领域还处于非常稚嫩的阶段,应该从其他领域借鉴更多的知识。在我前面的blog中已经说明了在 软件中的一些具体的分析技术。但如何有效的应用这些分析技术,我们还需要一些指导性的理论框架。如果把软件设计放在更加广泛的系统工程的背景下,一个合适 的支配性原理应该是最小复杂性原理。即在软件分析过程中我们所作的一切,无论是面向对象分析,基于工作流的分析等等,其目的都是尽量降低系统构建的复杂 性。只要能够有效降低系统构建的复杂性,那所采用的方法和所建立的模型就是好的。复杂性是唯一的度量,而不是是否符合某人的观点,是否采用流行的技术等 (当然,采用流行技术往往意味着降低了和其它系统交互的复杂性)。
     李小龙说:简单是美。但是否最简单的就是最好的?科学界对此却有着否定的结论。关于复杂性的一个很深刻的基本事实是,复杂性是分级的,不同复杂性层次的事 物有着本质的差异,不能期待一个较低复杂性的方法能够解决一个更高复杂性层次上的问题。所以Einstein说:make everything as simple as possible, but not simpler。复杂性级列的存在意味着,随着我们获取到更多的信息量或者因为系统自身的发展,当我们所建立的系统的模型沿着复杂性级列演化的时候,我们 所提出的解决问题的方案也必须作相应的演化。即我们提供的不是一些固定的解决方案(solution),  而必须是一个完整的策略(strategy)。这样我们就不是孤立的看待一个问题, 而是要看到它的过去,现在和未来。
     在作分析的时候,我们都知道"从一般到特殊,从特殊到一般",但如何科学的,有步骤的去做呢? 我从等离子体物理的BBGKY Hierarchy中学到了一个基本的分析框架: 级列理论.这个理论首先定义了一个最普适的模型:气体由N个完全相同的原子组成, 其动力学由N个互相耦合的Newton力学方程来描述。理论的第二步是从最极端的简化开始,假设所有的原子之间不存在相互作用,即假定原子之间是相互独立 的。则得到Vlasov方程。理论的第三步考虑系统的复杂性逐渐增加,当气体密度较高,必须考虑分子之间两两碰撞的时候,得到Boltzman方程。当需 要考虑更高阶的相互作用的时候,我们得到关联动理学方程,如此继续下去建立一个无穷级列。
抽象一些地说,该理论包含如下内容:
. 建立能够描述所有情况的最广泛的模型M0, 该模型定义系统的边界
. 建立一个包含最少假设的最简单的模型Mn,该模型作为求解的基础和基本的参照。
. 建立一个从Mn到M0的复杂性逐渐增加的模型级列,确保在每一个复杂性的层次上,都存在着对系统有效的建模,而且在求解高阶问题的时候,可以参考较低阶问题的解。
   如何从这种无穷级列中做出选择,必须根据具体应用情景而定,Vapnik在其统计学习理论中详细描述了这种求解策略:
 综合代价 = 训练数据与模型预测之间的偏差 + 模型自身的复杂性
    学习 = minimize[综合代价]
即在能够解决问题的方案中选择最简单的一个。


 建立级列理论,首先需要建立最广泛的模型。在量子力学中,确立了如下基本概念:
 1. 确定性的态。
 2. 所有态构成完备的态空间。
 3. 系综(即态的集合)的动力学。
   首先,状态的确定性非常重要。即使在量子力学中,量子态存在几率诠释,态函数本身在数学上仍然是确定性的,即在任一时刻,任一地点存在唯一的值。如果我们 的讨论没有一个确定性的基础,那所有的推演都将变得极为困难(模糊数学目前提供的帮助很少)。而且确定状态本身就是一个极为重要的简化过程。因为一般我们 可以建立Markov模型,使得系统的演化只与系统当前的状态有关,而与系统的历史状态无关,因此大大减少系统模型的参数。
   其次,状态演化的结果必须仍然处在态空间中,否则我们在该空间中建立的理论就存在着"盲点",存在着失效的可能。
   第三,我们研究的不是单个状态的演化,而是综合考虑相邻的状态。
   在软件世界,理论物理的这些真知灼见依然有效,只是在所有的概念前加上了"有限"这个修饰语。有限状态机(Finite State Machine)正是理论计算机科学研究的基本模型。所有的计算机都可以看作是有限状态的,因为所有的内存和硬盘能够表示的状态数是有限的。目前计算机科 学中几乎所有重要的理论成果都和有限自动机理论相关。在软件编制的过程中,我们第一步所要做的也是确定系统的状态。与物理系统所不同的是,我们认为物理系 统的状态及其演化规律是客观存在的,而软件系统中的规则是由开发者确立的,我们必须尽一切可能使得系统的状态能够被确定下来。例如,我们编制getXX ()和toString()等函数来暴露系统的状态,以确保系统状态的可观测性;使用assert断言来确保函数执行时的状态;尽量减少函数的副作用来避 免依赖函数调用历史。软件世界中态空间不完备的例子最著名的就是Y2K问题。所以很多人说,软件中最大的bug就是地址空间不足,因为这是任何技术手段都 无法解决的问题。

posted @ 2005-11-14 17:03 canonical 阅读(503) | 评论 (0)编辑 收藏

对象本质上是一种命名技术,即将一组相关的数据和函数放在一起,起一个名字。从业务层面上看,我们需要识别出大量的概念,对应到建立的领域模型,我 们就拥有不同的业务对象。这些业务对象的类型各不相同,可以区分出来。从中间件层面上看,需要从大量业务对象中抽象出共性,并以统一的方式进行处理。即在 中间件层,所有业务对象的类型被弱化下来,实际上丧失了其各自的独特性,即在中间件层看来,这些不同业务对象的类型是相同的。在中间件层的做法,一般是使 用reflection方法并结合少量全局性的接口。实际上是在结构层面上将对象作为Map来处理。这就象是应用科学与数学的关系。数学在抽象的层面上研 究结构之间的关系,每一个具体学科对相同的数学定理赋予不同的诠释。
 理论上,一个概念最好能够自适应的在不同的抽象层面上表现为不同的结构,但 受限于当前的面向对象实现技术,实际采取的技术路线多半为建立唯一的强类型模型==>通过reflection得到弱类型结构。因为java class作为元数据能够承载的信息量有限,reflection方法可能并不能充分揭示对象的结构,所以一般还要额外补充xml说明文件等。 因为我个人主要的工作都作在中间件层,所以我的做法是尽量使用Map和List等抽象数据结构,结合元数据对象,在需要强类型的时候通过对象封装来转化为 强类型。即从弱类型==>强类型。
例如:
class Work{
 public static final String KEY_NAME = "name";
 public static final String KEY_DESCRIPTION = "description";

 Map work;

 public String getName(){
  return (String)work.get(KEY_NAME);
 }

 public String getDescription(){
  return (String)work.get(KEY_DESCRIPTION);
 }

 public void setName(String name){
  work.put(KEY_NAME,name);
 }
...

 public Map toMap(){
  return work;
 }
}

posted @ 2005-11-14 17:02 canonical 阅读(218) | 评论 (0)编辑 收藏

JMX在技术上的需求可以说是将管理功能从功能性接口中分离出来。
例如一个缓存接口
interface ICache{
     Object get(Object key);
     void put(Object key, Object value);
}
但一个具体实现类可能有很多参数可以调整,如缓存的最大尺寸等。这些可配置参数一般与具体实现紧密相关,即与实例相关,而不直接涉及到所要实现的功能。例如实现类可以具有setMaxSize()和getMaxSize()方法。
如 果这些配置方法在功能接口中定义,就会造成功能接口的臃肿和不必要的与实现方法之间的依赖。如果直接调用实现类的方法,只能使用reflection, 但是java class作为元数据所承载的信息量有限,需要外部定义一个规范来补充信息。JMX就是这样的一种规范。

posted @ 2005-11-14 17:01 canonical 阅读(286) | 评论 (0)编辑 收藏

     分析学的离散形式是分而治之(Divide And Conquer)。 这一思想在软件设计领域的重要性不言而喻。 大系统分解为小系统,小系统分解为模块,模块分解为对象,对象分解为函数,函数分解为增删改查等动词和集合/个体等名词,如此递归下来。 在很多关于软件的"最佳实践"中,都列举了这种分解过程中的注意事项,如高内聚,低耦合等。 但是为什么要强调这些概念,谁能保证这个checklist是完整的, 具体实践过程中又当如何去做? 我们能否跳出软件的圈子,利用软件领域之外的词语重新表述一下这种思想?将大的系统分解为多个小的系统之后, 因为系统规模变小, 处理起来一般会容易一些, 但这是否就是分析学的全部?
     有一个小故事,说有人问一个科学家,如果地球文明将要毁灭,只有一句话可以传给后人,那他最想告诉后代的是什么。那个科学家回答:宇宙万物都是由原子组成 的。分析学的哲学基础是还原论,原子论可以说是数千年来还原论最辉煌的胜利。原子论也清楚的向我们揭示了分析学的奥秘:千变万化仅是事物的表象,分解之后 它们都由同质的基元构成。在分解的过程中,问题的规模越来越小,问题的数目似乎越来越多,但当问题空间因为某种原因"塌缩"的时候,分解后的子问题出现大 量的重叠,整个问题的复杂性出现了本质性的降低。FFT和动态规划算法中所采用的也正是这种从异质到同质的解决方案。
      分解之后,我们希望得到的子系统是低耦合的,那么最好是完全不相关的,我们希望得到的子系统是高内聚的,那么最好是不可分的,在数学上,我们称之为正交。 还原论最完美的载体是线性世界,而线性代数(或更广义的群论)说了,线性系统完全由其正交的特征向量所构成的"核"(kernel)来刻画,那大体上软件 系统应利用少数可重用的模块来构建。但线性代数又说了,特征向量的选择方式是无穷多的,而且完全等价, 那大体上软件系统的分解方式也是多种多样的, 多数很难分出优劣。  线性代数还说, 特征向量个数仅由系统的维度(系统复杂性的一种度量)来决定, 那大体上软件系统无论怎么分解, 总有一个复杂性的下限。过分简单的架构仅能支持过分简单的应用。线性代数没有明说,但潜在的表达着,特征向量的地位是平等的,所以在高内聚,低耦合的基础 上,软件分解的原则中至少还要增加一条:对称性, 以维护系统整体结构的平衡。
      很可惜,现实世界中发现了越来越多的非线性现象,以致于非线性研究本身已经成为了一门独立的学科。不过,古老的教诲仍然有效,分解可以帮我们找回系统的线 性。在微积分所描绘的极限情形中,外力产生了加速度,然后加速度产生了速度,因与果就这样实现了分离。(有人说,重整化方法在微观世界的成功正是因为在极 度纠缠的临界情况下微积分失效了,也许有些道理)。

      为了在软件中实施分析学,我们需要一些技术手段。首先,需要一种命名机制,使我们能够在思想中定义概念,并开始建模。所谓的对象,正是这样一种机制。可以从以下的级列关系来理解这一点
1. 高级语言规定了数据的类型,使得我们可以为不同的内存块指定不同的数据类型,从而在概念上对它们作出区分。
2. 当程序变得渐渐复杂起来,C语言提供的Struct结构体,使我们可以创建新的数据类型,可以将一组相关的数据放在一起,起个名字。而如果没有 结构体,这种相关性就无法直接在程序中得到表达,必须纪录在文档中或者程序员的思想中。
3. 对象(Object)是比结构体(Struct)更加强大的命名机制,它可以将一组相关的数据和函数放在一起,起个名字。而且通过封装和虚拟函数,一个对 象类型所表达的 不仅仅是它自身所代表的概念,它同时表达了它的派生类所具有的特征。即对象所表达的是一个概念的集合而不是一个单独的概念。
4. 更复杂的程序中,对象之间的相互作用产生了某种确定的特征,出现了设计模式。
5. 这个级列的下一步是什么?


       对象化没有什么神秘的地方,它只是使我们拥有了一种表述的工具。有时对象化比不对象化更遭,因为我们极有可能犯命名的错误。

     在没有对象的概念的日子里,我们无法命名数据和函数的耦合,一些概念也就无法在软件设计中得到自然的表达,因为它们在程序的世界中没有名字!一旦我们能够 命名系统中所有的概念,一扇门就被打开了,大量的可能性被发掘出来,形成了今天的面向对象技术。这其中最重要的就是软件中的正交分解技术。首先是继承。在 早期的C程序中,经常出现如下的代码:
if a then
   a_work_1();
else if b then
   b_work_1();
end

if a then
   a_work_2();
else if b then
   b_work_2();
end

通过继承,我们可以捕获以上程序中的关联性,代码被改写为如下方式
x = a or b;
x.work_1();

x.work_2();

但作为早期最主要的面向对象技术,很快继承这个概念就不堪重负。通过继承,系统中的所有关系被组织成了一个树状结构。随着树的层次越来越深,整个结构变得越来越不稳定,基类的小小变动随时可能会造成雪崩似的影响。作为一个整体,对象也越来越难以被重用。

       此时,接口(Interface)应天命而生。从简单的意义上来理解,接口可以被认为是对对象(Object)的正交分解。如果使用继承,
class CHuman {
 public void eat(){..} // human eat
 public void sleep(){..} // human sleep
}
class CManager extends CHuman {
 public void fireEmployee() { ...} // manager fire employee
};
class CEmployee extends CHuman {…}
   公有继承大致上对应于"is a" 关系, 即一种包含关系,在数学上称为偏序(Partial Order)。
偏 序在逻辑上隐含的是一种推理,即我们可以根据基类的行为我们可以推论派生类的行为。所以当我们知道某人是经理(CManager)的时候, 我们可以推论出他是一个人,即他能吃能睡。很可惜,这种微妙的信息泄漏也许并不是我们所希望了解的,毕竟董事会雇佣一个职业经理人来为的是管理而不是吃 饭。
      应用组件技术,我们进行如下建模:
interface IHuman {
 bool eat();
 bool sleep();
};
interface IManager{
 bool fireEmployee();
};
class Manager implements IHuman, IManager{…};

Manager = IHuman + IManager


    接口打破了继承所构建的僵化的树状结构,提倡灵活的网状结构,使得整个系统结构扁平化,分解的粒度也更小。有了接口,是否就应该忘了继承呢?不,推理关系仍然是重要的,只是不要滥用。

    最近几年,面向方面编程(AOP)逐渐兴起。从分解技术的角度上看,它代表了一个新的方向:形容词与动词的正交分解。例如,我们需要在一个事务中实现转账,
 实现转账这个功能可以很容易的编写, "在一个事务中"这一修饰语被抽象出来称为一个Aspect, 并单独实现。通过AOP技术,我们将动作与所需要的修饰组合起来,完成所需要的功能。

      最后,谈一谈Reusablity这个概念.
软 件设计是从需求领域到软件技术实现领域的一系列模型映射,在每一个层面上都存在着多种正交分解方式。构建软件的目的是为了满足需求,所以整个映射过程应该 向着应用层倾斜。有一个说法叫做Object oriented to user, 我是从科泰世纪的陈榕那里听来的。 我想这也正强调了从多种分解方式中作出选择的准则。 可重用的对象意味着它更可能成为构建系统的"特征基元", 同时它的可用性隐含的表达了对应用层用户的意义。 所以Reusability是一个比Objectlization和Encapsulation更为重要的一个概念。

posted @ 2005-11-14 17:00 canonical 阅读(503) | 评论 (0)编辑 收藏

    最近几年关于模型的提法突然多了起来,但这个概念到底意味着什么呢。从哲学上说,我们的思想是外部世界结构在主观意识中的反映,当我们把主观意识再投射回 外部世界时,就得到关于外部世界的模型。所以,在最广泛的意义上,模型不过是我们思维中的一组关联。问题不在于我们是否需要模型或者什么东西是模型而什么 东西不是模型,我们所意识到的一切都是模型,无论它与真实情况的差距有多大。我们所能区分的只是什么是一个"好"的模型,什么是一个"坏"的模型。在面向 对象建模中,经常听到人说XX是对象,YY不是对象。这是一种错误的提法。所有的东西都是对象,都是我们心智中可以操纵的符号。一张纸是对象,当它被撕成 了碎片,每个碎片也是对象。因为受力方式等随机因素的干扰,信纸碎裂的方式也是随机的, 最终造成的碎片也是随机的。我们可以说信纸L这个对象由碎片A,B,C这三个子对象构成,也可以说L由碎片D,E,F构成。定义对象的方式实在是无穷无 尽。

     我们通过比较来认知外部世界。当我们面对一个不熟悉的概念的时候,我们总是在把它分解,重构为我们已经熟悉的概念以后才能真正的理解它。也许,我们只能理 解那些我们已经知道的东西!我们已在这个星球上生存了亿万年,以至于我们总可以将那些"新"的结构与我们的"先验知识"做比较。只有当面对量子论和相对论 的时候,一个人才能真正意识到自己的知识是多么的贫乏,一种先天的贫乏。Cantor将比较的技术发挥到了极致,他通过比较区分了可数无穷大与不可数无穷 大,结果他疯了。
假设现实有一个结构S, 我们的模型具有结构M。 当S(通过一定的简化和拓扑变换)能够与M匹配时,我们就说现实S被理解了。 更一般的, 我们从不同的角度或者在不同的层面上看待同一个事物, 从而形成模型M1, M2, M3,...   我们可以通过一条逻辑途径M1-->M2-->M3-->S来理解S. 最好的模型应该构成到现实结构S的最短路径。一个鲜活的例子就是数据库。在von Neumann体系架构下,计算机能够直接回答的问题是:存放在0x***地址的值是多少(M1)。而在现实中,我们经常需要回答的问题是:  值大于**的纪录有那些(M2)。这些问题是关于变量的值的,而不是变量的地址。数据库的价值正在于通过索引提供了M1->M2的映射。早期的层次 数据库仅仅提供了一个中间模型H, H->M2的映射还需要程序员来完成,最终难免被淘汰的命运。一个模型就这样创造了一个产业。
    模型映射的最短路径意味着我们最好不要去创建那些"聪明"的新结构.在一个良好设计的系统中,我们不应该费力去"发现"什么或去认知什么。设计不等于创 造,一个最好的设计应该是抄袭成功的范例,最好是连改动也没有,可惜现实的多变总要你多少作些改变。如果需要改变,最好是局部化的改变,在数学上我们称之 为同态。如果在映射的时候,无法做到局域化,那往往意味着出现了一些本质的困难。例如html语言中的table元素。表格本质上是二维布局,而html 文本是一维的文本流,它们之间存在着深刻的差异。当某个表格单元的rowspan或者colspan属性需要修改时,对应于界面表现只是一个局部调整,而 反映到html代码中却是全局的修正。xml决不是万能的。
 
    某个事物被清楚的理解了,是因为我们对它已经有了一个完整的模型M,所以对已知的事物建模是相当容易的,因为我们可以从模型M出发,建立一个同态甚或是同 构的模型即可。所有的困难都在于如何对那些我们所知甚少抑或是一无所知的东西建模。按照Laplace的哲学,如果我们不知道什么是最好的选择,那么所有 的选择都是等价的。在用迭代法解方程的时候,通常的做法是抛一个随机的初始解进去,只要控制策略得当,最终会收敛到正确的解上。一切并不在于选择初始解的 启发式策略有多么的"聪明", 随机性才是创造性的根源,因为只有几率才能打破因果的枷锁。一个好的初始解一般只是缩短收敛的过程,但并不影响最终收敛的结局。
     一个模型首先提供的是一个术语体系或者说一个词汇表,一种语言。使得我们能够以一种一致的方式捕获那些转瞬即逝的思想。模型本身是否正确其实并不一定如想 象中的那么重要。一旦初始的模型建立,我们的认知就有了一个不断发展和积累的基础,最终就可能得到超越我们最初期望的结果。交流和表达具有根本的意义,优 劣与否只在其次。设计模式提出的时候引起轰动,到今天,当一切尘埃落定,它最大的贡献也许只是贡献了一批术语和模式名称,让大家可以在模式的框架下讨论问 题。恰如陈省身的纤维丛理论。也许这个世界上最成功的建模案例是宗教。通过一次提问,谁干的,就把所有未知的问题一揽子解决了。伟大的简化!借助宗教的术 语,我们创作了文学,纪录了思想,传承了知识,甚至建立了政治秩序。所以如果你不知道从哪里开始的话,那就随便吧。

posted @ 2005-11-14 16:59 canonical 阅读(638) | 评论 (2)编辑 收藏

仅列出标题
共18页: First 上一页 10 11 12 13 14 15 16 17 18