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

  AOP(Apsect Oriented Programming)概念的正式出现也有一些时日了,但是它在程序构造过程中似乎仍未找到合适的切入点,一般系统的设计实现很少将AOP作为必要的技术元素。AOP作为一种普适的技术思想,它所代表的是程序结构空间中的定位和组装技术。http://canonical.javaeye.com/blog/34941 AOP使我们可以通过非侵入性的方式动态修改“任意”已经构建好的程序,而不需要事前有大量的设计准备。原则上说,这种技术思想是可以在任何程序语言基础上进行表达的,并不是只有java, C#这样的面向对象语言才允许AOP操作. Witrix平台中所应用的部分技术与AOP有些类似,只是大量的结构调整表现为xml生成和xml变换,在具体的使用方式上也有一些微妙的差异。http://canonical.javaeye.com/blog/126467

  相对于通用程序语言,xml语言其实是AOP技术的一个更加合适的形式载体。
1. xml格式特殊的规范性确保了在最细的逻辑粒度上,程序结构也是可识别的,可操纵的(在这一点上非常类似于函数式语言)。而所有的命令式语言(imperative language)中,函数内部的结构都是很难采用统一方式进行描述和定位的。
   <ns1:loop>
     
<rpt:Row/>
   
</ns1:loop>

2. xml节点的坐标可以采用xpath或者css选择符等通用方式进行描述,而一般程序结构无法达到xml格式这样的均一性,其中的坐标定位方式要复杂得多。
3. xml节点上可以增加任意属性,不同的属性可以属于不同的命名空间(namespace),这些属性可以辅助AOP的定位机制。而一般程序语言中如果没有Annotation机制, 则定位只能依赖于函数名和类名(函数参数只有类型没有名称),而类名和函数名随时可能因为业务变化而调整(不是专为定位而存在), 由此构建的切点描述符是不稳定的。
 
<ui:PageTable pager="${pager}" cache:timeout="1000" />
4. xml节点的增删改查显然要比字节码生成技术要简单和直观得多。
   

   AOP技术难以找到应用的一个重要原因在于很多人机械式的将它定位为一种横切技术,认为它的价值完全在于某个确定的切面可以插入到多个不同的切点,实现系统的横向分解。而在实际应用中,业务层面上很少具有可抽象的固定的共同性,我们所迫切需要的一般是对已有程序结构进行动态扩展的一种能力。横切是AOP的一种特殊的应用,但不是它的全部。相对于继承(inheritance)等依赖于概念诠释的结构扩展机制,AOP所代表正是对程序结构空间进行任意操纵的一种能力。AOP可以为基础结构增加功能,改变原有功能实现,也可以取消原有功能实现,它不需要把所有的扩展逻辑按照树形结构进行组织,不要求在基础结构中为扩展编写特殊的代码。这种自由的结构扩展能力在Witrix平台中被发展为“实现业务代码与平台基础架构之间的动态融合”。

   在Witrix平台的实际应用中,AOP的切点匹配能力并不是十分重要。一般情况下我们主要通过整体结构规划来确保控制点意义明确且相对集中,因此不需要额外通过切点匹配进行业务功能的再组织,不需要再次从杂乱的程序逻辑中重新发现特殊的控制点。例如在Witrix平台的Jsplet框架中所有后台事件响应都通过objectName和objectEvent参数触发,在触发后台事件响应函数之前都会调用bizflow文件中的beforeAction段。
   在bizflow文件中,aop操作是明确指定到具体函数的,使用模糊匹配在一般情况下只会使问题变得不必要的复杂化。例如扩展actQuery函数
   <action id="aop-Query-default">
    
<source>
       通过自定义标签抽象出多个Action之间的共用代码
      
<app:DoWorkA/>
    
</source>
  
</action>

   
   在Witrix平台中结构组装主要是通过自定义标签库和extends算子来实现,它们都依赖于xml格式的规范性。
1. 通过在custom目录下实现同名的自定义标签,即可覆盖Witrix平台所提供的缺省标签实现,这里所依赖的并不是复杂的匹配过程,而是自然直观的映射过程。http://canonical.javaeye.com/blog/196826
2. 所有的xml配置文件支持extends操作,它允许定制两个具有业务含义的xml节点之间的结构融合规则。例如<biz-flow extends="docflow">。

   实际使用中, AOP技术的一个应用难点在于状态空间的管理问题。一般interceptor中所能访问的变量局限为this指针所携带的成员变量,以及函数调用时传入的调用参数。interceptor很难在状态空间中创建新的变量,也很难读取在其他地方所产生的状态变量。例如对于如下扩展 A(arg1); B(arg2); C(arg3); =〉 Ax(arg1); B(arg2); Cx(arg3); 因为原有的调用序列中没有传递额外的参数,因此A和C的扩展函数之间很难实现共享内部变量x。在TPL模板语言中,tpl本身是无状态的,状态变量通过外部的$thisContext对象统一管理。通过这种行为与状态的分离,结合灵活的变量作用域控制机制,可以以比较简单的方式实现扩展函数之间的信息共享。

