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

java中最常用的数据结构类型是Map和List, 它们也是Container的两种基本模式,一个是根据特征值定位,一个是根据地址定位。 它们共同的一个特征是表达了数据之间的直接的,短程的一种相关性。另一种常见的数据结构Tree则表达了数据之间的一种长程的关联:根节点与其所有层次上 的子节点之间都存在着关联。 文件系统,组织机构, XML文档等都可以对应为Tree数据结构。在描述树形结构的时候,我们经常使用XML文件, 但是XML文件在程序中操纵起来并不方便,这其中的一个重要原因是XML是面向文档的,即操纵XML的API返回的和使用的都只能是文本字符串,而不能直 接使用程序中常见的其他数据结构。在witrix平台中操纵Tree结构的标准接口是TreeNode类,它的设计是面向应用的,即节点的属性值为 Object类型而不是String类型。

Tree由三部分组成: 属性,值, 子节点

class TreeNode implements IVariant{
 List getChildren();

 int getChildCount();
 TreeNode child(int index);

 /** 当name对应的节点不存在时将会自动创建该节点 */
 TreeNode child(String name);

    /** 当name对应的节点不存在时返回null */
 TreeNode existingChild(String name);

 Map getAttributes();
 IVariant attribute(String name);
 void setAttribute(String name, Object attrValue);
}

TreeNode.attribute(name)返回的是IVariant接口,例如
boolean defaultValue = true;
boolean b = node.child("subA").attribute("attrB").booleanValue(defaultValue);

TreeNode本身也是IVariant接口的一个实现,例如
int i = ode.intValue();

通过使用IVariant接口,我们实现了强类型的java语言与弱类型的xml文本之间的自然转换,在转换过程中还可以指定缺省值,这些都极大的简化了实际应用时的编码量。

posted @ 2005-11-19 10:59 canonical 阅读(342) | 评论 (0)编辑 收藏

   CRUD(Create, Read,Update, Delete)操作中最难处理的是查询。因为查询总是多样化的,如果每个特定查询调用都编制一个对象方法,则维护量太大且扩展性很差。如果编制一个通用的 查询接口,一般的做法是直接以SQL文本作为参数,但这样就几乎丧失了封装的意义。这里的核心问题是Query本身是复杂的,我们应该将它对象化为一个 类,在程序中控制Query的结构,而一个文本对象与一个复杂的Java结构对象的差异就在于对于文本对象我们很难有什么假定,因而在程序中也很难编制通 用的程序对其进行处理,一般只能对它进行传递。实际上,文本中描述的结构存在于java程序之外!当然,我们可以利用Parser来重新发现这种结构,那 最容易使用的Parser就是xml parser了,所以我们应该将Query的结构建立在xml描述的基础上。
edu.thu.search.Query类直接体现了对主题域的通用查询条件。(对比我对数据仓库模型的描述)
class Query{
    List getFields();
 TreeNode getCondition();
}
查 询条件主要通过TreeNode进行显式建模,使得程序有可能对它进行进一步的处理。例如,在DataSource处理Query之前,权限配置模块可以 将附加约束直接追加到现有查询条件之后,实现对数据权限的行级控制。因为把Fields明确分离出来,我们也可以做到对权限的列级控制。
Query类的使用示例如下:
Query.begin().fields(TEST_FIELDS)
             .condition().eq(ID,"3")
   .end().resultType(IQueriable.TYPE_ROW_MAP)
   .findOne(dataSource).mapValue();
这里的调用接口的设计基本遵循与SQL类相同的风格,只是面向主题域而不是直接针对SQL语言的封装。

posted @ 2005-11-19 10:58 canonical 阅读(896) | 评论 (0)编辑 收藏

    新手总是有很多不好的代码习惯. 最常见的一个是不使用临时变量.例如
    for(int i=0;i<myList.size();i++){
        otherList.get(i).getSomeVar().getName();
        otherList.get(i).getSomeVar().getValue();
    }
    这种做法有如下后果:
    1. 代码冗长, 容易出错, 例如循环体中的某个i写成了j
    2. 函数调用终究是要耗费时间的, 在一个循环体中的调用往往对性能有可见的影响. 特别是当函数动态装载数据的时候, 例如每次调用该函数都查询数据库, 这种不使用临时变量的方法将会为系统留下性能隐患.
    3. 一条很长的语句如果不是为流式调用而设计的, 则这种调用方式会影响到我们的调试工作. 例如 当某个中间步骤返回空指针时, 程序会抛出NullPointerException异常, 而我们得到的信息只是某一行存在空指针异常, 但是无法定位到具体是哪个步骤. 当某个中间步骤返回的值不是null但也不是我们所期望的值的时候, 我们同样难以诊断出具体出错的步骤. 使用临时变量将会为调试提供便利
      int i,n=myList.size();
      for(i=0;i<n;i++){
          MyVar var = otherList.get(i);
          var.getName();
          var.getValue();
          ...
      }
      在需要的时候我们可以在出错语句处加上断点, 或者直接输出可疑的变量值.
    4. 长语句不利于抽象出子函数. 例如在第二种方式中我们抽象出子函数的难度比第一种方式小
       void processVar(MyVar var){
           var.getName();
           var.getValue();
       }

    造成这些习惯的原因很耐人寻味, 我猜想缺乏抽象能力似乎是最基本的原因, 毕竟为变量起一个名字也是最简单的抽象步骤之一.

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

    软件世界与真实的,物理的世界有着本质的不同,其中一点在于软件中的规则是根据需求制订的而不是先验的。我们在研究物理世界的时候,多少会有些唯物主义, 即纷繁芜杂的表象下蕴含着自恰的,不变的规律。物理学的建模是多方位的,多层次的。同一个规律,在不同的简化条件和不同的环境中我们会赋予它不同的名字。 在不同的抽象层面上,我们也可能会建立不同的模型。严格的说起来,这些模型之间可能存在着不一致性,但我们相信,存在着一个绝对精确的模型:无限的细节, 无限的关联,完美的,自恰的,而我们所建立的所有物理模型都只是对该终极模型在某个层次,某个角度上的近似抽象,而每一个模型都有着自己的适用范围。很多 时候物理学的建模是粗鲁的,抛弃了大量似乎必须的要素,只因为物理学家相信物理学的直觉能够将我们引导到正确的道路,不论我们做出什么样的简化和假设,只 要它是物理的,最终都会回归到真实的世界。
    软件是人为构造出来的,其体现的运行规律由外部需求所决定,而无法形成自我的证明。领域模型(Domain Model)隐喻式的期望能够建立稳定的逻辑层,可这注定是困难的。我们在软件设计中希望分层,职责单一,进行正交化设计。可是一个复杂系统的逻辑分解注 定是无法正交化的。非此即彼只存在于抽象的世界。多个复杂性层次上的结构交织在一起,使得我们难以建立稳定的根基。因为缺乏先验的支配规则,我们鼓吹需求 到实现的1:1映射,实际上只是希望通过贫乏的唯一性来维护演变中的自恰性。真正实现了1:1映射是不是在系统中引入了人为的刚性? 在一定的情景下,为了达到最适的模型,我们需要各种Facade,我们需要1:n映射,抑或是m:n映射。被割裂了的联系仍然需要通过各种service 在系统中重建出来。
 在建筑学的隐喻中,建筑设计师与建筑工人之间还存在着一个角色:土木工程师。他在物理结构的层面上而不是应用意义上把握整体 工程。在结构层次上我们是能够进行有效的推理和判断的。在软件中也是一样,抛去对象的业务含义,我们可以把它理解为一个Map,那对它可以进行那些操作是 可以预知的。只是我们对于软件结构层面的了解还是太肤浅了。每个项目,大量的时间花费在编写那些低层的与业务无关的模块(或者说可以抽象出这些模块),这 就如同每次建筑,都从制造砖块开始一样。材料的准备不是一朝一夕之功。在结构层上我们必须对系统形成深刻的理解,这不仅仅是业务建模的问题。

posted @ 2005-11-16 20:10 canonical 阅读(593) | 评论 (1)编辑 收藏