posted @ 2008-07-07 00:12 canonical 阅读(1665) | 评论 (0)编辑 收藏

    说到分解,很多人心中的意象大概只有正交分解。正交分解无疑是最重要的一种分析方法,它也是所谓“分而治之”思想最常见的实现策略。但是正交分解一般潜在的假定是分解后的子部分是大致均衡的,它们是相对具有独立价值的,可以彼此脱离独立发展。这是分解后实现系统解耦的重要原因。http://canonical.javaeye.com/blog/33885 但是物理学中另一种重要的分析学思想是微扰论(Perturbation). 针对一个复杂的物理现象,首先建立一个全局的规范的模型,然后考虑各种微扰条件对原有模型的影响。在小扰动情况下,模型的变化部分往往可以被线性化,被局域化,因而问题得到简化。微扰分析得到的解依赖于全局模型的解而存在,因而这是一种主从关系的分解方式。但是如果主体模型是我们已经熟知的物理现象,则我们关注的重点可以全部放在扰动解上,认为所有特定的物理规律都体现在扰动解中。如果微扰分析得到的物理元素足够丰富,则微扰模型本身可以成为独立的研究对象,在其中我们同样可以发现某种普适的结构规律。
    Witrix平台中系统化的应用主从分解模式,通过类似AOP的技术实现了业务模型与平台技术的自然结合。http://canonical.javaeye.com/blog/126467 最近我们的一个产品的新版本即将在全国范围内部署,如何有效的控制众多相近的二次开发版本,同时确保主版本的快速升级,是在架构层面必须解决的问题。http://canonical.javaeye.com/blog/73265 在Witrix平台中,各部署版本并不是直接修改主版本源代码得到,而是将差异化代码放在单独的目录中进行管理,由系统运行平台负责将差异化定制代码与主版本代码进行动态融合,实现部署版本的客户化。在这一过程中,系统模型本身支持逆元结构至关重要,否则某些多余的元素无法通过差异性描述去除,则将出现局部模型失效的情况。
    Witrix平台定义了特殊的_custom目录,它的内部目录结构与defaultroot目录相同,系统平台优先使用该目录下文件所提供的功能实现。同时定义了系统参数global.app_id和global.default_app_id,它们分别用来区分当前程序版本以及程序主版本代码。例如当global.app_id=beijing,global.default_app_id=main的时候,系统中装载ui.xml这个标签库时经历如下过程,
1.    装载平台内置的标签库,文件路径为 /_tpl/ui.xml.
2.    根据global.default_app_id设置,装载/_custom/main/_tpl/ui.xml, 其中定义的标签实现将覆盖平台缺省提供的标签实现。对于那些不需要特殊定制的标签,继续使用平台提供的缺省实现。
3.    根据global.app_id设置,装载/_custom/beijing/_tpl/ui.xml, 其中定义的标签实现将覆盖产品主版本的标签实现。

基础平台中对于代码动态融合定义了精细的融合策略,将通过编译技术检查扩展标签的接口与缺省实现的接口相兼容,由此确保代码扩展后不会破坏主版本中的已有调用代码。
    在基础平台的实现中,很多实现代码都是类似
          <df:WhenAllowFinishWf>
            
<df:FinishWfButton />
          
</df:WhenAllowFinishWf>

这样的类似废话的标签调用。但是通过这些标签的标记,我们确立了系统的逻辑结构,标定了系统中可以被安全替换的逻辑片断。

posted @ 2008-05-26 00:41 canonical 阅读(1715) | 评论 (0)编辑 收藏

     在与一些年岁较大的C程序员接触的过程中,可以比较明显的感受到C的思维方式与面向对象思想的不同。C的世界很清澈,先做A, 再做B, 我们所期待发生的计算过程与源代码的结构是直接一一对照的。这意味着程序将要执行的计算过程在编写代码的时刻就已经确定下来。面向对象首先需要确定的是类,对象等中间元素,而并不是最终的计算过程。对象可以之间可以产生很复杂的结构关系,透过这种中间逻辑结构我们来理解最终要发生的计算过程。在事件驱动的应用场景下,面向对象是一种更加有效的描述,
  o.someFunc()                  o.onEventA();
    sub1.someFunc();      ==>     sub1.onEventA();
    sub2.someFunc();              sub2.onEventB();
如果把对象看作是函数+状态的集合,则对象组装的关系实际上是函数集合之间的一种组装关系。当具体的事件发生的时候,将触发对象上确定的响应函数,此时在各个层面上所实际发生的计算才能被确定下来。



posted @ 2008-03-16 15:04 canonical 阅读(439) | 评论 (0)编辑 收藏

   所谓WebMVC即Model2模型是目前Web开发领域的主流模型,Struts/Struts2框架是其典型实现。在概念层面上,这种程序组织模型是怎样建立起来的?与其他Web开发模型(如面向对象模型)具有怎样的联系? 它未来可能的发展方向在哪里? 结合Witrix开发平台的具体实践,基于级列设计理论我们可以看到一条概念发展的脉络。http://canonical.javaeye.com/blog/33824

   1. 外部视角:原始的servlet规范提供了一个简单的面向IO的程序响应模型。一次前台访问由一个特定的servlet负责响应,它从request中读取输入流,在全局session中保持临时状态,向response中写入输出流。在此基础上,JSP提供的模板概念翻转了程序和输出文本之间的相对地位,简化了文本输出过程。至此,这种整体的程序模型基本上只是规范化了外部系统访问Web服务器的响应模型,并没有对后台程序的具体实现制定明确的约束条件。因此在最粗野的后台实现中,读取参数,业务处理,生成页面等处理步骤是纠缠在一起的,很难理解,也很难重用。每一个后台页面都是一个不可分析的整体。
<%
   String paramA 
= request.getParameter("paramA");
   ResultSet rsA 
= 
%>
   result 