有人认为jsplet中使用session是个缺点,关于这一点,我想起一件以前听来的事情。我们都知道Linux的内核是常驻内存,不换页的(不知道最 新的内核是否已经有所改变),Torvalds认为内核换页对系统性能有巨大影响,是愚蠢的想法,所以Linux内核不能换页。据陈榕说,NT内核是可换 页的,而微软内部有一个小组,专门编写工具,对已经编译好的操作系统二机制代码进行优化,调整,最终结果是NT内核可以换页,但几乎不换页,这才是微软可 怕的技术实力。
对于简单的应用,session可以随意使用,而对那些性能要求极高的应用,每一个系统架构师都会如履薄冰,简单的依靠全局Cache不是真正的解决方 案,在每一个细节上我们所需要的是更多的控制权而不是更多的限制。jsplet通过objectScope可以对session进行有效的使用,这是它的 优点而不是缺点。在一个有效的框架下,才能进行真正有序的控制。只有在对系统拥有更多假设的情况下,才能把控制施加在关键点上。

顺便提一下,现在有些人一提到性能,就对jsp直摇头,而对第三方产品却热情拥抱。目前在java社区内普遍存在着一种对官方标准的漠视或者反感,不知道这是怎么回事。

posted @ 2005-11-16 20:07 canonical 阅读(261) | 评论 (0)编辑 收藏

在时间轴上定位一般比较麻烦,我们可以编写大量的get函数来得到特殊时间点,如 getFirstDayOfMonth(int year,int month), getFirstDayOfNextMonth(int year, int month),这不如采用如下正交化的流式设计。
EasyCalendar cal =

    new EasyCalendar().toYear(2001).toMonth(1).toFirstDayOfMonth().toFirstDayOfWeek();

posted @ 2005-11-16 19:28 canonical 阅读(392) | 评论 (0)编辑 收藏

tpl是witrix开发平台中的动态xml标签技术,其基本特点表现为如下三个方面:
1. 执行能力
 xml本身只是对数据的描述,而没有循环和判断的能力。
 在tpl中<c:forEach>和<c:if>标签可以完成程序语言的执行功能,并定义了<c:tile>, <c:iif>等更方便的标签。

2. 抽象能力
   获得抽象能力,首要的一点是使得创建新概念的成本极低。很少有人大量开发jsp tag,原因就是开发tag过于麻烦,而且调整不方便。另外开发出的tag如果粒度太小,大量使用的时候是对性能的致命伤害。
   tpl首先需要经过编译,而不是象jsp tag那样完全动态运行,而且tpl编译出的结果是无状态的,因此解决了性能上的问题。
   在tpl中导入外部模板的语法非常简单,可以实现基本的分解
    <c:include src="xxx.tpl" />
   更重要的是,tpl中定义了完善的库机制。
使用库
    <!-- 导入外部包, 导入时指定名字空间为demo。
         注意tpl中并没有完整的名字空间支持,只能作为qName使用
    -->
    <c:lib src="demo_lib.xml" namespace="demo" />

 <!-- 对具有cache:timeout属性的节点应用cache修饰,即该节点的运行内容被缓存 -->
 <c:decorator type="cache" match="
//@[cache:timeout]" />

    <div id="blockA">
        <p>通过tpl:tag属性可以设定宿主标签的真实标签名。这种做法可以方便可视化设计:</p>
        <input tpl:tag="demo:文本框" value="in block A" otherValue="${varInJsp}" onclick="clickOnBtn()"/>
    </div>

    <div id="blockB">       
       <p>分页表格:</p>
        <img tpl:tag="demo:分页表格" headers="fieldA,fieldB" pager="${pager}" />
    </div>

    <div id="blockC">
      <p>bodyTag标签的调用:</p>
        <demo:循环data>
            <input type="text" value="${row}" /> <br/>
        </demo:循环data>
    </div>

    <div id="blockD" cache:timeout="1000s" >
        <p>条件标签的调用:</p>
        <demo:当Num足够大>
            <p>当 thisObj中的变量 'num' 的值大于3的时候显示这句话 </p>
        </demo:当Num足够大>
    </div>

定义库文件demo_lib.xml
<!--
     自定义标签的属性:
  type: simpleTag, bodyTag 或者 conditionTag, 缺省为simpleTag
  importVars : 每个自定义标签具有自己的变量空间,需要通过importVars来明确指定从外部变量空间中导入的变量。
  demandArgs: 调用时必须明确给出这些参数的值
  otherArgs: 如果指定了该参数,则调用自定义标签时能够使用的参数就必须在demandArgs和otherArgs指定的范围之内。
-->
<demo>
    <!-- 一个简单的tag示例, 直接输出变量。这里demandArgs指定调用时必须提供的变量。 -->
 <文本框 demandArgs="value" otherArgs="otherValue,onclick" type="simpleTag" >
  <p> -----------------------------------  </p>
     <!-- 可以使用调用时提供的其他变量,如otherValue-->
  <input size="40" type="text" value="${value} * ${otherValue}" onclick="${onclick}"/>
  <p> -----------------------------------</p>
 </文本框>

    <!-- 一个自动分页表格,要求传入headers(List类型)指定表头,pager(Pager类型)提供数据 -->
 <分页表格 demandArgs="headers,pager">
     <!-- 从外部导入tpl文件 -->
  <c:include src="flex_table.tpl" />
 </分页表格>

 <!-- 一个bodyTag示例: 循环处理thisObj中的变量data中的每一行。
      importVars从调用环境中自动导入变量而不需要通过调用参数直接指定。
      当然如果调用参数中指定的变量与importVars中指定的变量重复,则调用参数会覆盖importVars.
    -->
 <循环data importVars="thisObj" type="bodyTag">
  <c:forEach var="row" items="${data}">
   <!-- tagBody为调用时标签的内容 -->
   <cp:compile src="${tagBody}" />
  </c:forEach>
 </循环data>

 <!-- 一个条件标签的示例.
      条件标签一般不应该输出文本,而是返回一个bool值。仅当返回true的时候,调用时标签的内容才会被运行。
 -->
 <当Num足够大 importVars="thisObj" type="conditionTag">
  <l:gt name="num" value="3" />
 </当Num足够大>
</demo>

注 意到bodyTag和conditionTag使得我们可以将循环(容器)逻辑和判断逻辑抽象成有意义的概念,而不再是一些执行的指令。这种抽象能力的应 用非常依赖于xml的自描述特性,我们不需要付出太大的努力,就可以在lib中封装出一个有意义的概念来! 而且tag的参数可以指定,可以缺省,存在多种选择,也对应着多种逻辑模型。
通过<c:decorator>的用法可以看到,因为xml明确定义了结构,使得我们可以轻易的实施类似AOP的功能。

3. 集成能力
   首先,tpl完全符合xml规范,与xml世界接轨,可以自由的使用xslt。例如,在
   任何标签中都可以指定tpl:xdecorator属性, tpl在编译之前首先会应用指定的xslt文件进行变换,对变换后的结果再进行编译。
   <anyTagName tpl:xdecorator="some_xslt.xslt">
  ...
   </anyTagName>
   其次,tpl可以使用属性标记,即tpl标签不仅仅可以通过标签名来标识,而且可以通过tpl:tag属性来标识。例如:
   <input tpl:decorator="demo:文本框" />
   与 <demo:文本框 />等效。
   这种做法避免了tpl侵入html模型,使得我们可以利用现有工具对tpl进行所见即所得(WISIWIG)的设计。

   再次, 在tpl中使用EL(Expression Language)语言,集成java数据模型。

   第四, 在tpl中可以采用如下方式实现强类型化,完成与java的接口。
   interface ITest{
     void test(String name, int value);
   }

   <o:class name="MyClass" implements="ITest">
    <test args="name,value" >
   ...
    </test>
   </o:class>
   这种能力可以在数据源的filter中使用。

   第五, tpl可以轻易的集成其它xml技术。例如,tpl集成了ant
   <ant:run>
  <echo message="xxx" />
   </ant:run>
   因此,我们立刻拥有了ant的数千个功能标签。

posted @ 2005-11-16 19:23 canonical 阅读(530) | 评论 (0)编辑 收藏