= <%=rsA.getString(0%>
   String paramB 
= request.getParamter("paramB");
   ResultSet rsB 
= 
<%
   rsB.close();
   rsA.close();
   conn.close();
%>


2. 自发分离:在复杂的程序实践中,我们会自发的对业务处理代码和界面代码进行一定程度的分离。因为我们可以直观的感受到这两种代码的稳定性并不匹配。例如不同业务处理过程产生的结果都可以用一个html表格来展现,而同一个业务处理过程产生的结果页面可能经常发生变化。一般我们倾向于将业务代码写在页面上方,而界面代码写在页面下方,并使用一些原始的分解机制,例如include指令。这种分离是随意的,缺乏形式边界的。例如我们无法表达被包含的页面需要哪些参数,也难以避免全局变量名冲突。需要注意的是,分层的一般意义在于各个层面可以独立发展,它的隐含假定是各层面之间的交互是规范化的,只使用确定的数据结构,按照确定的方式进行交互。例如业务层和界面层通过标准的List/Map等数据结构交互,而不是使用具有无限多种样式的特殊的数据结构。(在弱类型语言环境中,实体对象的结构和Map是等价的).
<%
   List header 
= 
   List dataList 
= 
%>
<%@ include file="/show_table.jsp" %>


   3. 规范分离:JSP所提供的useBean和tag机制,即所谓的Model1模型,是对程序结构分离的一种规范化。业务代码封装在java类中,一般业务函数与web环境无关,即不使用request和response对象, 允许单元测试。tag机制可以看作是对include指令的增强,是一种代码重用机制。tld描述明确了调用tag时的约束关系。调用tag时需要就地指定调用参数,而include页面所依赖的参数可能是在此前任意地方指定的,是与功能实现分离的。此外tag所使用的参数名是局部对象上的属性名,从而避免了对全局变量的依赖。很遗憾的是,jsp tag所封装的仍然是原始的IO模型,对程序结构缺乏精细的定义,在概念层面上只是对文本片段的再加工,难以支撑复杂的控件结构。早期jsp tag无法利用jsp模板本身来构造,无法构成一个层层递进的概念抽象机制,更是让这种孱弱的重用模型雪上加霜。在其位却无能谋其政,这直接造成了整个j2ee前台界面抽象层的概念缺失,以致很多人认为一种前台模板重用机制是无用的。在Witrix平台中所定义的tpl模板语言,充分利用了xml的结构特点,结合编译期变换技术,成为Witrix平台中进行结构抽象的基本手段。实际上,xml能够有效表达的语义比一般人所想象的要多得多。
 <jsp:useBean id="myBiz" class="" />
  
<% List dataList = myBiz.process(paramA) %>
  
<ui:Table data="<%= dataList %>" />

  
  4. 框架分离:在Model1模型中,页面中存在着大量的粘结性代码,它们负责解析前台参数,进行类型转换和数据校验,定位特定的业务处理类,设置返回结果,控制页面跳转等。一种自然的想法是定义一个全局的程序框架,它根据集中的配置文件完成所有的粘结性操作。这也就是所谓面向action的WebMVC模型。这一模型实现了服务器端业务层和界面层在实现上的分离,但是对于外部访问者而言,它所暴露的仍然是原始的自动机模型:整个网站是一个庞大的自动机,每次访问都触发一个action,在action中可能更改自动机的状态(作为全局状态容器的session对象或者数据库)。struts作为面向action框架的先驱,它也很自然的成为了先烈。struts中所引入的FormBean, 链接管理等概念已经在实践中被证明是无益的。一些新兴的框架开始回归到通用的Map结构,直接指定跳转页面,或者利用CoC(Convention Over Configuration)缺省映射.
public class RegisterAction extends Action {
    
public ActionForward perform (ActionMapping mapping,
                                  ActionForm form,
                                  HttpServletRequest req,
                                  HttpServletResponse res)
{
    RegisterForm rf 
= (RegisterForm) form;
    
    
return mapping.findForward("success");
}

  
5. 横向延展:分层之后必然导向各个层面的独立发展,我们的视野自然也会扩大到单个页面之外,看到一个层面上更多元素之间的相互作用.在面向对象语言大行其道的今天,继承(inheritance)无疑是多数人首先想到的程序结构组织手段.后台action可以很自然的利用java语言自身的继承机制,配置文件中也可以定义类似的extends或者parent属性.但是对于前台页面一般却很少有适用的抽象手段,于是便有人致力于前台页面的对象语言化:首先将前台页面采用某种对象语言表达,然后再利用对象语言内置的结构抽象机制.放弃界面的可描述性,将其转化为某种活动对象,在我看来是一种错误的方向.而JSF(JavaServerFace)规范却似乎想在这个方向上越走越远.JSF早期设计中存在的一个严重问题是延续了面向对象语言中的状态与行为绑定的组织方式.这造成每次访问后台页面都要重建整个Component Tree, 无法实现页面结构的有效缓存.而Witrix平台中的tpl模板语言编译出的结构是无状态的,可以在多个用户之间重用.

  6. 相关聚合:对象化首先意味着相关性的局域化,它并不等价于对象语言化. 当面对一个大的集合的时候,最自然的管理手段便是分组聚合:紧密相关的元素被分配到同一分组,相关性被局域化到组内.例如,针对某个业务对象的增删改查操作可以看作属于同一分组. struts中的一个最佳实践是使用DispatchAction, 它根据一个额外的参数将调用请求映射到Action对象的子函数上.例如/book.do?dispatchMethod=add. 从外部看来,这种访问方式已经超越了原始的servlet响应模型,看起来颇有一些面向对象的样子,但也仅仅局限于样子而已.DispatchAction在struts框架中无疑只是一种权宜之计,它与form, navigation等都是不协调的,而且多个子函数之间并不共享任何状态变量(即不发生内部的相互作用),并不是真正对象化的组织方式.按照结构主义的观点,整体大于部分之和.当一组函数聚集在一起的时候,它们所催生的一个概念便是整体的表征:this指针.Witrix平台中的Jsplet框架是一个面向对象的Web框架,其中同属于一个对象的多个Action响应函数之间可以共享局部的状态变量(thisObj),而不仅仅是通过全局的session对象来发生无差别的全局关联.http://canonical.javaeye.com/blog/33873 需要注意的是,thisObj不仅仅聚集了后台的业务操作,它同时定义了前后台之间的一个标准状态共享机制,实现了前后台之间的聚合.而前台的add.jsp, view.jsp等页面也因此通过thisObj产生了状态关联,构成页面分组.为了更加明确的支持前台页面分组的概念,Witrix平台提供了其他一些辅助关联手段.例如标准页面中的按钮操作都集中在std.js中的stdPage对象上,因此只需要一条语句stdPage.mixin(DocflowOps);即可为docflow定制多个页面上的众多相关按钮操作.此外Witrix平台中定义了标准的url构建手段,它确保在多个页面间跳转的时候,所有以$字符为前缀的参数将被自动携带.从概念上说这是一种类似于cookie,但却更加灵活,更加面向应用的状态保持机制.

  class DaoWebAction extends WebContext{
     IEntityDao entityDao;
     String metaName;

     
public Object actQuery(){
       
       thisObj.put(
"pager",pager);
       
return success();
     }

     
public Object actExport(){
       Pager pager 
= (Pager)thisObj.get("pager");
       
       
return success();
     }
    }


  7. 描述分离:当明确定义了Action所聚集而成的对象结构之后,我们再次回到问题的原点:如何简化程序基元(对象)的构建?继承始终是一种可行的手段,但是它要求信息的组织结构是递进式的,而很多时候我们实际希望的组织方式只是简单的加和。通过明确定义的meta(元数据),从对象中分离出部分描述信息,在实践中被证明是一种有效的手段。同样的后台事件响应对象(ActionObject),同样的前台界面显示代码(PageGroup),配合不同的Meta,可以产生完全不同的行为结果, 表达不同的业务需求。http://canonical.javaeye.com/blog/114066 从概念上说,这可以看作是一种模板化过程或者是一种复杂的策略模式 ProductWebObject = DaoWebObject<ProductMeta>。当然限于技术实现的原因,在一般框架实现中,meta并不是通过泛型技术引入到Web对象中的。目前常见的开发实践中,经常可以看见类似BaseAction<T>, BaseManager<T>的基类,它们多半仅仅是为了自动实现类型检查。如果结合Annotation技术,则可以超越类型填充,部分达到Meta组合的效果。使用meta的另外一个副作用在于,meta提供了各个层面之间新的信息传递手段,它可以维系多个层面之间的共变(covariant)。例如在使用meta的情况下,后台代码调用requestVars(dsMeta.getUpdatableFields())得到提交参数,前台页面调用forEach dsMeta.getViewableFields()来生成界面. 则新增一个字段的时候,只需要在meta中修改一处,前后台即可实现同步更新,自动维持前后台概念的一致性。有趣的是,前后台在分离之后它们之间的关联变得更加丰富。

8. 切面分离: Meta一般用于引入外部的描述信息,很少直接改变对象的行为结构。AOP(Aspect Oriented Programming)概念的出现为程序结构的组织提供了新的技术手段。AOP可以看作是程序结构空间中定位技术和组装技术的结合,它比继承机制和模板机制更加灵活,也更加强大。http://canonical.javaeye.com/blog/34941 Witrix平台中通过类似AOP的BizFlow技术实现对DaoWebAction和前台界面的行为扩展,它可以在不扩展DaoWebAction类的情况下,增加/修正/减少web事件响应函数,增加/修正/减少前台界面展现元素。当前台发送的$bizId参数不同的时候,应用到WebObject上的行为切片也不同,从而可以自然的支持同一业务对象具有多个不同应用场景的情况(例如审核和拟制)。在BizFlow中定义了明确的实体化过程,前台提交的集合操作将被分解为针对单个实体的操作。例如前台提交objectEvent=Remove&id=1&id=2,将会调用两次<action id="Remove-default">操作。注意到AOP定位技术首先要求的就是良好的坐标定义, 实体化明确定义了实体操作边界,为实体相关切点的构造奠定了基础。http://canonical.javaeye.com/blog/33784

9. 背景消除:在Witrix平台中, (DaoWebAction + StdPageGroup + Meta + BizFlow)构成完整的程序模型,因此一般情况下并不需要继承DaoWebAction类,也不需要增加新的前台页面文件,而只需要在BizFlow文件中对修正部分进行描述即可。在某种程度上DaoWebAction+StdPageGroup所提供的CRUD(CreateReadUpdateDelete)模型成为了默认的背景知识。如果背景信息极少泄漏,则我们可以在较高抽象层次上进行工作,而不再理会原始的构造机制。例如在深度集成hibernate的情况下,很少会有必须使用SQL语句的需求。BizFlow是对实体相关的所有业务操作和所有页面展现的集中描述,在考虑到背景知识的情况下,它定义了一个完整的自给自足的程序模型。当我们的建模视角转移到BizFlow模型上时,可以发展出新的程序构造手段。例如BizFlow之间可以定义类似继承机制的extends算子,可以定义实体状态驱动的有限自动机,可以定义不同实体之间的钩稽关系(实体A发生变化的时候自动更新实体B上的相关属性),也可以定义对Workflow的自然嵌入机制。从表面上看,BizFlow似乎回归到了前后台大杂烩的最初场景(甚至更加严重,它同时描述了多个相关页面和多个相关操作),但是在分分合合的模型建立过程中,大量信息被分解到背景模型中,同时发展了各种高级结构抽象机制, 确保了我们注意力的关注点始终是有限的变化部分。而紧致的描述提高了信息密度,简化了程序构造过程。http://canonical.javaeye.com/blog/126467
  <bizflow extends="docflow"> <!-- 引入docflow模型,包括一系列界面修正和后台操作 -->
<biz id="my">
     
<tpls>
       
<tpl id="initTpl">
          
<script src="my_ops.js" ></script>
          
<script>
            stdPage.mixin(MyOps); // 引入多个页面上相关按钮对应的操作
          
</script>
       
</tpl>
     
</tpls>
    
</biz>
  
</bizflow>



posted @ 2008-02-18 22:02 canonical 阅读(1787) | 评论 (0)编辑 收藏

  自从离开学校就基本上不再使用C++了,最近却又因为项目上的原因重新走入这一迷失的世界, 感觉很是缺乏一些顺手的工具。首先就是做配置管理有点麻烦, 因为缺乏反射机制, 无法直接映射, 所以一般需要手工书写配置设置功能.
  我们希望配置类在配置阶段能够支持动态属性名,
  GConfig cfg;
  cfg.set(
"bgColor.b",3.0);
  cfg.set(
"lightEnabled",false);

  t_float b 
= cfg.get("bgColor.b");
  bool l 
= cfg.get("lightEnabled");

    但是内部使用时支持直接的属性访问,便于编译器检查, 也提高运算速度。
        t_float b = cfg.bgColor.b;
        bool l 
= cfg.lightEnabled;


所幸C++的类型系统能够偷偷的去干很多见不得人的勾当,因此便有了下面这个简易机制。

#define S_P(x) do{if(strcmp(name,#x) == 0) { x = value; return; } } while(0)
#define G_P(x) 
do{if(strcmp(name,#x) == 0) { value = x; return; } } while(0)

class _GConfig{
public:
  bool lightEnabled;

  t_float minX;
  t_float maxX;
  t_float minY;
  t_float maxY;

  _GConfig(){
    
// initialize all primitive members
    memset(this,0,sizeof(_GConfig));
  }
};

class GConfig: public _GConfig{
public:
  GColor bgColor;

  GConfig(){
  }

  _variant_t get(
const char* name){
    _variant_t value;
    get(name,value);
    
return value;
  }

  
void get(const char* name,_variant_t& value){
    G_P(lightEnabled);

    G_P(minX);
    G_P(maxX);
    G_P(minY);
    G_P(maxY);
   
    G_P(bgColor.r);
    G_P(bgColor.g);
    G_P(bgColor.b);
    G_P(bgColor.a);
  }

  
void set(const char* name, _variant_t value){
    S_P(lightEnabled);

    S_P(minX);
    S_P(maxX);
    S_P(minY);
    S_P(maxY);
   
    S_P(bgColor.r);
    S_P(bgColor.g);
    S_P(bgColor.b);
    S_P(bgColor.a);
  }
};

_variant_t是VC++在<comdef.h>中提供的对变体数据类型的封装。使用S_P和G_P这样的宏可以由编译器检查变量名的正确性。

posted @ 2008-01-12 20:58 canonical 阅读(1776) | 评论 (0)编辑 收藏

    关系数据库模型在理论上主要解决的是消除数据冗余的问题。关系模型的数学基础是所谓的集合论,而集合的基本含义正是一组具有某种原子性的互不相同的元素。面向对象技术是对相关性进行局域化的一种手段(相关的数据和操作聚集到同一对象名义下),在这一局域化过程中,相同的元素被识别出来,成为独立的对象。从某种意义上说,关系模型与对象模型是殊途同归的过程,是从不同侧面对同一事物的反映。关系模型中,我们关注的重点是元素组成的集合,允许的连接关系定义在集合之上。而在对象模型中,我们关注的首先是横向关联的实体,实体之间具有稳定的联系。在概念层面上,从对象模型映射到一种关系存储模型只是一个分组问题。为了断开实体之间的直接联系,关系模型创造了一个id字段,而对象模型并不是需要显式id的。在关系模型中,关联并不是通过某种存在的结构来表达的(一个实体持有另一个实体的指针,拥有直接联系),而是将直接关联问题弱化为某种计算过程,我们必须检查id的值(不是某种直接的存在性),通过某种运算过程才能重新发现数据之间的关联。
   
    通过id(伴随一个匹配计算过程)来进行间接关联对于保证模型的一致性是非常关键的。在ORM中恢复了对象的强关联其实会造成很多潜在的复杂性。例如为了维护对象层面结构的一致性,在更新父子关系的时候,我们需要同时调用 child.setParent(parent); parent.getChildren().remove(child); 当关联结构更加复杂的时候,这里所需要的维护工作是随之增加的。不过,在ORM中,对象的形态是暂时性的。在ORM的一次session的操作过程中,对于对象状态的修改可以是不一致的。例如我们可以只调用child.setParent(parent); 而不需要同时  parent.getChilren().remove(child); 只要我们在此次session操作中,不需要同时用到parent.getChildren(). 这种关联的暂时性对于很多ORM应用来说是必不可少的。
   
    对象模型中可以直接表达的结构关系比关系模型要丰富一些,例如继承关系,many-to-many, one-to-list等。但是所有这些都不是真正本质性的差异。抛弃概念诠释,基类与众多派生类之间的关系基本上可以等价于一组one-to-one关系。而当关联对象本身的重要性凸现出来的时候,当我们无法把它约化为对象上的一些附属特性的时候(例如数组的下标),我们必然要建立相应的关联对象,而这正对应于关系模型中的中间关联表。中间关联表上增加额外的字段是一个自然的扩展过程,而对象模型上做这样的扩充往往表现为形态上的重大的不兼容的变化,例如从getManyToManyEntity() -> getToManyRelation(), 这实际上意味着这里的对象形式是偶然的,简化的。
   
    在原始的关系数据库模型中,所有的表之间的地位是平等的,所有字段之间的地位是平等的(主键和外键在参与数据关联时和其他字段的处理方式一致)。这种概念上的均一性和普遍性往往被认为是理论的优美之处。但是现实世界是复杂的,发展的方向就是逐步识别出不同之处,并找出自然的表达形式将这些不同表达出来。均匀的关系模型是对称性最高的,最简化的模型。在面对物理约束时,它隐含的假设是集合之间很少发生相互作用,单表(表单到数据表之间的映射)和主从表是最广泛的情况。试着想象一下关系模型,在思维中一般我们只能看到两个数据表,当考虑到多个表的时候,因为这些表之间没有明确的可区分性,因此它们的意象是模糊的。只有明确意识到主键,外键,主表,从表,字典表,事实表,纬度表这些不同的概念的时候,当对称性出现破缺的时候,我们思维中的模型才能够丰富化起来。
   
    关系模型理论应用到数据库具体应用中时,并不需要死守关系范式教条,它们只是描述了某种极端化的对唯一性的追求。面对具体应用的时候,理论本身也在不断丰富化。我并不把现实应用中必然需要增加冗余字段看作是关系理论失效的结果。从关系完全分解,到关系完全不分解之间,我们可以建立大量的模型。建立冗余字段的时候,我们存在着大量可能的选择,到底哪一种选择是最优的,理论方面仍然可以给我们以具体的指导。理论在各种不同纯化程度的关系模型中都可以给我们以直观的建议。数据仓库理论中建立的snowflake模式和star模式,强调了针对主题域的允许部分冗余的关系分解。这里实际上是强调了表之间的不同性。不再是所有的表都处于同一地位。Fact Table和Dimension Table之间的区别被识别出来,并被明确处理。在我看来,这是原始关系模型的一种自然发展,它也是关系模型理论的一部分。理论不应该是单一的,而是提供一个模型级列,在不同的复杂性层次上,我们可以根据理论的指导选择具体的实现模型。
   
    关于ORM http://canonical.javaeye.com/blog/111500
    关系模型中的所谓关系是在使用时刻才定义的,所有建立关系的方式在某种程度上都是等价的,也是外在的。而在ORM中主键与外键之间的关联被独立出来,成为模型内置的部分。这在很多时候简化了数据查询的结构构造过程。
    在ORM中主键因为缓存的存在而显出与其他字段的区别。ORM的使用使得数据存储的分解策略得到扩充。并不是所有的表的更新频度都是一致的,而且表中的数据量大小也不同。字典表一般较小,而且很少更新,可以安全的复制。在整个数据存储框架中,ORM作为独立的技术元素参与数据存储过程,通过主键提供缓存服务,产生了新的数据分布模型,提供了新的性能优化契机。


posted @ 2008-01-06 19:04 canonical 阅读(3146) | 评论 (3)编辑 收藏

    我平时Kill Time的主要方式是阅读各类学术书籍。但是学习本身只是了解前人的发现,间或锻炼一下自己的思维能力,对自己的工作和生活并没有什么直接的助益。学习本身无论如何深入,多半也只是采用前人的话语复现前人思考的历程。在我们通过独立的思考获得直观的体验之前,在我们将所学到的知识应用到书本之外的场景之前,我们所学习的知识只是考试的工具,只是可供欣赏的对象,却不能成为我们思考中活跃的因素,无法参与我们思维的进程。别人只能给你思想的引子,并不能给你真正的思想。只有自己找到了所学知识的超出书本的非显然的应用,只有独立建立了不同概念之间未曾阐明的联系,我们才能真正获得对于真理的体悟。无论我们自己的发现是如何的微不足道,无论它是否只是重复发现了劣质的轮子,它唯一的意义只在于它是我们独立思考的结果,是我们自己的创造。即使它是卑微的,是重复的,它对我们的理解的作用仍然是任何外在的教诲都无法比拟的。

    现代社会中创造所需的成本已经被空前降低了。想想百年前的天才们,他们缺少信息来源,只能一页页翻查文献,反复誊写文稿来保存知识,大量的时间被花费在了与思考完全无关的事情上。同时,他们所处的时代存在着更多的不确知性,他们的思考所能够凭依的事实更少,做出错误判断的可能性与今天相比也更大。即使Bill Gates这样的挣钱能手在1981年也能放出"640k ought to be enough for anybody"的厥词,显示出我们在预测未来的时候是何等的乏力。当我们今天站在人类文明的巅峰,拥有前人无法想象的工具,并不断制造着从未经历过的实践的时候,我们理应拥有超越历史上任何天才的,更加宽广的眼界。

    现代社会中的创造看似简单了,但从另一个方面看,却又是大大的复杂化了。前人的成就成为了难以逾越的丰碑,而为了进入科学殿堂,我们需要的准备工作也变得异常的繁复。这里有科学内在的规律,但也有人为制造的障碍,而这其中最主要的是数学障碍。只要想一想,针对软件工程,经济运行,企业管理,每个参与其中的实践者都可以提出一些自己的意见,但是如果涉及到数学,为什么大多数人只有三缄其口了?现代抽象数学取得了辉煌的成就,但是它也可能毁掉了更多学生创造的激情。宏伟精深的大厦让人敬畏,却无法激发我们任何直观的共鸣。甚至Arnold这样的数学大师也坦承读不懂当代数学家们的著述:因为他们从不说“彼嘉洗了手”,而只是写道:存在一个t1<0,使得t1在自然的映射t1->彼嘉(t1)之下的像属于脏手组成的集合,并且还存在一个t2,t1<t2<=0,使得t2在上面提到的映射之下的像属于前一句中定义的集合的补集。当Bourbaki学派致力于在课本上消灭所有图示的时候,理性达到了非理性的彼岸。

    有时我在想为什么现在的程序员似乎对于程序的理解能力降低了。排除教学水平的降低和个人努力的不足之外,是否是因为现在需要学习的内容过多,以至于丧失了自我思考的勇气?在C的时代,每个程序员对于程序的理解都是直接的,原始的,对程序结构的把握都是充满自信的。当新的概念不断涌现的时候,人们总是说,Object不过是..., Component不过是..., AOP不过是..., ORM不过是..., IoC不过是.... 这体现了人们试图把新的概念融入自己原有知识体系的一种努力。虽然仔细考究起来,这里的理解很多时候都是似是而非的,未必掌握了新技术真正创新的思想方向,但是这里的思考总是独立进行的,总是对我们的理解和工作有所助益的。而新一代的程序员生活在Object, Pattern等概念已经无需饶舌来证明自己的时代,他们是否在思想中独自评估过所有概念的意义,是否建立了概念和实现之间直观的联系,是否在统一的思维世界中为所有的概念找到了合适的坐标?这一切,我不得而知。
    
推荐:Arnold 论数学教育 http://www.ieee.org.cn/dispbbs.asp?boardID=64&ID=25892

posted @ 2007-12-24 01:10 canonical 阅读(2011) | 评论 (6)编辑 收藏

    我在各种场合一直都在强调结构问题是独立的,在程序语言之外存在着独立的,可研究的,富有成效的结构问题。http://canonical.javaeye.com/blog/147424 在这个方向上更进一步,我们注意到所有的代码并不是天然出现的,而是由人所编制的,因此代码世界内部并不构成封闭的,自足的某个世界。代码中的结构问题并不是由代码本身完全解决的,即在代码之外仍然存在着技术上可研究的结构问题。
    我们在编制代码的同时也在编制着大量的说明文档。这些文档描述了代码片断之间的相互关系,描述了代码未来的扩展方向,描述了代码之间的可能的交互方式,同时也描述了针对现有代码实现的很多具体约束。例如我们在文档中约定某个量要在10和20之间,但在代码中却不一定显式进行了判断。针对代码结构的很多具体约束条件和相关性描述可能只在文档中体现,只在程序员的头脑中存在,而并不一定忠实的在代码结构中得到表达。
    我在设计领域基本持有一种物理实在论,即某种技术相关的约束应该在技术世界中通过技术手段得到表达。只是这里的技术手段却不一定指在应用中加上特定的代码实现,虽然我们在代码实现中更直接的表达设计要求无疑是需要提倡的。为了在程序中有效的维护结构相关性,我们并不一定需要抽象出所有可能重用的代码,并不一定需要确保某一概念在程序中只有精确的唯一的表达。程序中难以直接精确表达的弱关联,仍然可以通过开发/设计工具等技术手段得到有效的维护。我们需要保证的是代码世界中知识的自恰性,而自恰性并不等于唯一性。http://canonical.javaeye.com/blog/33788
    在Witrix中我们采用一种物理模型驱动的开发方式,http://canonical.javaeye.com/blog/29412 由pdm模型出发,自动生成hibernate的hbm文件,java实体类,元数据meta文件,前台Action注册文件等。生成的配置文件通过syncWithModel标记与模型保持着稳定的关联。所有配置文件都支持手工修改,开发工具识别syncWithModel标记,当pdm模型发生变化的时候,工具自动将变化信息同步到各个配置文件中。注意到这里并不是采用一个统一的元数据模型的方式,各个配置文件所表达的信息在一定程度上是重复的,也可能是不一致的。例如后台数据库允许保存100个字节,但是前台meta中我们可能配置只允许录入20个字节。根据不同应用场景的需要,我们可以在各个层面对每个配置文件进行独立的调节. 当然,在一般情况下并不存在这种需要。整个开发过程中,信息表达的自恰性并不是在应用程序代码中得到定义的,而是因为开发工具的存在而在技术上得到保证的。放松模型之间的唯一匹配要求,我们可以得到更加丰富,更加灵活的软件结构。实际上我认为RoR(RubyOnRails)采用直接映射的ActiveRecord的方式虽然有效保证了系统变动中知识的一致性,但是如果不允许在各个层面上都能够自然的定义某种偏离,它在复杂应用中的价值就要打上大大的折扣。

posted @ 2007-12-15 19:46 canonical 阅读(1394) | 评论 (0)编辑 收藏

   设计考虑的是最终需要什么,最终我们要提供的调用接口是什么,我们所直接需要的某个有价值的,直接存在的,直接可以接触的结构是什么,而不是它所依据的原理是什么,不是它的具体构造过程或者构造方法是什么。比如说我们在程序中完全不需要内置Map这一结构,因为它可以通过列表等结构组合构建出来,但是很显然,Map这一概念的直接存在对我们来说是方便的,是经济的,是有效的。我们在思考的时候并不需要考虑它是采用List实现还是采用Set实现,或者任何它本身的构造结构。这一概念在我们的思想中成为某种原子化的东西。

    那么,我们到底需要构建哪些概念,才能够最方便的基于这些概念应对万千应用系统的开发呢。 这是我们需要在结构空间作出探索的。 这里的思维方向不是把系统推向某种纯粹化,某种极致的简单化,而是让它更加物理化,揭示出更多的层次,更加关切在物理约束情况下如何实现灵活性的最大化。一种概念在物理上如果被证明能够在很多场景下成为不变的基元,则它便是有价值的,是可以进行物理诠释的。

   很多人习惯于接受语言存在的现实,接受设计后的结果,但是作为程序语言设计者,他们又是如何工作的?他们是否真的是从纯粹的数学关系推演得到所有的语法特征,还是他们预先已经在心中设定了应该出现的语法特征,然后去寻找它的数学表达,只是借此清除思维中潜在存在的矛盾性呢?

    语言中存在的所有特征是经过全局考虑的,是清除了所有概念的矛盾冲突的。但是在现实中,我们偶然构造的结构却可能是局限于当下的信息构造的,因此它们相会的时候,可能会出现不协调,可能会出现结构障碍。例如同样是关闭操作,有些人命名为close, 另一些人命名为destroy. 可能一个具有额外参数,另外一个没有。这里可能需要一种adaptor接口的封装,也可能使用ruby那种method-missing的动态判断。对于更加错综复杂的结构问题,其解决方案就不是那么显然的了,但这并不意味着我们无办法可想。究竟设计何种结构边界才能最小化结构融合时所要付出的代价呢?结构被识别并表征出来以后,是否允许它在一定范围内有所变形?在变形中我们需要保持的拓扑不变量是什么?结构动态调整的时候,我们是否需要定义调整的物理代价,是否能够定义某种动力学?

   我所阐述的只是在计算机理论中从数学视角向物理视角的转换,它不是必然给你提供某种超越当下的能力,而是提供一种不同的眼光看待所有的一切。视角变换后,我们发现了一些新的命题,而在原先的视角下在我们的话语体系中原本是无法表达这些命题的。串行程序假设了只有1颗CPU, 而函数式语言假设了可以有无限多个CPU, 你不觉得1至无穷之间缺点什么吗。我们可以创造一些东西把1至无穷之间的空白补齐,概念空间是连续的。
 

posted @ 2007-12-10 23:57 canonical 阅读(1281) | 评论 (1)编辑 收藏

    没有人否认抽象的意义,但是抽象是否就是抽象到无穷大,这是个可以明确定义的问题,也是数学领域正在解决的问题。在我们的思考中没有明确定义何处是边界, 没有明确的限制,这便是导向无穷的一种思维方式,它和现实中是否真的允许消耗无限多的资源,创建无限多的对象无关。当我们认为自己明白了终极的意义,明白 了一种推向无穷的抽象,这并不是理解了世界的全部,我们仍然要明白如何解决一些更加小范围,但是却又普遍发生的事情。
例如现在我的系统中只需要10个相互依赖的线程,如果我们定死了10这个数字,显然我们可以发展一种这个领域特有的高效的一些算法结构。而抽象到通用语言 中的时候,显然我们只能假设线程数是任意大,或者是充分大的,而无法充分利用10这一领域信息,因此在这个意义上我说通用语言不是有效的。
说到10这个确定的数字,不过是一种极端化的比喻。我常说概念是连续的,并不是非此即彼的,因此并不是从一种普遍情况到一种最特殊的情况之间不再有其他情 况了。在这中间环节,存在着非常深刻的复杂的物理事实。但是这些事实却又是某种有限性向我们揭示出来的。(请不要把这里的有限性理解为算术中的10以内)

现在来一个理论推演吧:
1. 任何系统都在一定约束下运行,即它们需要符合某些约束条件
2. 通用语言描述了某些结构,但是这些结构是充分通用的,能够应用到尽可能广泛的领域的
3. 线程数=10这个约束过份特殊,显然通用语言是不会考虑这个约束。实际上目前在通用语言设计中,无限资源假定基本都是默认的。
4. 我们承认某些现实的约束通用语言是不会考虑的
5. 在最特殊的,明显不会考虑的约束以及非常通用,一般通用语言必然考虑的约束之间,是否存在着更多的,非平凡的结构呢
6. 假如10年以内我们所有的硬件都只能支持10个内核,在我的产品研发中假定10个线程有问题吗。难道我在开发的时候就不再需要抽象了吗。我在其他方面仍然是需要建立抽象的。
7. n个抽象约束+1个具体约束,以及 n个无限约束+1个有限约束 仍然是有效的抽象形式。原谅我在这里又使用了有效一词,它真的很难理解吗。
8. 不是在我们的思维中存在着具体的或者有限的物理量,就意味着这种思维是不抽象的.

函数式语言或者任何一种其他我们已知的与Turing机等价的语言,它们在某种数学的含义上说是"没有差别的"。但是在我们的实际使用过程中,显然我们是 能够感受到它们之间的结构差异的。否则那些不断发明新的函数式语言的人的大脑都进水了吗?在具体使用中,总有人偏好这个语言,有人偏好那个语言, 总是某种情况下应用某个语言会方便一些,另一些麻烦一些。难道在所有函数式语言中开发类似ErLang解决的那些程序结构都是一样方便的吗?

有些人的论调是无论啥都逃不出函数式语言的思想。但是假如现在限定你必须使用java语言来开发商业应用,难道你罢工吗?如果你不使用函数式语言,你所做 的工作就不是程序工作了?你所解决的难道不是程序结构问题了吗?现在就是有一个结构问题要解决, 它是和语言无关的. 语言提供了就可以直接用, 语言没有提供我们可以写代码构造. 难道除了语言直接体现的结构之外, 程序本身就无法构造任何具有通用价值的结构了吗?

我在说到函数式语言的时候,基本的态度只是说不要太沉迷于一种特殊的抽象方式,应该多看看别的视角。在不同的情况下存在着做事情的不同的最优方式。思维中不要只允许永远,而容不下现在。

非此即彼,天下唯我,是我们思维中经常陷入的一个误区。现在计算机领域所谓的理论主要是基于数学视角的,没有考虑物理世界因为我们观察的有限性,因为资源 的有限性所造成的种种约束,此外数学中目前也没有考虑到物理世界真实的各种复杂性的存在。在我们想到一种计算机理论的时候,图像过于简单化,这种简单化被 认为是优美的全部。其实我们应该思维更加开放一些。

posted @ 2007-12-09 22:25 canonical 阅读(1168) | 评论 (5)编辑 收藏

仅列出标题
共18页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last