经常有人说XX技术是面向复杂应用的,对于常规应用如果采用那是得不偿失。我想很多情况下这只是体现了该技术的不适 应性。我主张系统设计应该尽量体现一种共振原则,即系统架构只有一个,但是面向复杂的应用,它表现出复杂的特性,能够辨识精细的概念,而面向简单应用,可 以实现一种优雅的退化(degradation), 对外暴露出一种简单的结构。抽象的说,我们所建立的不是一个孤立的模型,而是一个模型的系列,不是一个绑定应用的solution而是一种 strategy,在每一个复杂性层次上,都存在着对应的解决方案,而不同复杂性层次上的模型之间又存在着清晰的演化路径。这也是我所提出的级列设计理论 的要点之一。

在jsplet框架的设计中,充分体现了这一点。
jsplet中通过如下url格式来访问web应用:
view.jsp?objectName=xxx&objectEvent=yyy&eventTarget=ZZZ
其中objectName的格式规定如下
   
objectScope@objectType$objectInstanceId
在系统的规模较小,不需要对象的生命周期控制的情况下,我们可以将所有对象都放在根路径下/。
在同类型的对象只有一个实例的情况下,我们可以不使用objectInstanceId。
在一个页面对应一个模型对象的情况下,我们可以不使用eventTarget参数。
当页面比较简单的情况下,我们可以直接使用jsp输出页面,而不用使用其它页面模板机制。(采用jsp作为view原因很简单,jsp是标准,它应该成为其它所有第三方模板技术的入口)

为了支持jsplet的url中所体现出的概念,只需要1000行不到的代码,但是随着复杂性的增加,我们可以增加越来越多的功能,这体现在url格式的细化上,但是整体的程序结构并没有发生改变。

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

jsplet中的对象化并不是一种巧妙的trick,而是一种设计上的必然。现在大家言 必称OO,可OO到底意味着什么,除了书本上的话语,你能不能用自己的话描述一下,能否体会到那种必然。OO如果是一个有效的概念,它在软件以外的领域是 否有着对应。按照早期教科书的说法,OO是为了模拟现实世界,这种说法只是反映了设计上的一种困境,一种思想上的贫乏。面向对象最直接的意义在于标示了状 态与行为之间的耦合,此后在程序中可以用一种显示的,一致的方式来操纵这个集合体。在界面上,我们看到一个组件,在模型层,我们看到的还是那个对象,在配 置文件里我们还能清晰的辨别出它来。可在webwork这种面向action的框架中,package看起来像对象,在action层却不见了,当我们需 要同时使用两个action的功能的时候(如同时列出role和user),以前的action不能用了,只能再写一个。想一想,我们最少需要多少概念, 最少需要做多少工作,才能在软件中建立一个合适的概念框架,怎样才能保持这种框架中的张力。

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

关于jsplet中的object生命周期的管理以及使用拉模式,如果套用现在流行的设计术语,那就是涉及到所谓的IoC设计(控制反转)
IoC 的Container现在很受追捧, 但真正的IoC设计思想并没有引起大家的重视。也许大多数人使用的都是成品吧,以至于把成品的功能等价于其所依赖的设计原理。Spring等所建立的 IoC更准确的说法是Dependency Injection,只是IoC的一种体现。其基本思想是一个对象并不控制所有与它相关的部分,而是把控制权交给使用对象的人。这里重要的就是控制流(信 息流)的反转。
对象生命周期的管理也是这样,并不是由一个Manager猜测用户是否使用该对象,而是由用户直接标明他的态度,直接发出指令。
参 考一下桌面应用中的资源控制手段,我们打开一个窗口,与系统进行交互,此时占用资源,关闭窗口,则该窗口以及其子窗口所占用的资源都释放。在jsplet 中对象控制策略类似。当用户从某个功能区退出的时候,即当用户访问其它scope中对象而放弃当前objectScope的时候,开始做资源清理工作。即 用户的行为和意向直接驱动着系统的对象管理层。当然,如果用户一直不发出调用,那么系统只能猜测用户的行为,用户是否已断线或者正在思考?在这种情况下, 如果控制资源,则需要通过AOP给thisObj 加上类似EJB的功能。

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

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