<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>yimlin</title>
    <description>Anders小明@Javaeye.com

架构解决问题;技术服务业务;ROI高才是王道! 

关注于Domain Model的分析与设计;
专心于Analysis and Design能力的提高;</description>
    <link>http://yimlin.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>AOSD的实践冲动——Use Case的实现</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/155315" style="color:red;">http://yimlin.javaeye.com/blog/155315</a>&nbsp;
          发表时间: 2008年01月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Author：Anders小明<br /><br />目前采用是面向对象设计方法，设计的粒度分为两级：类和方法（属性），类似于数据库设计的表和字段；<br /><br />在现有实现体系下，一个方法内部将包容多个Use Case；同时因为Use Case本身的横向扩展，也会导致一个Use Case将关联到多个方法；这是一个多对多的关系，为我们的开发管理带来巨大的成本。<br /><br />为了有效管理Use Case及其实现映射，AOP技术成为一个好的选择；AOP允许我们为每个Use Case建立起独立的可管理的设计粒度：从方法中的一个代码段升级为一个独立方法和类；并允许这些Use Case被合理的有序的组织。<br /><br />现有的技术体系已经为我们建立了可行方案，如何组织Use Case间的逻辑操作：与，或和非操作就成为实施的关键。<br /><br />现有实践中，由于非业务Use Case在逻辑上的操作比较明确：与操作，执行顺序上也非常明确（更换顺序几乎不影响业务正确性），AOP已有广泛的应用；而对于业务操作由于逻辑上操作不十分明确，对于执行顺序上也存在不确定性，目前缺乏合适的实践管理；
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/155315#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 12 Jan 2008 21:42:08 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/155315</link>
        <guid>http://yimlin.javaeye.com/blog/155315</guid>
      </item>
      <item>
        <title>业务流程的层次和内容</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/154168" style="color:red;">http://yimlin.javaeye.com/blog/154168</a>&nbsp;
          发表时间: 2008年01月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Author：Anders小明<br /><br />（2008-1-12更新）<br /><br />业务流程依据不同层次分为3个<br />0. 领域流程<br />领域流程即领域对象生命周期，是流程中的核心；<br />1. 工作流程<br />工作流是处理多人协同工作，三权分立的过程；该过程是建立在领域对象生命周期上；生命周期的不同状态在工作流中都有映射；工作流处理的是领域中Request对象的生命周期；<br />2. 页面流程<br />页面流是处理工作中一个工作请求的分步申请；该过程建立在工作流上，是其人工节点的UI代理；<br /><br />这3个不同层次的流程，关注各自处理不同类型的领域对象；页面流在最外层关注于Request对象的数据收集，而工作流则负责Request对象的生命周期，在不同工作节点的流转；而处于核心的领域服务，则负责核心（长生命）领域对象的生命周期，输入Request对象，同时返回Response对象，以及产生Transition对象；对于同一业务对象的并发处理，应该通过业务来控制； <br /><br />但并非所有系统流程都是如此，对于没有显著长生命周期领域对象的业务系统，由于没有明确的增量变更操作类型及其规则，业务操作是直接更新业务对象，也就没有Request以及Response对象，因此其领域流程和工作流程趋于一致，合并更有效率，而页面流程也是直接处理核心领域对象，但这样的系统可能需要支持领域对象的多版本能力。
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/154168#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 08 Jan 2008 22:42:48 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/154168</link>
        <guid>http://yimlin.javaeye.com/blog/154168</guid>
      </item>
      <item>
        <title>Domain Model的三种类型及其级别</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/154167" style="color:red;">http://yimlin.javaeye.com/blog/154167</a>&nbsp;
          发表时间: 2008年01月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Author: Anders小明<br /><br />（2008-1-12更新）<br /><br />从以上的分析中，我们可以了解到Domain Model分为三种类型：<br />0. 全局常量对象<br />1. 长生命周期业务对象（类似保单对象）；<br />2. 交易过程的Transaction对象，几乎没有生命周期的；<br />3. Request/Response对象。这类对象以前没有识别的，通常和VO混在一起；但是在IAA中以及电信业的模型是这类对象是独立存在，并被持久化的；Request对象建立在增量更新上很有用。当然他们也是几乎没有生命周期的。 <br />	<br />并非所有的业务系统都拥有这三类对象！相当一部分的业务系统，并没有显著的长生命周期对象，因而没有明确的增量变更操作类型及其规则，业务操作是直接更新业务对象，也就没有Request以及Response对象；同时此类业务系统的Transaction对象也通常不存在；<br /><br />Domain Model的级别：<br />但不论是那种类型对象，都拥有一个属性，对象等级；对于保险系统来说，保单对象，产品对象以及组织对象是一级对象，而险种和角色等都是二级对象；这点对于设计Repository以及服务粒度都有影响。<br /><br />关于VO对象的讨论：<br />VO对象是一种技术类型对象，存在于Web，Domain等层次，其存在的意义是提供集成能力；其意义是：1. 保护系统的信息边界，提供一种结构可以使其它系统或者组件通过编码方式获取系统内信息的方式；2. 保护系统的事务边界，领域对象技术上携带着持久化信息，通过VO可以屏蔽得以屏蔽。常见的VO对象存在于Web层和Domain层。<br />因此，VO对象的存在只是为了集成而存在，其是否存在的取决于两个方面：框架提供的事务边界以及对象路径访问能力。<br />Domain层VO对象，通常是用于不同Domain组件间的访问。但随着架构的改进，集成代码独立存在，而不再嵌入到组件内部，组件的边界问题保护不复存在；更进一步的是，框架提供自动化的接口适配映射能力的增强。因而VO对象也失去存在的意义。<br />Web层VO对象，以SWF为例，早在SWF 1.x时代，框架就提供了丰富的对象路径访问能力，但其Web交互是典型的MVC2方式，事务边界在view的render前关闭，因而导致需要特定的VO对象来避免持久化信息问题；而SWF 2.x时代，view的render是在事务边界内，VO不再需要。
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/154167#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 08 Jan 2008 22:41:33 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/154167</link>
        <guid>http://yimlin.javaeye.com/blog/154167</guid>
      </item>
      <item>
        <title>基于业务模块组件的系统架构</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/148489" style="color:red;">http://yimlin.javaeye.com/blog/148489</a>&nbsp;
          发表时间: 2007年12月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Author：<a href="http://www.blogjava.net/AndersLin/">Anders小明</a><br /><br /><p>以前写过一篇《<a href="http://www.blogjava.net/AndersLin/archive/2007/01/05/91876.html" id="viewpost1_TitleUrl">基于抽象的分层结构</a>》，这里补充一篇《基于业务模块组件的系统架构》<br />一些内容在《<a href="http://www.blogjava.net/AndersLin/archive/2007/07/02/127677.html" id="viewpost1_TitleUrl">项目笔记：dao，web，模块边界以及Model分类</a>》以及《<a href="http://www.blogjava.net/AndersLin/archive/2007/03/12/103387.html" id="viewpost1_TitleUrl">模块的接口设计</a>》随笔中已经提到，这里补充总结一下。<br /><br />任何一个有一定规模系统，通常会把系统做一定分解降低分析设计开发的难度，模块划分是一个比较常见的方式。<br />而在模块的划分及其分析设计的实践中，包括了两种层次的边界。第一是交互行为层次，第二是对象层次。</p><p><strong><em>首先说交互行为。</em></strong><br />模块和模块的交互接口最为重要，通常我们认为这些接口应该通用稳定，然而如何设计每个模块对外提供的接口却是一个不易的问题。</p><p><strong>集成的两种基本实现方式：</strong><br /><em>1.&nbsp;API方式</em><strong>。</strong><br />这是常见的实践方式。即每个模块公开数量不多的接口类（但拥有较多的接口方法）以及接口参数（都是普通的VO），每个接口的设计和实现都有该模块成员维护；实践中：这些接口都独立打包，形成多（模块）对一（接口）的依赖关系，方便编译。<br />这样的好处是：接口边界清晰，开发编译依赖比较简单。<br />然而实践中极有可能出现两种状况：接口维护失控或者过严而死板（而影响开发）。接口失控是因为接口的维护太过随意，因为A模块的需要就轻易在B模块中添加一个接口（方法），导致该接口（方法）非独立性（基本上只给模块A的这个功能点使用），或者是接口的控制过严，导致或者工作效率不高，或者接口的易用性不好。<br />原因在于：接口是两个模块间的耦合，而发生的种种问题在于模块耦合太过紧密；同时实践中，把模块对外提供的接口，与模块需要实现的外部模块的集成混为一谈；换句话说，A模块需要集成的语义，和B模块提供接口的语义存在差距。<br />这样的实践优点：开发管理控制成本较低，模块编译依赖简单，模块物理边界明确。</p><p><em>2.&nbsp;Adapter方式</em><br />这种的实践可以一定程度上解决API方式面临的问题：根据指导原则&mdash;&mdash;为了降低耦合只有在中间加一层；即不轻易为模块设计对外提供的接口（方法），除非是通过重构得来的；模块对外提供两种类：一个是需要外部模块实现的接口（接口设计从本模块需要出发，当然每个接口尽管是为某个功能点服务，但也要注意其在模块内通用性）；另一个是其它模块要求本模块实现的接口的实现类（这块物理上独立打包，独立于模块之外）。<br />即：A模块拥有一些需要B模块实现的接口（A模块对B模块的要求），而B模块中也有要求A模块实现的接口，因而A有这些接口的实现类。<br />处于编译依赖的考虑，已有的实践是把处理接口适配的代码维护在模块外的，就是所谓的集成模块，这样编译上就形成了1（接口实现）对n（模块）的依赖关系。模块的边界在这里模糊了，当然在这之外模块的边界是很清晰的。<br />这种实践方式的好处在于：模块的接口就多了一层隔离降低了耦合，把接口的通用性和接口的适应性分离，又明确了模块的边界，使得接口在日后的优化和调整有了缓冲。<br />这样的实践和API实践的不同之处在于：接口的设计权利一分为二，需求方拥有了其应该有的权利，并且这个权利与提供方没有冲突（旧的模式不行，一旦供应方改变了接口，就立刻导致编译问题）。</p><p>两种方式的对比：<br />1. 采用API方式集成，代价在于维护API本身，API面对众多的客户端，其面临的设计维护成本较高，同时实施成本高；<br />2. 采用Adapter方式集成，由于没有维护API，缺乏可复用接口，但可以做到为每个客户端自由定制，其设计维护成本较低，实施上成本较高；</p><p>&nbsp;<strong>集成的演进方式：</strong><br /><em>1. 采用事件模式。<br /></em>这也是完成交互处理的模式。通常事件模式在业务行为上也可以完成和接口相互的功能，不过由于其在接口签名上无法明确提供其业务含义，建议谨慎应用。对于产品化项目，项目开始可以应用事件处理机制，在成熟时提取为更具体的接口。<br />&nbsp;事件模式中，Event的监听类也面临着模块的边界问题。已知的成熟开源项目都在事件源中暴露的模块内部对象，方便开发者使用。<br />&nbsp;可以说这种方式的是Adapter方式的演进；<br />&nbsp;<br /><em>2. 采用框架回调方式</em><br />工作流和页面流也负责集成不同的模块.不过因为工作流和页面流框架都维护独立的数据对象共享池,因而在这个层面上,模块间直接的交互并不存在。这种集成上的优势，演化成通过ESB等集成框架完成.<br />&nbsp;这种方式是API方式的演进，但是把调用点以及调用方独立出来，成为框架的一部分。</p><p><strong><em>现在说说对象一级的边界集成。</em></strong><br />在保险中，benefit对象会关联一个product对象，不过product对象是是属于产品模块而非保单模块，对于保单模块只关心id而并不使用对象，但在RMS或者FMS或者UIC这样的集成环境却是需要product对象的。我们要采用代码生成的方式，在benefit对象上加一个annotation，比如Integration的annotation标识，使用AspectJ在编译上enhancement生成的class，使得benefit对象在集成环境上可以拿到product对象，这算是一个集成方面。</p><p><strong><em>组件化管理<br /></em></strong>组件化管理包含两个含义：<br />1. 组件访问控制，依赖管理以及服务注册；<br />现有的访问控制的办法，只能通过代码提交时或者编译发布时检查，但我们更期望支持运行时的能力，避免外部组件访问组件未申明公开的内部结构和程序；同时维护方式应该简单方便，成本低。<br />目前的组件依赖管理几乎没有，尤其是基于版本的依赖；而在实践中，依赖维护管理尤其是多版本的通常花费大量的成本。<br />2. 组件依赖的绑定以及失效（故障）的隔离；<br />我们希望能够为每个组件提供一个保护区域，当其依赖的一个组件因为种种原因（软件错误的或者软件升级）失效时，系统至少应该提供一种fail-fast机制，包括如下能力：<br />A．故障即停（Halt on failure）&mdash;&mdash;当一个组件出错时，应当立即停止下来，而不是继续执行可能不正确的操作；<br />B．故障曝光性质（Failure status property）&mdash;&mdash;当一个组件发生故障时，系统中的其他组件应该得到通知，故障的原因必须交代清楚；<br />同时，我们希望该组件，如果可能它应该可以继续运行。完成这样的目标需要很多的工作，但首先依赖于系统fail-fast能力，同时在故障消除服务恢复后，系统能够提供相应的通知，并控制恢复的顺序。</p><p>已知的支撑框架：SCA和OSGi<br />1. SCA考虑的是异构异步环境，OSGi考虑同构同步环境；<br />2. OSGi规定了容器如何管理组件，组件依赖（版本）管理，组件边界保护以及组件服务依赖运行时绑定策略和权限控制；SCA对此没有涉及；<br />3. SCA考虑的如何暴露服务，设计时客户端如何使用服务；OSGi考虑的更加完善；</p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/148489#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 15 Dec 2007 01:14:08 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/148489</link>
        <guid>http://yimlin.javaeye.com/blog/148489</guid>
      </item>
      <item>
        <title>小议领域模型(Domain Model)补充以及更新</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/148486" style="color:red;">http://yimlin.javaeye.com/blog/148486</a>&nbsp;
          发表时间: 2007年12月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Author: Anders小明<br /><br /><strong>为何要Domain Model</strong><br />传统的开发方式：基于数据库的设计开发。数据库提供的设计模型是表和字段两种粒度，这两种粒度有时并不合适于系统设计：<br />1. 模型的结构化能力<br />1.1. 同一模块组件下的设计优势；一个model可以来自多张表的数据聚合而成，一张表可以聚合多个Model；一个逻辑是由几个固定字段或者非固定字段聚合；Model间的关联关系也是使用表无法展示的（外键的约束对于系统开发来说实在太有限了）。而这些不论表还是字段粒度都无法支持的！<br />1.2. 采用Model方式容易解决项目的集成问题（两个不同模块组件访问同一张表的情况）<br /><br />2. 架构的结构化能力<br />事务脚本直接访问到表。换句话说，其架构只有Service + Database Table，这样的架构下Database Table可以说就是我们的模型。<br />这里看看在逻辑设计上Domain Model对于架构影响<br />A. Service， Model和Repository，model的重建和关联交由Repository完成，单个数据逻辑交给Model（支持泛化）<br />B. 测试的好处<br /><br />3. 分析和设计的统一<br />沟通的问题，客户关注于其业务，分析的模型接近于客户需求，设计也采用model的方式，避免分析和设计的割裂。有助于IT间，IT和BA（客户）间的沟通<br /><br />4. 其它好处<br />4.1.采用Domain Model可以屏蔽持久化信息。持久化设计的表设计是和DBA相关的，DBA对于表设计有权力的，采用Model可以有效隔离各自工作；<br />4.2.Model可以通过很多的手段透明的解决性能问题，而采用表做模型导致容易导致性能问题，当然不是没有办法解决，一种是通过DataSet的方式，但这样的切换成本较高。<br /><br /><strong>应用Domain的体系结构</strong><br />如前所述，逻辑设计上Domain Model的实施使得架构分为Service，Model和Repository；其中Model的持久化，重建和关联交由Repository完成；而单数据逻辑（依赖自身数据信息以及关联数据）则归于Model（支持泛化）；Service则关注于任务处理，相当于一个macro flow，包括了多个model处理，以及其它服务的调用。<br />以下是示意图：<br />Domain Service  | Repository<br />Domain Object   | <br /><br /><strong>Repository和Dao</strong><br />Repository是一种特殊的Service，不做任务处理；而是提供Model的持久化，重建和查询工作。由于企业应用大都通过数据库实现持久化，因而Repository和传统的Dao间的集成设计就非常重要。<br /><br />已知的有三种设计方式：<br />1. 两个接口两个实现类。Repository和Dao各自独立接口，而通过Repository实现类转发请求给Dao实现类。<br />这种方式虽然正统，但是维护成本太高；一次更新最多要改四处地方。<br />2. 两个接口一个实现类。Repository和Dao各自独立接口，一个实现类同时实现两个接口。<br />这种方式就大大简化维护成本；一次更新最多只改一个接口和一个实现类。<br />3. 两个接口一个实现类。与上面不同是，Dao接口继承Repository接口，一个实现类实现Dao接口。<br />这种方式的维护成本和上一种差不多，但是当接口方法在这个两个接口间流动时，可以通过开发工具完成。<br /><br />另外Dao实现类本身也是工作中开发维护成本较高的一部分，可以通过代码生成降低开发成本，更多资料可以参考ibatis。
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/148486#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 15 Dec 2007 01:07:54 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/148486</link>
        <guid>http://yimlin.javaeye.com/blog/148486</guid>
      </item>
      <item>
        <title>软件架构乱弹——问题域及其解决方法（2007.12.14更新）</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/125766" style="color:red;">http://yimlin.javaeye.com/blog/125766</a>&nbsp;
          发表时间: 2007年09月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="postcontent">作者：Anders小明<br /><br />(2007.12.14日补充更新了部分内容，其中有关Web网站性能特点部分内容来自网络) </div><p class="postcontent"><strong><span style="font-family: 宋体">一、什么是架构</span></strong></p><p class="postcontent"><span style="font-family: 宋体">1. 和</span><span style="font-family: 宋体">架构相关的几个问题域</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">架构需要解决的非业务问题域包括如下：</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">A 系统目标：系统性能，稳定性.</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">B.项目目标：开发成本，质量</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">C.项目过程：需求的不确定性和开发过程的团队协作性</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">不同的问题域，解决之道也不相同！而同一问题域的不同层次的要求，解决之道也不尽相同。</span></p><p class="postcontent"><span style="font-family: 宋体">2. 什么是架构</span></p><p class="postcontent"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;架构到底是啥，愚以为下面的这段英文描述的很清楚。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="color: black; font-family: 宋体">That's like asking, what is culture? Culture is the way you do things in a group of people. Architecture is the way you do things in a software product. You could argue by analogy, then, that architecture is to a software product as culture is to a team. It is how that team has established and chosen its conventions，</span></p><p class="postcontent" style="text-indent: 21pt"><span style="color: black; font-family: 宋体">Which leads us inevitably to the question of &ldquo;goodness&rdquo;? How do you know if an architecture is good? Consider an architecture that isn't built using a strong domain model, and instead relies heavily on stored procedures. That might be OK, or it might not be OK. You could have decided that part of your architecture is to use a really strong domain model and not use stored procedures, right? So an architecture is some reasonable regularity about the structure of the system, the way the team goes about building its software, and how the software responds and adapts to its own environment. How well the architecture responds and adapts, and how well it goes through that construction process, is a measure of whether that architecture is any good. </span></p><p class="postcontent" style="text-indent: 21pt"><span style="color: black; font-family: 宋体">The system architecture determines how hard or easy it is to implement a given feature. Good architectures are those in which it is considered easy to create the features desired. In that the way to judge whether an architecture is good is whether the architecture is good for the purposes to which it is applied. </span></p><p class="postcontent" style="text-indent: 21pt"><span style="color: black; font-family: 宋体">The definition of goodness has to be related to fitness for purpose. Is this glove good? I don't know. What are you doing with the glove? Are you throwing snowballs, cooking barbeques, or playing golf? There's a set of changes that are going to occur to a software system over time. Probably the utilitarian or most useful definition of goodness is the answer to this question: are the changes that will keep this system successful in this domain in this product line relatively easy? If they are, then it's probably a good architecture. </span></p><p class="postcontent"><span style="font-family: 宋体">3. 架构的背后</span></p><p class="postcontent"><span style="font-family: 宋体">为了实现架构的目标涉及到以下三个方面：技术，组织和过程。这里举例说明。</span></p><p class="postcontent" style="margin-left: 21pt; text-indent: -21pt; tab-stops: list 21.0pt"><span style="font-family: 宋体">1)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">技术对开发效率和运行性能，以及组织和过程的影响。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">案例A．映射的问题。公司产品的一个重要需求是根据客户输入，映射到PDF文件上。技术上整体实现需要四个步骤：在PDF文件上画好所有的数据域，通过读入一个XML映射文件，获得运行数据并生成FDF，合并FDF和PDF生成目标文件。后两步工作都由代码自动化了，因而实现的主要工作在于前两步。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">在第一个实现版本里，XML映射文件的DTD太简单，致使一个xml文件至少在4000行左右，同时xml文件太verbose了。这样的结果直接导致运行系统在峰值时，由于XML消耗了大量内存，1G的内存根本吃不消；同时对XML解析执行使用了CPU的大量时间；导致开发人员需要做大量的工作，开发效率降低了，通常需要尽一周才能完成一个xml文件，员工都不愿意做；也导致开发过程的漫长， 开发部门对于BA部门和ST部门的要求反应变的缓慢。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">在第二个版本的实现中，重新实现了DTD，加入了大量的关键字同时也消除了verbose，大量的缩小了XML大小，从4000多行减低到900多行。不仅减低了内存使用，提高执行效率；也提高了开发效率，基本只要一天就可以完成一个映射文件。同时对BA部门和ST部门的反应也快了。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">案例B：脚本的问题。产品在web层提供了脚本支持，出于方便开发的目的。但是没有对脚本的环境限制，脚本可以做系统程序的大部分工作。导致开发人员偷懒，在web层混入了大量业务逻辑代码。最终造成业务逻辑分散而不可控制。</span></p><p class="postcontent" style="margin-left: 21pt; text-indent: -21pt; tab-stops: list 21.0pt"><span style="font-family: 宋体">2)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">组织结构对技术，开发效率和应变能力的影响。</span></p><p class="postcontent" style="text-indent: 18pt"><span style="font-family: 宋体">案例A.部门的分工问题。开发部门根据不同的职责，分成A，B和C等数个小组。大部分开发中互不相干。但也有时候，需要跨组的支持，比如B要实现某个需求，需要A在一定条件在记录一个或多个信息。因为每个开发人员各自负责一部分工作，导致跨组沟通的困难。同时由于整个开发部采取任务绩效，有时间压力，加上只是一个小的要求。于是在A人员的同意下，B人员直接在A代码中写入业务逻辑。每次都是这样的小改动，不断的发展后，代码开发变凌乱。</span></p><p class="postcontent" style="text-indent: 18pt"><span style="font-family: 宋体">案例B.开发的历史问题，当某个开发人员写下的代码，有是问题的，接手开发人员由于文档不全以及没有测试用例，不愿意承担变化的代价，选择小修小补，这个小修小补有可能和有问题的代码混杂，导致更大的代码。</span></p><p class="postcontent" style="margin-left: 21pt; text-indent: -21pt; tab-stops: list 21.0pt"><span style="font-family: 宋体">3)<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">过程对开发效率和应变能力，以及组织的影响</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">案例A.过程的问题。开发部门的上下游部门BA部门和ST部门的合作关系。ST部门的绩效考核,考核基于发现错误的数量，导致ST为了完成任务，提出一些非正常性要求。PM部门出于部门的方便通常提出一些实现难度比较大要求。开发部门本身又存在时间压力，导致一些需求的实现本应在低一层的代码中实现的，却在高层用蹩足的方式实现。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">案例B.帮助系统的问题。帮助系统一开始采用一个个单独分散的静态页面。出于性能的考虑和部门负责考虑。帮助系统不断改进中，过程缺乏组织性，文件的命名规则随意，存储位置随意，造成了管理的混乱。直接的后果是页面的入口混乱和各自引用关系混乱。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">在帮助系统的第二版，从静态页面转成动态页面。采取统一分类和命名规则，并统一了入口。同时采取分级管理引用关系，适度冗余。虽然减低了运行性能。但提高了开发效率和可维护性。</span></p><p class="postcontent" style="text-indent: 21pt"><span style="font-family: 宋体">&nbsp; </span></p><strong><span style="color: black; font-family: 宋体">二、架构的性能问题解决讨论</span></strong><span style="font-family: 宋体"></span><span style="font-family: 宋体">性能问题&mdash;&mdash;嗯，一个非常神圣而高深的问题的。从我刚刚开始工作的时候，至今依然是。然而我相信，一定存在一个基本的思路和方法，我以为解决性能问题的工作还是在于分解<span>,</span>通过分解来确定问题域。</span><span style="font-family: 宋体">&nbsp;</span><u><span style="font-family: 宋体">1. </span></u><u><span style="font-family: 宋体">性能调优的关键<span></span></span></u><span style="font-family: 宋体">性能调优的关键是：发现性能瓶颈所在，慢是相对概念，评价标准是符合不符合系统要求。调整同时需要考虑维护成本，因为维护成本通常是开发成本的<span>3</span>倍。</span><span style="font-family: 宋体">&nbsp;</span><u><span style="font-family: 宋体">2. </span></u><u><span style="font-family: 宋体">性能调优的公式<span></span></span></u><span style="font-family: 宋体">先介绍三个公式性能问题的公式<span>: </span></span><strong><span style="font-family: 宋体">总处理单量<span> = </span>总处理时间<span>/ </span>单笔请求处理时间<span> * </span>总并发数<span></span></span></strong><span style="font-family: 宋体">这个公式另一个写法为<span>:</span></span><strong><span style="font-family: 宋体">总处理时间<span> = </span>单笔请求处理时间<span> * </span>总处理单量<span> / </span>总并发数<span></span></span></strong><span style="font-family: 宋体">不同的写法代表不同的关注点，适合不同类型的业务类型<span>, </span>一般说前一种写法代表在线请求的，后一种写法代表后台<span>batch;</span>即高并发或大数据量问题<span>.</span></span><span style="font-family: 宋体">也有客户给明确要求系统要支持<span>xxx</span>并发，这个就需要了解客户的这个并发数是如何计算得来，需要通过分析客户的业务，而通常是根据总处理单量来确定客户实际的并发数。<span></span></span><span style="font-family: 宋体">但无论如如何，四个变量中，总处理单量和总处理时间是先被确定的，换句话说需要关注是单笔请求处理时间和并发数，也就是降低单笔请求处理时间或者增加并发数。</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">对于单笔请求处理时间，其公式为：<span></span></span><strong><span style="font-family: 宋体">单笔请求处理时间 ＝ 数据计算时间 ＋ 数据读写时间＋其它技术导致时间消耗</span></strong><span style="font-family: 宋体"></span><span style="font-family: 宋体">很显然降低单笔请求处理时间就需要降低三个因素消耗的时间。<span></span></span><span style="font-family: 宋体">1.</span><span style="font-family: 宋体">降低单笔请求处理时间<span></span></span><span style="font-family: 宋体">第一原则是<span>, </span>只计算一次<span>.</span>缓存计算结果；<span></span></span><span style="font-family: 宋体">第二是，延迟部分计算（在不影响结果的情况下，将部分后续计算延后处理）<span></span></span><span style="font-family: 宋体">第三是，提前部分计算（例如对于年度的<span>batch</span>计算，可以在每个月都计算各自数据，年底汇总即可）<span></span></span><span style="font-family: 宋体">2.</span><span style="font-family: 宋体">降低数据读取时间<span>,</span>分三种<span></span></span><span style="font-family: 宋体">2.1. Global</span><span style="font-family: 宋体">的<span>,</span>系统启动时加载<span></span></span><span style="font-family: 宋体">2.2. Long Time, </span><span style="font-family: 宋体">可采用<span>LRU</span>方式<span>cache</span></span><span style="font-family: 宋体">3.2. Per operation. </span><span style="font-family: 宋体">第一次访问加载<span>,operation</span>结束后丢弃<span>.</span></span><span style="font-family: 宋体">3.</span><span style="font-family: 宋体">降低数据写入时间<span></span></span><span style="font-family: 宋体">例如文件写入通过<span>buffer</span>一次<span>flush</span>；对于<span>SQL</span>采用<span>batch</span>提交（<span>hibernate</span>的做法）；对于同一张表数据只做一次更新；<span></span></span><span style="font-family: 宋体">4 .</span><span style="font-family: 宋体">改进计算时间，针对不同技术结构采用不同手段。<span></span></span><span style="font-family: 宋体">4.1.</span><span style="font-family: 宋体">让计算支持并发<span>,</span>提高性能<span>,</span>例如采用<span>MapReduce</span>的方式<span></span></span><span style="font-family: 宋体">4.2.</span><span style="font-family: 宋体">改进算法<span>.</span>例如数据库中的<span>SQL</span>改进<span>.</span></span><span style="font-family: 宋体">4.3.</span><span style="font-family: 宋体">减少不必要计算时间<span>.<span>&nbsp; </span></span></span><span style="font-family: 宋体">5.</span><span style="font-family: 宋体">减少其它技术原因导致的消耗<span></span></span><span style="font-family: 宋体">如<span>JVM</span>的<span>GC</span>导致性能消耗等</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">对于总并发数，其公式为：<span></span></span><strong><span style="font-family: 宋体">总并发数 ＝ 单机服务器并发能力<span> * </span>总并发服务器数</span></strong><span style="font-family: 宋体">&nbsp;</span><u><span style="font-family: 宋体">3. </span></u><u><span style="font-family: 宋体">确定改进方案<span></span></span></u><span style="font-family: 宋体">那么如何确定那些因素需要调整呢，在于两个方面的分解：<span></span></span><span style="font-family: 宋体">A. </span><span style="font-family: 宋体">业务层面<span></span></span><span style="font-family: 宋体">业务层面只是指通过业务行为分析<span>, </span>把性能问题分解为不同的部分<span>,</span>每个部分面临性能压力现状和目标<span>,</span>最终确定需要优化的问题域<span>.</span></span><span style="font-family: 宋体">业务层面分解包括<span>4</span>个内容<span>: </span>功能<span>, </span>内容<span>,</span>时间和区域<span>.</span>最重要的是前三个<span>.</span></span><span style="font-family: 宋体">以<span>eBay</span>为例<span>, ebay</span>对于前端功能划分划分为<span>70</span>多个功能<span>,</span>不同的服务器处理不同的功能<span>.</span></span><span style="font-family: 宋体">内容是指内容热点<span>,</span>比如对于<span>search</span>来说<span>,</span>就按体育<span>,</span>数码<span>,</span>音乐等划分<span>,</span>不同内容有不同热点数据<span>,</span>以及不同搜索关键匹配<span>.</span></span><span style="font-family: 宋体">时间<span>, </span>时间是一个非常重要的因素<span>,</span>在一些特定时间段<span>,</span>性能的要求会非常高<span>.</span>比如下半夜的访问点击量和白天的就有不同<span>.</span>对于一些<span>batch</span>来说<span>, </span>月末或者年末处理的单量就有明显的提高<span>,</span>比如分红险的记息<span>,</span>平时每天只有<span>7000</span>单<span>,</span>而年末会有<span>12w</span>单<span>.</span></span><span style="font-family: 宋体">地点划分<span>,</span>不太常见<span>,</span>不过也有助于分配计算资源<span>.</span></span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">业务层面的分析不仅是确定问题所在<span>,</span>还是确定优化的策略<span>.</span>比如有一个<span>batch</span>计算<span>,</span>执行时间比较长<span>,</span>而通过业务分析<span>,</span>发现该计算只针对特定的业务<span>, </span>系统全部有效单量是<span>12w</span>单<span>,</span>而符合计算要求的只有<span>3000</span>单<span>,</span>只要加上一个前置判断就可以免除无谓的计算<span>,</span>运行时间减少数个小时<span>(</span>大约<span>0.2</span>秒<span>1</span>单<span>).</span></span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">B. </span><span style="font-family: 宋体">技术层面<span></span></span><span style="font-family: 宋体">系统建立时技术结构<span>,</span>通常一个系统结构如下<span>:</span>接入网络<span>,Web</span>服务器<span>,</span>应用服务器<span>,</span>以及数据库服务器<span>.</span></span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">在这样结构下，要小心的分析和验证系统性能的瓶颈，需要优化<span>Web</span>服务器，或者提高数据库并发能力等等。这部分网上的资料非常多。</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">采用并发立刻面临一个问题<span>,</span>即负载均衡<span>.</span>负载均衡如果无法正常的工作<span>,</span>那么并发也就无法正确的工作<span>.</span>负载均衡可以静态分布<span>,</span>也要动态分布<span>.</span>这里面涉及的问题比较多<span>.</span>服务器自带的负载均衡有时不能满足业务上的需要<span>,</span>要自行开发<span>.</span></span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">4. Web</span><span style="font-family: 宋体">网站性能特点<span></span></span><span style="font-family: 宋体">互联网网站的特点是交易少<span>,</span>事务短和并发高<span>.</span>对于网站这一特点需要做一番分解<span>.</span>网站的计算可以分解为<span>:</span>静态内容和动态内容<span>,</span>动态内容又可以分为状态无关<span>(stateless)</span>和状态有关<span>(stateful).</span></span><span style="font-family: 宋体">静态内容通过负载均衡或者<span>CDN</span>就可以简单做到<span>. </span></span><span style="font-family: 宋体">动态内容特别是状态有关的就复杂一点<span>.</span>动态内容中涉及到两大技术<span>:session</span>和<span>cache.</span></span><span style="font-family: 宋体">Session</span><span style="font-family: 宋体">技术导致很多问题<span>.</span>负载均衡中导致<span>session</span>复制的难题<span>.</span>解决的方式是<span>:1. </span>把所有的<span>session</span>数据存储到数据库中<span>,</span>这样通过增加数据库的<span>IO</span>读取<span>,</span>换取应用服务器没有任何<span>session</span>数据问题<span>(</span>另一种做法是把<span>session</span>数据放到<span>cookie</span>或者页面<span>hidden</span>值中<span>);2. </span>负载均衡采用<span>Hash</span>法<span>,</span>固定的把同一个请求绑定到同一台服务器上<span>,</span>这样通过牺牲一定的负载<span>,</span>换取应用服务器的<span>session</span>数据<span>.</span></span><span style="font-family: 宋体">应用<span>session sticky,</span>虽然避免了<span>session</span>的复制<span>,</span>但是依然面临<span>failover</span>的问题<span>.</span>如果应用程序在<span>session</span>中放入了<span>domain object,</span>在<span>failover</span>就容易出问题<span>.</span>一个解决方法是对<span>session</span>的<span>set</span>和<span>get</span>进行拦截<span>,</span>发现是<span>domain object</span>就只在<span>session</span>中记录<span>ID+ClassName</span>结构<span>,</span>而把对象放入<span>cache</span>中<span>,failover</span>时<span>session</span>中内容简单<span>,</span>就通过<span>cache</span>从数据库加载<span>.(SpringSide</span>对于<span>jBPM</span>的扩展采用相同策略<span>)</span></span><span style="font-family: 宋体">Cache</span><span style="font-family: 宋体">技术本事没有特别的要说的<span>,</span>但它处在负载均衡环境中就会带来问题<span>:</span>缓存数据失效<span>(</span>版本低<span>).</span>解决的问题有两种<span>:1. </span>独立缓存<span>+</span>广播通知<span>.</span>一旦数据更新后立刻广播通知<span>,</span>这样引发的问题是通知的管理<span>.2. </span>采用中央缓存<span>,</span>如<span>memcached</span>技术<span>,</span>代价是网络读写<span>.</span></span><span style="font-family: 宋体">Center Cache</span><span style="font-family: 宋体">和<span>Cluster Cache</span>的特性比较如下：<span></span></span><span style="font-family: 宋体">Center Cache</span><span style="font-family: 宋体">没有同步问题，所以，<span>remove/clear</span>的时候，比较有优势，不需要把通知发送到好几个计算机上。但是，<span>Center Cache</span>的所有操作，<span>get/put/remove/clear</span>都是<span>Remote</span>操作。而<span>Cluster Cache</span>的<span>get/put</span>都是<span>Local</span>操作，所以，<span>Cluster Cache</span>在<span>get/put</span>操作上具有优势。<span></span></span><span style="font-family: 宋体">Local get/put</span><span style="font-family: 宋体">在关联对象的组装和分拆方面，优势比较明显。 关联对象的分拆是这个意思<span>, </span>比如，有一个<span>Topic</span>对象，下面有几个<span>Post</span>对象，每个<span>Post</span>对象都有一个<span>User</span>对象。<span> Topic</span>对象存放到<span>Cache</span>中的时候，下面的关联对象都要拆开来，分成各自的<span>Entity Region</span>来存放。<span> </span></span><span style="font-family: 宋体">Topic Region -&gt; Topic ID -&gt; Topic Object </span><span style="font-family: 宋体">Post Region <span>&nbsp;</span>-&gt; Post ID <span>&nbsp;</span>-&gt; Post Object </span><span style="font-family: 宋体">User Region <span>&nbsp;</span>-&gt; User ID <span>&nbsp;</span>-&gt; User Object </span><span style="font-family: 宋体">这个时候，<span>put</span>的动作可能发生多次。<span>Remote Put</span>的开销就比较大。<span> Get</span>的过程类似，也需要<span>get</span>多次，才能拼装成一个完整的<span>Topic</span>对象。</span><span style="font-family: 宋体">&nbsp;</span><strong><span style="font-family: 宋体">三</span></strong><strong><span style="color: black; font-family: 宋体">、架构的开发成本以及品质问题解决讨论</span></strong><span style="color: black; font-family: 宋体"></span><span style="font-family: 宋体">架构一个重要的关注点在于控制开发成本，这点很重要，因为通常讲维护成本是开发成本的<span>3</span>倍。降低开发成本核心，在于提高效率，这也意味着提高了开发对需求的响应时间，而时间对公司来说是重要的。</span><span style="font-family: 宋体">&nbsp;</span><u><span style="font-family: 宋体">1. </span></u><u><span style="font-family: 宋体">问题域<span></span></span></u><span style="font-family: 宋体">问题域可分解为两种类型，业务上和技术上。（又见分解，分而治之真是老祖宗传下的灵丹妙药啊）<span></span></span><span style="font-family: 宋体">1. </span><span style="font-family: 宋体">业务上。问题域分解为，逻辑的纵向抽象层次，以及逻辑的横向模块分解和集成。<span></span></span><span style="font-family: 宋体">2. </span><span style="font-family: 宋体">技术上。问题域分解为，纵向的技术主题，以及横向的技术职责的分解和集成。</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">A. </span><span style="font-family: 宋体">领域基本问题<span></span></span><span style="font-family: 宋体">所以通常而言，领域模型设计中，模块分解，抽象分层和职责分层都是重要手段。问题域为：流程，业务实体和计算（包括规则）。<span></span></span> <div><ol style="margin-top: 0cm"><li class="MsoNormal" style="tab-stops: list 36.0pt"><span style="font-family: 宋体">对象的抽象分解和集成<span></span></span> </li><li class="MsoNormal" style="tab-stops: list 36.0pt"><span style="font-family: 宋体">对象的依赖分解和集成（模块内和模块外）<span></span></span> </li><li class="MsoNormal" style="tab-stops: list 36.0pt"><span style="font-family: 宋体">流程的分解和集成（页面流，工作流以及计算流程）<span></span></span> </li><li class="MsoNormal" style="tab-stops: list 36.0pt"><span style="font-family: 宋体">进程边界：用户请求重定向，以及业务数据持久化等。</span><span style="font-family: 宋体">&nbsp;</span> </li></ol></div><span style="font-family: 宋体">B. </span><span style="font-family: 宋体">领域组件问题<span></span></span><span style="font-family: 宋体">面向对象语言本身没有提供的组件级别的依赖关系集成能力。语言不提供，因为领域组件的粒度太大，超越了语言的范畴。但我们可以通过框架提供，在<span>Java</span>体系中，目前已经有一个较好的解决方案：<span>OSGi</span>（<span>JSR291</span>）。可以完美的解决组件服务依赖关系管理，包括热替换。<span></span></span><span style="font-family: 宋体">同时另一个问题<span>&mdash;&mdash;</span>逻辑分层的问题：保险产品面临的核心层，国家层以及公司层三个逻辑层次分解和集成能力。这点的解决方案可以通过<span>OSGi + Spring</span>来解决，包括了静态差异性替换和动态差异性替换。<span style="background: #d9d9d9"></span></span><span style="font-family: 宋体">还有组件边界保护问题，我们希望限制别的组件访问本组件内部实现，有两种手段可以完成，<span>1</span>是提交部署时，通过在代码提交时的代码检查工具，或者发布时编译工具完成；<span>2</span>是通过<span>OSGi</span>的边界限制能力。</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">C. </span><span style="font-family: 宋体">逻辑替换问题<span></span></span><span style="font-family: 宋体">逻辑的替换根据开发方式不同，有两种类型：基于接口和基于继承；<span></span></span><span style="font-family: 宋体">A. </span><span style="font-family: 宋体">基于接口（包括了静态替换和动态替换）<span></span></span><span style="font-family: 宋体">1. </span><span style="font-family: 宋体">静态替换是<span>override</span>，在<span>OSGi</span>中只要停止原有服务，启用新服务即可，而在<span>Spring</span>中更改相应配置文件即可；<span></span></span><span style="font-family: 宋体">2. </span><span style="font-family: 宋体">动态替换，其实是指运行时<span>Condition Service Locator</span>，在<span>OSGi</span>中可以利用<span>Extension Point</span>（<span>Plug-in</span>）解决，而<span>Spring</span>中只要提供一个类似<span>Service Locator</span>就可以。<span></span></span><span style="font-family: 宋体">B. </span><span style="font-family: 宋体">基于继承（或者静态类）<span></span></span><span style="font-family: 宋体">1.</span><span style="font-family: 宋体">开发时，直接修改源代码编译；<span></span></span><span style="font-family: 宋体">2.</span><span style="font-family: 宋体">编译时，采用<span>AspectJ</span>，在编译时提供替换；</span><span style="font-size: 9pt; color: black; font-family: Tahoma"></span><span style="font-family: 宋体">3.</span><span style="font-family: 宋体">加载时，开发一个新逻辑的同名类，但其加载路径优先于原有类；</span><span style="font-family: 宋体">&nbsp;</span><u><span style="font-family: 宋体">2. </span></u><u><span style="font-family: 宋体">基本手段<span></span></span></u><span style="font-family: 宋体">提高开发效率和品质的基本手段是分解&mdash;&mdash;即充分的分离系统中不同的关注点，好处不用说了，可以并发的工作，每个人面对的问题都简单而容易操作。而与分解对应的集成，只有提供了好的集成能力，分解才成为现实，而只有分解了，才能清晰的提供业务更多适应性。<span></span></span><span style="font-family: 宋体">分解和集成的手段分为编程语言和技术框架两个层面。所谓语言就是强框架，而框架就是弱语言。<span></span></span><span style="font-family: 宋体">A. </span><span style="font-family: 宋体">语言<span></span></span><span style="font-family: 宋体">现代面向对象的语言提供如下能力：抽象和派生能力，以及接口隔离能力。实际提供两种分解和集成能力：<span></span></span><span style="font-family: 宋体"><span>1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体">把逻辑分解在两个层次中，而通过继承的方式把两个部分集成在一起。<span></span></span><span style="font-family: 宋体"><span>2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-family: 宋体">把逻辑的外观和实现分解在两个地方，而通过接口实现的方式把两部分集成在一起。<span></span></span><span style="font-family: 宋体">另一种语言<span>AspectJ</span>或者<span>C#</span>语言<span>2.0</span>之后提供的特性：把流程逻辑，分解在不同的地方，而通过签名匹配，利用代码生成的方式来把几部分集成在一起。</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">B. </span><span style="font-family: 宋体">框架<span></span></span><span style="font-family: 宋体">然而语言提供的集成能力，毕竟底层，而且有限，扩展起来也格外小心。因而技术框架提供另外的集成能力就格外重要：<span></span></span><span style="font-family: 宋体">1. </span><span style="font-family: 宋体">对象关联关系的分解和集成，如<span>Spring</span>提供容器管理能力<span></span></span><span style="font-family: 宋体">2. </span><span style="font-family: 宋体">模块间关联关系的分解和集成，如<span>OSGi</span>，<span>ESB</span>等<span></span></span><span style="font-family: 宋体">3. </span><span style="font-family: 宋体">不同系统的类型分解和集成，如<span>Spring</span>利用动态代理提供的<span>Exporter</span>模式。<span></span></span><span style="font-family: 宋体">4. </span><span style="font-family: 宋体">流程逻辑的分解和集成，如<span>Spring Web Flow</span>以及<span>jBPM</span>。</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">C. </span><span style="font-family: 宋体">设计<span></span></span><span style="font-family: 宋体">说起集成，就不得不提到一种类型的对象存在&mdash;&mdash;<span>VO</span>对象。<span>VO</span>对象是为了集成而存在的；其意义是：<span>1. </span>保护系统的信息边界，提供一种结构可以使其它系统或者组件通过<em>编码方式</em>获取系统内信息的方式；<span>2. </span>保护系统的事务边界，领域对象技术上携带着持久化信息，通过<span>VO</span>可以屏蔽得以屏蔽。常见的<span>VO</span>对象存在于<span>Web</span>层和<span>Domain</span>层。<span></span></span><span style="font-family: 宋体">因此，<span>VO</span>对象的存在只是为了集成而存在，其是否存在的取决于框架的两个方面：对象路径访问能力以及事务边界管理。<span></span></span><span style="font-family: 宋体">Web</span><span style="font-family: 宋体">层<span>VO</span>对象，以<span>SWF</span>为例，早在<span>SWF 1.x</span>时代，框架就提供了丰富的对象路径访问能力，但其<span>Web</span>交互是典型的<span>MVC2</span>方式，事务边界在<span>view</span>的<span>render</span>前关闭，因而导致需要特定的<span>VO</span>对象来避免持久化信息问题；而<span>SWF 2.x</span>时代，<span>view</span>的<span>render</span>是在事务边界内，<span>VO</span>不在需要。<span></span></span> <p class="MsoNormal" style="text-indent: 18pt"><span style="font-family: 宋体">Domain</span><span style="font-family: 宋体">层<span>VO</span>对象，通常是用于不同领域组件间的交互，但随着架构的改进，集成代码独立存在而不再嵌入到组件内部，组件的边界问题保护不复存在；更进一步的是，框架提供自动化的接口适配映射能力的增强。因而<span>VO</span>对象也失去存在的意义。</span></p><span style="font-family: 宋体">BTW</span><span style="font-family: 宋体">：通常语言作为架构的基础引入和更换是有巨大风险的；</span><span style="font-family: 宋体">而通过提供强大的框架能力，框架尽可能多的完成技术问题，并通过元数据，模式以及约定降低业务和框架的耦合。避免因为框架升级带来不必要的成本。</span><span style="font-family: 宋体"></span><span style="font-family: 宋体">&nbsp;&nbsp; </span><u><span style="font-size: 10.5pt">3. </span></u><u><span style="font-size: 10.5pt">其它手段<span></span></span></u><span style="font-size: 10.5pt">从技术手段上，提高开发效率的另外两个手段是代码生成和类库引用。但代码生成和类库引用，都只解决了逻辑的分解能力，没有提供集成能力，所以一般情况下需要提供框架集成，尤其代码生成需要在系统的最外层，避免集成带来的问题。<span></span></span><span style="font-size: 10.5pt">&nbsp;</span><u><span style="font-family: 宋体">4. </span></u><u><span style="font-family: 宋体">学习成本<span></span></span></u><span style="font-family: 宋体">对于开发团队来说，额外面临一个问题，组织内部的学习成本问题。<span></span></span><span style="font-family: 宋体">1. </span><span style="font-family: 宋体">需要保持分解以及集成能力本身的简约性<span></span></span><span style="font-family: 宋体">这个&hellip;&hellip;其实是一个<span>culture</span>问题，不再罗唆！<span></span></span><span style="font-family: 宋体">2. </span><span style="font-family: 宋体">采用模式和约定是减少学习成本的另一种手段。<span>ROR</span>的兴起就是最好的例证。</span><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体"><span>&nbsp;&nbsp;&nbsp; </span></span><strong><span style="font-family: 宋体">成本还表现在组织的划分上，应用开发<span>/</span>框架开发，而在每个层面又划分为横向模块划分。</span></strong><span style="font-family: 宋体">&nbsp;</span><span style="font-family: 宋体">总结一下，解决架构面临开发成本问题需要如下几个方面：<span></span></span><span style="color: black; font-family: 宋体">0. </span><span style="color: black; font-family: 宋体">问题域</span><span style="color: black; font-family: 宋体"></span><span style="color: black; font-family: 宋体">1. </span><span style="color: black; font-family: 宋体">分解与分层</span><span style="color: black; font-family: 宋体"></span><span style="color: black; font-family: 宋体">2. </span><span style="color: black; font-family: 宋体">架构与类库</span><span style="color: black; font-family: 宋体">，<span>Spring</span>，<span>Hibernate</span></span><span style="color: black; font-family: 宋体">。起支撑性作用。</span><span style="color: black; font-family: 宋体"></span><span style="color: black; font-family: 宋体">3. </span><span style="color: black; font-family: 宋体">模式和技巧</span><span style="color: black; font-family: 宋体"></span><span style="color: black; font-family: 宋体">4. 领域模型</span><span style="color: black; font-family: 宋体">5. 方法论</span><span style="color: black; font-family: 宋体"></span><span style="color: black; font-family: 宋体">5.1.</span><span style="color: black; font-family: 宋体">开发方法</span><span style="color: black; font-family: 宋体">：<span>OO</span>（</span><span style="color: black; font-family: 宋体">设计模式</span><span style="color: black; font-family: 宋体">），<span>FP(</span></span><span style="color: black; font-family: 宋体">函数式编程</span><span style="color: black; font-family: 宋体">)</span><span style="color: black; font-family: 宋体">。<span></span></span> <p class="MsoNormal" style="text-indent: 21pt"><span style="color: black; font-family: 宋体">5.2.</span><span style="color: black; font-family: 宋体">设计方法</span><span style="color: black; font-family: 宋体">：<span>Domain Model Prototype</span></span><span style="color: black; font-family: 宋体">和业务行为的分析模式。</span></p><span style="color: black; font-family: 宋体">&nbsp;</span><u><span style="color: black; font-family: 宋体">5. </span></u><u><span style="color: black; font-family: 宋体">质量问题<span></span></span></u> <p class="MsoNormal" style="text-indent: 21pt"><span style="color: black; font-family: 宋体">架构面临的</span><span style="color: black; font-family: 宋体">品质问题，则通过自动化测试，代码检测工具来完成。</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/125766#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 20 Sep 2007 23:09:23 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/125766</link>
        <guid>http://yimlin.javaeye.com/blog/125766</guid>
      </item>
      <item>
        <title>从EAI到SOA</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/113841" style="color:red;">http://yimlin.javaeye.com/blog/113841</a>&nbsp;
          发表时间: 2007年08月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>（2008-1-12更新）&nbsp;</strong></p><p>&nbsp;</p><p><strong>写在前面</strong><br />SOA现在越发闹腾的厉害了，各种宣传越来越多，都把SOA吹上天；到底SOA是什么，有啥神奇之处，真的想宣传说的那么好吗？看了种种文章，只是越发混沌。<br />罢了，俺做技术的，商业上的宣传，俺不在意。既然SOA只是理念，那么俺就从它的支持技术来看看，从过去到现在的区别，看看SOA到底是啥！<br /><br /><strong>从EAI到SOA<br /></strong>从集成的角度看，集成面临的问题如下<br />A. 数据集成，包括<strong>信息交换</strong>和<strong>交换管理</strong><br />B. 业务集成，包括<strong>服务管理</strong>和<strong>流程管理</strong><br /><br />1.史前时代<br />现象： <br />1.1. 采用原始交换手段&mdash;&mdash; 点对点的传输通道依赖，机制是Socket或者后来的RMI。存在两个问题：1. 只能在同一平台上传输数据，无法处理异构系统数据传递，比如RMI没有办法和.NET通信；2.如果目标地址变化或者故障，就出问题。由于没有更多的交换管理能力，点对点的交换越多导致的管理成本就越高；<br />1.2. 数据格式绑定，依赖于双方的严格的私有格式。 </p><p>1.3 扩展&mdash;&mdash;EDI的出现解决了异构系统的数据传递格式标准。</p><p>2. EAI时代。<br />现象：<br />2.1. 基于中间件系统，采用了集中式管理的消息交换管理系统，就是所谓的信息总线技术&mdash;&mdash;MQ技术，包括了两种不同的消息传递方式。<br />相对以前，统一了内部信息格式（这是基本工作），提供较好的数据交换管理。存在的问题：1. 只关注于消息的内部格式和传递，而忽略的各个系统的集成程序：没有提供对于这些集成程序的打包和管理；2.系统是紧耦合的，不同消息系统间的通信困难。</p><p>2.2 扩展&mdash;&mdash;JCA. 相对于JMS，JCA关注于对于旧有系统数据访问，相当于访问数据库的JDBC；</p><p>2.3 发展：采用开放的自解释的XML作为系统通信格式，采用松耦合的Web Service作为通信手段。</p><p>3. SOA时代。<br />现象：<br />3.1. 天然的引入XML和Web Service技术。<br />3.2. 出现了ESB这样的技术平台。ESB是SOA的最佳基础平台。ESB与MQ一样也提供统一的消息格式，并管理消息传递；不同的是，ESB重新发现了集成程序的价值&mdash;&mdash;在集成环境中，集成程序代表其背后的应用系统，这些程序提供了各个子系统的应用服务，它们才是集成环境中最有价值的部分，是集成环境中的First Class；并对这些程序提供统一的打包方式，并提供运行时管理。另一方面，ESB把集成程序进一步分解为服务（业务逻辑）以及Endpoint（服务的入口点），这样服务不仅仅是可重用，而且是可组装编排; 可快速注册发布; 质量可监控；生命周期可管理的，也正是因此，<br />3.3. 出现了所谓的BPEL等面向业务的能力开始显现，提供流程管理。</p><p><strong>EAI和SOA的区别</strong><br />EAI时代是企业的收发室，只知道信件本身，对于信件收发者的身份却不知道，更不知道信件所处的流程体系。<br />SOA时代是企业的办公室，不仅知道信件本身，对于信件收发者的身份都清楚，还可以知道信件所处的流程体系。就可以很容易的组合各个服务，建立起各种虚拟专员（specialist），响应业务的变化。</p><p><strong>SOA的产商利益<br /></strong>SOA的基础框架提供了支撑平台，也就是可能性。但要实现SOA的理想，却还需要对业务重新梳理，发现和重用IT资产，正如ERP那样，这才是SOA实施的关键所在；而IBM这样的公司正拥有这样的咨询能力，所以IBM每年都投入大量的资金来推动SOA的应用，就在情理之中了。</p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/113841#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 18 Aug 2007 14:42:25 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/113841</link>
        <guid>http://yimlin.javaeye.com/blog/113841</guid>
      </item>
      <item>
        <title>项目笔记：dao，web，模块边界以及Model分类</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/96993" style="color:red;">http://yimlin.javaeye.com/blog/96993</a>&nbsp;
          发表时间: 2007年07月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Author：<a href="../../../">Anders小明</a><br /><br /><strong>先从后端的dao说起吧：</strong><br />已有项目的开发以及appfuse的开发，都属于传统的开放方式，内部有dao，外部还有service。<br /><br />这样的开发方式太学院了，每次改动其实影响面很大，要改二个类，两个接口。平时不忙的时候也就算了，项目一紧的话，大家就乱来的了。相当部分都是最外层接口实现类就直接访问了数据库，而不走规范路线。<br /><br />现在对外提供一个repository的service接口（加载该领域模块的root对象，以便程序利用root对象来游走，ddd推荐的做法），然后内部有一个dao接口，继承该repository接口，提供一些内部使用的额外服务，比如一些数据库查询。接着有一个类继承框架提供的如spring的HiberanteTemplate同时实现dao接口，这样来改动的规模就比较小，一个接口和一个实现类。这个思路和SS2的思路是一样的。<br /><br />框架数据访问已经提供一些公共的操作简化开发，当然还利用泛型等。此外，Ibatis3.0的设计思路，将是下一步的工作目标。<br /><br />另外：通过root对象游走访问领域对象，性能可能有点问题。但这是可以接受的，因为从开发的角度看，通常都root下的对象都是和root对象一起出现操作的，不可能单一出现一个root以外的对象来操作以及显示的。</p><p><strong>现在就说说Web层：</strong><br />由于servcie对外都是开放domian model了，不再提供VO对象，免去无谓的copy property操作。而把VO的这部分工作交给自行开发的界面设计工具，因此在设计页面的时候其实已经知道的页面的访问的元素以及对象路径，在设计页面完成后由该设计工具生成VO对象和映射对象。这样VO对象的设计产生，以及property的copy都是工具完成，无须人工干预。<br /><br />另外Web层采用SWF，除了完整的抽取出流程控制流转逻辑，还完整的封装了对于Request以及Response的数据访问操作。这样一个流程中调用了那些model以及servcie都非常清楚。将来有可能通过工具像规则引擎那样可以提供给业务人员直接使用。同时没有了传统的Control类结构的存在，极大的提高了开发效率问题。</p><p><strong>模块边界的处理包括两个部分：</strong><br />一是行为的边界集成上，比如订单管理模块对财务模块的行为要求。<br />订单管理模块自行设计它所需要的接口，由一个集成模块提供adapter类，来适配到财务模块合适的service上。这样就解决了旧系统设计现有的问题，旧系统现在都是直接调用财务模块提供的API，这样的做法实际上是把集成工作放在了订单管理模块下。<br /><br />二是对象的边界集成，是这样做的。<br />订单明细对象（OrderItem）会关联一个产品对象，不过产品对象是是属于产品模块而非订单模块，对于订单模块只关心id而并不使用对象，但在规则引擎或者界面设计工具这样的集成环境却是需要产品对象的。我们采用代码生成的方式，在订单明细对象上加一个annotation，比如Integration的annotation标识，使用aspectj在编译上enhancement生成的class，使得订单明细对象在集成环境上可以拿到产品对象，这算是一个集成方面。</p><p><strong>最后是domain model部分：</strong><br />一类是类似保险中的保单对象这样的长生命周期对象；<br /><br />另一类是transaction交易过程的对象，几乎没有生命周期的；<br /><br />最后一类是request/response对象。这类对象以前没有识别，通常和VO混在一起；但是在IAA中以及电信业的模型是这类对象是独立存在，并被持久化的。<br />当然他们也是几乎没有生命周期的，request对象建立在增量更新上很有用。<br /><br />reponse对象目前没有特别用途，依然采用VO处理。<br /><br />request对象的建立有两个好处。<br />1. 以前直接更新订单对象，虽然记录了log但是只知道减肥前，减肥后。<br />2. request对象的第二个好处，可以解决一个业务事务跨越两个物理事务的设计问题。即一个业务请求可以分多次累进完成，比如分两天来处理，每天完成一个部分，但在完成之前，所有操作数据都不生效。在没有持久化request对象前，我们只能把操作的数据写到临时表上。<br /><br />此外，由于某个领域模块的增量操作通常从一个根对象开始，所依赖的criteria可以从request中加以识别并通过框架提前加载，而service对象的方法接受传递对象而不再关心对象的加载工作；同时也可以通过框架处理基本数据复制工作，这样程序只关心关联对象的操作。这个做法和SS2是一样的，只不过采用的是AOP的处理方式。</p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/96993#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 02 Jul 2007 21:56:53 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/96993</link>
        <guid>http://yimlin.javaeye.com/blog/96993</guid>
      </item>
      <item>
        <title>软件工程中的经济行为与软件架构师的工作 </title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/92164" style="color:red;">http://yimlin.javaeye.com/blog/92164</a>&nbsp;
          发表时间: 2007年06月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="postcontent"><p>Author：<a href="../../../">Anders小明<br /></a><br /><strong>软件工程中的经济行为</strong><br />1. 在传统财务概念下，软件公司或者商业公司IT部门的员工，是公司的成本中心。对于一个定额合同项目，员工工资成为项目中唯一的可变成本。<br />2. 因此，尽可能的缩短工期，减少人员投入就成为缩减成本的基本方式。<br /><br />3. 软件的分工模式，以及传统的waterfall&mdash;&mdash;流水线的工作方式，决定了问题发现的越早，修正的成本越低。<br />4. 有两种手段来减少工期，工程上采用迭代，并让迭代周期尽可能短，以及从技术上对于问题域进行分解，建立有效的边界。<br />5. 迭代周期减少的目的是减少分工环节中尤其是第一个环节的不确定性带来的问题，而对问题域的技术分解是解决开发环节的质量问题。因为通常维护成本是开发成本的3倍，提高质量可以减少项目后期对前期开发代码的维护成本。<br />6. 同时，分解的好坏将决定了是否可以在开发环节中引入测试环节的工作，从而提供质量；并减少修正缺陷以及测试的工作量。<br />7. 质量是难以衡量的，通常以能否工作为准。而通常意义上的高质量代码是可以较容易调整以及适应变化，但是不容易识别。<br />为保障质量，通常的手段是代码审查，但在工程中完全代码审查承保高昂，同时工作量大，并难以评估（评估结果有时因人而异）。而各种各样的代码规范检查工具只能保障最低要求。<br /><br />8. 在进度落后于计划时，管理人员通常会下达行政指令&mdash;&mdash;要求项目成员全力以赴赶上计划。在此情况下，工程人员通常牺牲质量换取开发进度，同时把质量问题推后发生。<br />9. 软件开发是脑力劳动 而非体力劳动；因此工程人员有很大的权利，他们有选择质量和进度的能力，工程人员的工作状况严重影响产出和进度。而上述情况的发生是人在利益环境下的自然选择，对于工程人员的指责无济于事。<br />10. 正确的工作方式是，为工程人员创造合适的条件以便工程人员做正确的事。<br /><br /><strong>软件架构师的工作<br /></strong>软件工程的三要素：工具，方法和过程。然这所有的一切是规范人的分工和行为，提高人的生产效率，降低成本。<br />架构师的工作围绕这个三个要素进行。<br />1. <strong>工具评估</strong>（包括<strong>开发平台</strong>，开发语言，开发工具以及辅助工具）。<br />A. 用好的工具提高生产效率，使人关注于有效工作内容，从而减少不必要工作量，减少成本。<br />特别对于分工下的团队开发尤为重要。典型的分工是流水线，一步接一步。减少上一个环节的工作量，如开发环节，不仅提前下一个环节&mdash;&mdash;测试的时间。<br />B. 用好的工具保证质量&mdash;&mdash;另一种生产效率。<br />保证质量有利于减少工作上的反复，尤其影响到测试工作量，从而减少成本。<br />提高生产效率的同时有利于保证士气。</p><p>2. <strong>方法论选择</strong><br /><strong>解决问题的办法就是分治。要被分解问题域是：数据（模型），计算和流程；而如何分解问题便是架构师的取舍啦，流行的有OOD和AOSD两种。</strong></p><p>在大比例结构中必需考虑的是：抽象分层，技术分层以及模块切分。抽象分层(包括模型，计算以及流程抽象)以及模块切分是基于业务的纵向以及横向分解。而技术分层则是对于业务逻辑的技术分类，分类本身还可能涉及到平台技术限制。所有分解都涉及到上下文的边界建立&mdash;&mdash;不仅仅是业务逻辑同时也是技术边界。</p><p>分解问题必需考虑人的因素，降低分解后的知识学习阻力，保持知识的内聚以及有效组织是保证分解成功的关键。这些工作将有效保证开发人员不做出破坏系统分解边界的行为。<br />考核分解的有效性：保证开发的效率。分解的目的是降低解决问题的难度，从而提高生产效率，如果分解方案增加了系统适应变化的能力，那么分解方案可能是错误的。</p><p><strong>除了开发方法还有开发方式，已知的三种开发方式：编程式，声明式以及产生式。</strong><br />开发方式的选择和技术分层有相当的联系，一般认为除了Service以及Model，其它的技术分层代码尽量使用声明式以及产生式开发方式来完成，减少建立以及维护成本，提高效率。</p><p><strong>对于Domain Model还要分析model的生命周期，明确设计主题。</strong></p><p>3. 过程选择<br /><strong>选择的过程，最重要的是让问题及早暴露（降低成本），尽早让用户使用（创造价值）.<br /></strong>敏捷方法就是要让问题更快的暴露，让功能更快的实现。</p><p><strong>文档</strong>，文档是过程的一个重要产物。文档也是保证知识传递的。<br />在问题分解情况下的开发角色分为三种：开发者，使用者以及维护者。开发者写的文档给后两者看，最最关键是写给使用者的文档。</p><p>所有的决定都是基于利益和成本考虑的。</p></div>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/92164#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 19 Jun 2007 22:22:19 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/92164</link>
        <guid>http://yimlin.javaeye.com/blog/92164</guid>
      </item>
      <item>
        <title>项目笔记：命名，分层，文档以及细节</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/83346" style="color:red;">http://yimlin.javaeye.com/blog/83346</a>&nbsp;
          发表时间: 2007年05月26日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Author：Anders小明.</p><p><strong>0. 业务分包.</strong></p><p>在package命名上，摈斥了Appfuse以及SpringSide中出现的model, service以及manager等技术名词,取而代之的是业务名词和动词,使得行为和模型物理上内聚。我们以为对于开发人员来说，通过包名以及类名获取其业务功能远比了解其技术分类更有助于设计和开发。</p><p>这里借用springside的package前缀:</p><p>org.springside.insurance.product<br />org.springside.insurance.quotation<br />org.springside.insurance.quote<br />org.springside.insurance.contract<br />org.springside.insurance.newbiz<br />org.springside.insurance.cs<br />org.springside.insurance.claim<br />&nbsp;</p><p><strong>1. 抽象分层</strong></p><p>业务是有抽象层次的. 往大的方面，可以见《<span style="color: #366900"><a href="http://www.blogjava.net/AndersLin/archive/2007/01/05/91876.html">Domain Model：基于抽象的分层结构</a></span>》。这里只往小的方面说，比如保险中accident就拥有自己独立的业务概念和行为，并且这些概念和行为是紧耦合的。我们的目标是类似这样的业务抽象层次，我们只要发布相应的类和资源文件就可以在服务器上热部署。包的命名结构如下：</p><p>org.springside.insurance.accident.contract<br />org.springside.insurance.accident.product<br />org.springside.insurance.accident.quotation<br />org.springside.insurance.accident.quote<br />org.springside.insurance.accident.newbiz</p><p>当然可能有朋友说：我也可以这样命名，一样可以达到同样的目的。</p><p>org.springside.insurance.contract.accident<br />org.springside.insurance.product.accident<br />org.springside.insurance.quotation.accident<br />org.springside.insurance.quote.accident<br />org.springside.insurance.newbiz.accident</p><p>的确如此，对计算机或者jvm来说没有区别，它不管怎么命名。但是程序是写给人阅读的，我们希望在命名上能够尽量提供信息和暗示。我们以为，上一种命名方式更容易阅读，同时能给出紧耦合的暗示。</p><p>&nbsp;</p><p><strong>2. 技术职责分层</strong></p><p>减少不必要的技术职责层次. 尤其是减少了Web层的VO. Web层直接访问Domain Model. </p><p>当然关于这点在技术上有过很多讨论. 我只想说的: 是否建立VO的题关键在于边界的确立, 建立边界的情况有两种:人员分工以及边界双方存在替换可能. 通常意义上Web和Domain都是一个人开发的,没有特别分工(也有可能不是同一个但没有出现并行开发, 美工除外)，即便此时强制要求设计VO，也会发现设计出来的VO和model非常的类似，但增加了不必要工作量，包括copy属性操作，以及null处理（是页面没有提交该值，还是user要清空该值）; 同时很少存在Web不替换而替换后台Service以及Model的情况. 因此我们可以得出一个结论：页面依赖于模型. </p><p>这样系统需要写代码的只有Service和Model两类对象&nbsp;</p><p>还有一个类对象，传统的Dao，这类对象比较麻烦。采用DDD中提出的Aggregate模式，把domain模型区分开来。Domain Model利用主对象作为root，游走访问其关联对象，减少dao爆炸，使OLTP中的dao保持在较少的一个层次；对于Report或者OLAP则利用其它SQL保持性能。（关于这块目前的没有遇到大的问题，不算成功了，还在尝试中）<br />&nbsp;</p><p><strong>3. 业务职责设计模块</strong></p><p>主要工作在模块间的交互接口设计：适应性接口和公共接口分离。 (原则:不要以用户无法使用或不感兴趣的东西扰乱类的公有接口). 在我的另一篇blog《<a href="http://www.blogjava.net/AndersLin/archive/2007/03/12/103387.html"><span style="color: #366900">模块的接口设计</span></a>》中有描述.</p><p>&nbsp;</p><p><strong>4. 代码和文档的统一。</strong></p><p>文档的维护一直是比较麻烦的问题。尝试通过Annotation等元数据来帮助我们简化工作。</p><p>首先Annotation能够提供的信息有三个级别：代码级别（IDE支持, JavaDoc），编译级别（编译器支持，代码生成），运行时级别（框架反射）。尤其是后两种，既是设计又是代码。除此外Spring的XML还包括很多信息，例如事务声明，Web Flow的页面流程信息等。</p><p>尝试通过应用更多既是设计又是代码的元数据，在借助一定手段收集这些信息，减少在文档上的工作量。</p><p>&nbsp;</p><p><strong>5. 设计上的细节</strong></p><p>在Service对象的设计上。按model是骨架, service是肌肉,而rule是神经的职责分类.service对象的设计以行为为主线,利用template模式把rule做一定分离。另一个原因是系统应用了规则引擎，采用这样的设计有利于单元测试。</p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/83346#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 26 May 2007 12:10:19 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/83346</link>
        <guid>http://yimlin.javaeye.com/blog/83346</guid>
      </item>
      <item>
        <title>Web框架比较</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/82312" style="color:red;">http://yimlin.javaeye.com/blog/82312</a>&nbsp;
          发表时间: 2007年05月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="postcontent">
<div class="postcontent">
<div class="postcontent">Blog好久没有更新了, 最近一直忙于一个新项目,在这个项目中尝试很多新的做法,准备收集一下放上blog来,这里先放一篇关于Web框架的,基本是老调重谈了. 该文写于4月,主要是为了和朋友讨论问题,有些地方可能不正确
<table cellspacing="0" border="1" cellpadding="0">
    <tbody>
        <tr>
            <td valign="top" width="150">
            <p>&nbsp;</p>
            </td>
            <td valign="top">
            <p>Struts</p>
            </td>
            <td valign="top">
            <p>JSF</p>
            </td>
            <td valign="top">
            <p>Tapestry</p>
            </td>
            <td valign="top">
            <p>ASP.NET</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Architecture</p>
            </td>
            <td valign="top">
            <p>跳转模型</p>
            <p>MVC</p>
            </td>
            <td valign="top">
            <p>跳转模型</p>
            <p>Front Controller+组件化编程</p>
            </td>
            <td valign="top">
            <p>页面模型</p>
            <p>Page Controller+组件化编程</p>
            </td>
            <td valign="top">
            <p>页面模型</p>
            <p>Page Controller+组件化编程</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Programming Model</p>
            </td>
            <td valign="top">
            <p>业务逻辑：</p>
            <p>Struts1中需要继承基类；Struts2是POJO的模型；</p>
            <p>页面逻辑：</p>
            <p>有很不同实现，可以是JSP，也可以是通过模版引擎渲染。</p>
            </td>
            <td valign="top">
            <p>业务逻辑：</p>
            <p>POJO的编程风格；</p>
            <p>页面逻辑：</p>
            <p>主要是JSP，也可以用HTML风格。</p>
            </td>
            <td valign="top">
            <p>业务逻辑：</p>
            <p>Taperstry4需要继承基类；但Taperstry5就是POJO风格；</p>
            <p>页面逻辑：</p>
            <p>普通的HTML。</p>
            </td>
            <td valign="top">
            <p>业务逻辑：</p>
            <p>需要继承基类；</p>
            <p>页面逻辑：</p>
            <p>类似JSP，但不同的是，该页面实际是业务逻辑类的子类。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Request Process</p>
            </td>
            <td valign="top">
            <p>&nbsp;</p>
            </td>
            <td valign="top">
            <p>由官方定义的六个步骤组成；</p>
            </td>
            <td valign="top">
            <p>取决于Engine Service。</p>
            </td>
            <td valign="top">
            <p>由官方定义的15个步骤组成。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Navigation</p>
            </td>
            <td valign="top">
            <p>Path和Action绑定，需要配置文件解析。</p>
            </td>
            <td valign="top">
            <p>通过faces-config.xml配置文件完成。</p>
            </td>
            <td valign="top">
            <p>URL是全局的，没有额外的配置文件；</p>
            <p>除非显式跳转，所以行为都在本Page上。</p>
            <p>而跳转分两种：</p>
            <p>1. DirectLink写在页面上</p>
            <p>2. 在代码逻辑中定义页面跳转逻辑。</p>
            </td>
            <td valign="top">
            <p>同Tapestry类似。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Event handling</p>
            </td>
            <td valign="top">
            <p>无</p>
            </td>
            <td valign="top">
            <p>页面定义事件发起；两种方式参数传递方式：一种分离传递；另一种通过FacesContext。</p>
            </td>
            <td valign="top">
            <p>页面定义事件发起；直接赋予参数，没有参数个数限制；除此外还有内置的生命周期相关的event</p>
            </td>
            <td valign="top">
            <p>类似Swing的事件控制方式。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Component State</p>
            </td>
            <td valign="top">
            <p>无</p>
            </td>
            <td valign="top">
            <p>没有状态维护机制，每次request都从建组件。</p>
            </td>
            <td valign="top">
            <p>提供组件状态的维护机制。</p>
            </td>
            <td valign="top">
            <p>提供组件状态的维护机制。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Component Dev</p>
            </td>
            <td valign="top">
            <p>无</p>
            </td>
            <td valign="top">
            <p>基于JSP Tag的开发方式。</p>
            </td>
            <td valign="top">
            <p>开发方式类似Page, 逻辑代码和页面分离，页面输出使用HTML。</p>
            </td>
            <td valign="top">
            <p>开发方式类似Page；逻辑代码和页面分离；页面输出可以复用已有的组件</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>View</p>
            </td>
            <td valign="top">
            <p>有很不同实现，可以是JSP，也可以是通过模版引擎渲染。</p>
            </td>
            <td valign="top">
            <p>主要是JSP，也可以用HTML风格。</p>
            </td>
            <td valign="top">
            <p>HTML</p>
            </td>
            <td valign="top">
            <p>类似JSP页面。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Validation and Conversion</p>
            </td>
            <td valign="top">
            <p>&nbsp;</p>
            </td>
            <td valign="top">
            <p>提供了多种方式支持，但客户端验证支持不好，同时在form一级的支持不好，通常需要项目自己定制。</p>
            </td>
            <td valign="top">
            <p>同样提供多种方式支持；此外提供客户端的Validation；天然地支持form一级支持。</p>
            </td>
            <td valign="top">
            <p>类似Tapestry。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>I18N</p>
            </td>
            <td valign="top">
            <p>较好的支持。</p>
            </td>
            <td valign="top">
            <p>较好的支持。</p>
            </td>
            <td valign="top">
            <p>很好的支持，额外提供预览功能。</p>
            </td>
            <td valign="top">
            <p>&nbsp;</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Testability</p>
            </td>
            <td valign="top">
            <p>Struts1的测试不容易，Strut2测试容易简单。</p>
            </td>
            <td valign="top">
            <p>测试支持简单容易。</p>
            </td>
            <td valign="top">
            <p>Tapestry4的测试不容易，不过Tapestry5的测试可以很简单。</p>
            </td>
            <td valign="top">
            <p>不容易测试。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Extensibility</p>
            </td>
            <td valign="top">
            <p>&nbsp;</p>
            </td>
            <td valign="top">
            <p>良好</p>
            </td>
            <td valign="top">
            <p>良好</p>
            </td>
            <td valign="top">
            <p>良好</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Industry Momentum</p>
            </td>
            <td valign="top">
            <p>广泛使用，目前各种资源都不错。</p>
            </td>
            <td valign="top">
            <p>JSF业界标准，业内厂商支持会比较多，不过未必不会出现EJB2的结局。</p>
            </td>
            <td valign="top">
            <p>应用范围小于Struts，之前的版本学习曲线太高。</p>
            </td>
            <td valign="top">
            <p>微软地主老财，有大把的钱；此外，大量的第三方公司提供支持。</p>
            </td>
        </tr>
        <tr>
            <td valign="top">
            <p>Migrate</p>
            </td>
            <td valign="top">
            <p>&nbsp;</p>
            </td>
            <td valign="top">
            <p>从Struts迁移不难；</p>
            </td>
            <td valign="top">
            <p>从Struts或者JSP迁移难度较大些。</p>
            </td>
            <td valign="top">
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<br />
因为工作原因,最近一直在使用Spring Web Flow,与之上几个Web框架对比优点是:<br />
1. 页面流程明确, 除去JSF外,其它几类框架要明确获取页面流程信息并不容易. 对于企业开发来说,这点其实蛮重要的.&nbsp;一般的互联网网站没有特别的好处.<br />
2. 不需要再写Action等Web控制类. 虽然Struts2,JSF和Tapestry都是POJO了,但依然存在属于Web层范畴的类,而Spring Web Flow不需要,逻辑写在Flow文件中, 直接访问Service对象,获取Domain Model(我们还同时省略了VO). 当然这点可能有同学持反对意见.仁者见仁了!<br />
3. Spring Web Flow提供单元测试能可以容易覆盖页面流程了. </div>
</div>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/82312#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 23 May 2007 23:06:25 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/82312</link>
        <guid>http://yimlin.javaeye.com/blog/82312</guid>
      </item>
      <item>
        <title>纪念逝去的恋情</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/72297" style="color:red;">http://yimlin.javaeye.com/blog/72297</a>&nbsp;
          发表时间: 2007年04月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          终于想通了，其实发现自己还不太笨！<br /><br />2007.4.15我的女友——确切的说是前女友——订婚了——当然新郎不是我！<br />不去怪她，她只不过是在两份感情中选择了一份有光明前途的感情，而我不是那个幸运儿！<br />不去想是非对错，不去想先来后到！感情的世界中，没有对错，只有缘深缘浅！<br /><br />过去的两年半中，我们跨越距离一起渡过很多难忘的日子，都将是我记忆中的最美好的回忆！<br />只是还有些遗憾：<br />1. 没有陪你好好逛南京，逛杭州；希望今后有机会你带上你的老公，大家一起去西藏，我知道你很想去那里的。<br />2. 自己不好，两年半中让你哭了好几次，我应该让你笑的，你笑的样子是最美的！<br /><br />从分手的那天起就想过回头，也想继续，付出的感情毕竟不是说放就放的。<br />然而想想既然你已是别人的未婚妻，在回头纠缠就是第三者了，己所不欲勿施于人；既然当初自己说出分手就不后悔。<br />你已经找到属于你的那个男孩——不是吗？不然以你的性格你不会愿意和他订婚——就应该坚决的支持你。<br /><br />你说：不要想太多。我听你的；不再回头，不再纠缠，不再给你电话让你为难；我失去了一个本来可以变成亲情的爱情，但我还有友情。<br /><br />爱你不一定要拥有你，曾经年少轻狂以为自己能了解，现在才知道要体会这句话并不容易。<br />总算我们还可以做好朋友，你的婚礼我一定会去；以后常带你老公回来一起打牌唱歌。<br /><br />今后有开心的事，不开心的事都要及时通知我。<br />衷心的祝你幸福！<br /><br />从2007.4.18这一刻起，我正式告别过去，恢复单身！
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/72297#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 18 Apr 2007 23:50:51 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/72297</link>
        <guid>http://yimlin.javaeye.com/blog/72297</guid>
      </item>
      <item>
        <title>编译器的春天</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/70129" style="color:red;">http://yimlin.javaeye.com/blog/70129</a>&nbsp;
          发表时间: 2007年04月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          为什么这么说呢？这样说是有理由的：<br />
先来看看Java世界中看看最近新热的groovy，以及早已名声在外的aspectj，无一不是通过编译器支持新的语法结构：一个提供了全新动态语言，一个支持面向方面扩展语法。<br />
而MS世界里，且按下各个动态语言的CLR实现不表，就单看C#语法本身，其delegate，partial等都是利用编译器来完成处理的。
<p>想想也是：<br />
VM的更新工程浩大且影响面广，而在编译器中动手脚简直是低风险高收益的买卖，何乐而不为。<br />
而手握像antlr这样一神兵利器更是如虎添翼。</p>
<p>那么下一个带来惊喜将会是什么？DSL还是其它？</p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/70129#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 11 Apr 2007 23:24:16 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/70129</link>
        <guid>http://yimlin.javaeye.com/blog/70129</guid>
      </item>
      <item>
        <title>模块的接口设计</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/59364" style="color:red;">http://yimlin.javaeye.com/blog/59364</a>&nbsp;
          发表时间: 2007年03月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Author:&nbsp;<a href="../../../">Anders小明</a>&nbsp; </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 任何一个有一定规模系统，通常会把系统做一定分解降低分析设计开发的难度，模块划分是一个比较常见的方式。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 而在模块的划分，及其分析设计的实践中，模块和模块的交互接口最为重要，通常我们认为这些接口应该通用稳定，然而如何设计每个模块对外提供的接口却是一个不易的问题。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 实践中，极有可能出现两种状况：接口维护失控或者过严而死板（而影响开发）。接口失控是因为接口的维护太过随意，因为A模块的需要就轻易在B模块中添加一个接口（方法），导致该接口（方法）非独立性（基本上只给模块A的这个功能点使用），或者是接口的控制过严，导致或者工作效率不高，或者接口的易用性不好。 <br />原因在于：接口是两个模块间的耦合，而发生的种种问题在于模块耦合太过紧密；同时实践中，把模块对外提供的接口，与模块需要实现的外部模块的接口混为一谈。&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据指导原则：为了降低耦合只有在中间加一层。一种可行的实践是：不轻易为模块设计对外提供的接口（方法），除非是通过重构得来的；模块对外提供两种类：一个是需要外部模块实现的接口（接口设计从本模块需要出发，当然每个接口尽管是为某个功能点服务，但也要注意其在模块内通用性），另一个是其它模块要求本模块实现的接口的实现类。&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 即：A模块拥有一些需要B模块实现的接口（A模块对B模块的要求），而B模块中也有要求A模块实现的接口，因而A有这些接口的实现类。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这种实践方式的好处在于：模块的接口就多了一层隔离降低了耦合，把接口的通用性和接口的适应性分离，又明确了模块的边界，使得接口在日后的优化和调整有了缓冲。 </p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/59364#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 12 Mar 2007 20:36:08 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/59364</link>
        <guid>http://yimlin.javaeye.com/blog/59364</guid>
      </item>
      <item>
        <title>对Robbin《domain model的延伸讨论(重新编辑) 》一文质疑</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/57477" style="color:red;">http://yimlin.javaeye.com/blog/57477</a>&nbsp;
          发表时间: 2007年03月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><font face="Arial"><font face="Arial"><font face="Arial"><font face="Arial">《</font>domain model的延伸讨论<font face="Arial">》</font> </font>http://www.javaeye.com/topic/57075</font><br />
robbin试图用两个例子来支撑其观点似乎太过牵强！</font></p>
<p><font face="Arial">1. ruby的代码中是domain model直接包含了操作集合的代码，java的理念则不是如此。用来比较优劣是否妥当暂且不说，首要的问题在于：domain model理念是哪个？是允许一个对象包含自己的集合操作还是不可以？<br />
我以为目前并没有定论。<br />
退一万步说，就算是允许，那么java可以参照的同级别的语言是有些ADO.NET的C#（C#是可以和ruby在ActiveRecord可比的），java想做不难！<br />
再次，允许是一回事，好不好又是另一回事。</font></p>
<p><font face="Arial">回到正题，既然是比较就要标准统一，表面上看都是同一个题目，其实背后的理念是不一样（由此引发的种种设计实现上的变化，robbin就得出了ruby可以做更多的逻辑，因而&hellip;&hellip;），<br />
然而我以为这样的比较是支持不了robbin的观点的。</font></p>
<p><font face="Arial">2. robbin说：&ldquo;但是Java如果不使用IoC方式注入，而是直接调用依赖类的静态方法，行为就被限定死了，复杂的类依赖关系的创建和织入就是一个很麻烦的问题，这也是Java引入IoC的主要原因。&rdquo;<br />
为什么不可以直接调用依赖类的静态方法，我可以不用IoC而用AspectJ。可以看看我之前写的blog<br />
<a href="http://www.blogjava.net/AndersLin/archive/2007/02/09/95666.html" target="_blank">http://www.blogjava.net/AndersLin/archive/2007/02/09/95666.html</a><br />
或者<br />
<a href="http://yimlin.javaeye.com/blog/53545" target="_blank">http://yimlin.javaeye.com/blog/53545</a><br />
看上去有点抬杠，其实不是，看官们请冷静思考一下这个问题（又或各位不以为然，没有关系）。</font></p>
<p><font face="Arial">因此robbin说：&ldquo;对于Java来说，更加适合采用贫血的模型，Java比较适合于把一个复杂的业务逻辑分离到n个小对象中去，每个小对象描述单一的职责，n个对象互相协作来表达一个复杂的业务逻辑，这n个对象之间的依赖和协作需要通过外部的容器例如IoC来显式的管理。但对于每个具体的对象来说，他们毫无疑问是贫血的。&rdquo;<br />
从robbin写的这样java来看，似乎确实如此。然而java的程序的设计是否就此一种。<br />
如果用AspectJ来重写代码，是否还需要那么多接口，那么类？同样加上annotation的应用，又可以省去xml文件<br />
有关于dao的设计可以这样处理：<br />
public class User{ public static UserFinder finder; ...... } <br />
public interface UserFinder{ public List doQuery(...); ...... } <br />
public class UserService{ public void FindByDept(...){ List list = User.finder.doQuery(....); .... } }<br />
这里我选择保留了接口，实际的情况则是未必，各自选择。</font></p>
<p><font face="Arial">3. Java在AspectJ的支持下，可以对domain model的各个方面的逻辑进行切割，就像C#的partial class那样，一个应用就是<br />
<a href="http://yimlin.javaeye.com/blog/54060" target="_blank">http://yimlin.javaeye.com/blog/54060</a><br />
我以为是可以解决robbin所认为的一个&rdquo;充血模型的坏处&ldquo;：对象高度自洽的结果是不利于大规模团队分工协作。一个编程个体至少要完成一个完整业务逻辑的功能。对于单个完整业务逻辑，无法再细分下去了。 </font></p>
<p><font face="Arial">4.如果robbin的观点是：ruby和java在实践domain model理念中，因为ruby的语言特性及其设计理念比java的有自己的优势，那我认为没有什么问题。而现有观点太过草率和武断了。<br />
</font><font face="Arial"><br />
<br />
BTW：感谢newman对小弟《小议领域模型》一文的认可。</font></p>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/57477#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 05 Mar 2007 18:53:53 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/57477</link>
        <guid>http://yimlin.javaeye.com/blog/57477</guid>
      </item>
      <item>
        <title>AspectJ应用--软件产品化的新方法</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/54060" style="color:red;">http://yimlin.javaeye.com/blog/54060</a>&nbsp;
          发表时间: 2007年02月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="postcontent"><p>Author: Anders小明<br /><br />产品化和客户化两个词语是一个对孪生兄弟,一个没有强大客户化能力的软件是不能称为产品化的软件.产品化也就意味着软件公司的开发至少分为两个不同性质的角色:一个负责产品化,一个负责客户化.除去传统的参数化, 继承以及plugin技术外,aspectJ为我们提供另一种能力.</p><p>如前所述, 公司有两个不同角色的团队在开发和维护&mdash;研发组和项目组，更进一步,本文所说适应如下的开发团队结构:<br />一个研发组---多个项目组, 每个项目组需要定制化的东西各自不尽相同. 为了保证几个团队的工作的相对独立性，公司希望项目组在做客户化过程中不改研发团队所开发的代码，应用了已知的方法:参数化, 继承以及plugin技术外.</p><p>然而这些技术并没有覆盖到所有的问题, 问题描述如下<br /><img src="http://www.blogjava.net/images/blogjava_net/anderslin/example.JPG" border="0" height="355" alt="example.JPG" width="530" /></p><p>经过分析发现需要客户化的地方,是需要在Root类里加一个propertyA, 这个时候基于已有的能力,我们可以做的选择是要么项目组A在修改Root类,要么修改每个叶子类(LeafA....), 显然修改root类是一个不错的选择. 然而槽糕的是,项目组B需要是添加另一个properyB,于是项目组B也这么做了. 对于项目组A和B来说这样做是没有太多问题的.<br />但对于研发组来说问题却是很严重的, 由于研发组需要从项目回收代码,以便吸取优点改进软件,研发组不得不小心的维护各个版本,并处理项目组所做的修改.而同时项目组获取了修改研发组代码的权力,处于时间的压力,会产生不符合系统结构约束的代码.<br />很明显,这样的工作方式不利我们的工作,我们需要一个新的思路.</p><p>在正式提出新方法前，我想先介绍一下微软的DSL Tools以及C# 2.0，各位客官，还请耐心的看一下。<br />DSL Tools是微软开发的Domain Specification Language，目的是用更接近于业务的语言模型来描述业务问题，并生成运行代码。工具允许其用户定义各自的Domain Language，目标是软件工厂。DSL Tools面临的一个问题（也是公司目前面临），定义好的DSL以及生成代码，不可避免的出现不同客户存在细微的差异性，DSL Tools必须很好的解决它，事实上DSL Tools的确做到了&mdash;&mdash;它基于是C#2.0 Partial Class技术。（有关于DSL Tools的更多信息可以参见MSDN）。<br />简单说的Partial Class的其实就是一个类的结构信息可以被存储在多个文件中，以下是个例子：<br />&nbsp;<br />file1.cs（微软的一个文件可以定义多个类，所有其文件名不需要同类名）：<br />public partial class CAgent {<br />&nbsp;&nbsp;&nbsp; private String name;<br />&nbsp;&nbsp;&nbsp; Property String Name{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name = name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; public void doSomethingA(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br />&nbsp;&nbsp;&nbsp; }<br />}<br />&nbsp;<br />file2.cs<br />public partial class CAgent {<br />&nbsp;&nbsp;&nbsp; private String code;<br />&nbsp;&nbsp;&nbsp; Property String Code{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return code;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.code= code;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; public void doSomethingB(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br />&nbsp;&nbsp;&nbsp; }<br />}<br />虽然CAgent类被分解为两个文件，不过对于调用者类说，CAgent具体name以及code两个property，以及doSomethingA和doSomethingB两个方法。<br />现在你可以想象DSL Tools是如何解决客户化这个问题的了！<br />DSL Tools生成的所有class都是partial class，当对于CAgent（或者其它类）有需要客户化时，需要做的只是添加一个新的文件，用同样的类名加上一个partial的关键字，<br />public partial class CAgent 就可以添加新的属性和方法（如同我展示的那样，想象一下file1.cs是研发维护，而file2.cs是项目维护的）。也就说，DSL的研发团队和客户化团队的工作时相对独立的。<br />&nbsp;<br />那么你一定会说，这个微软的C#，不幸的是公司用的是Java，java的语言规范不支持这样的能力。<br />是的，你说的很对，Java语言本身是不支持，不过我们有这样的手段&mdash;&mdash;那就是AspectJ。<br />实际上AspectJ面世很久了，我接触它至少有一年半了，不过支持一直没有想到这样的用法，直到我研究了DSL Tools（我相信很多道理是相通的，我们目前面临的问题硅谷的那帮人也一样面对过）<br />下面是一个简单的例子：<br />OnType.java：<br />public class OnType {<br />&nbsp;public void doAction(){<br />&nbsp; System.out.println(&quot;OnType.doAction&quot;);<br />&nbsp;}<br />}<br />&nbsp;<br />OnTypeAspect.aj<br />public aspect OnTypeAspect {<br />&nbsp;public String OnType.name = &quot;OnTypeAspect&quot;;<br />&nbsp;<br />&nbsp;public void OnType.doActionAspect(){<br />&nbsp; System.out.println(&quot;OnType.doActionAspect and name is &quot;+ name);<br />&nbsp;} <br />}<br />&nbsp;<br />Main2.aj<br />public class Main2 {<br />&nbsp;public static void main(String[] args){<br />&nbsp; OnType onType = new OnType();<br />&nbsp; onType.doActionAspect();<br />&nbsp;}<br />}<br />&nbsp;<br />如你所见，程序是可以完整的运行。你的控制台打出了：OnType.doActionAspect and name is OnTypeAspect<br />不仅如此，AspectJ可以为已有的类添加新的接口,就像C#的partial class做的那样。<br />&nbsp;<br />我以为aspectj是实现我们的目标的一个捷径，并且它是经过实践的，我不相信aspectj和微软的那帮人会闲着没事来支持这样的技术。BTW，我听说weblogic的jrockit准备在虚拟机一级上支持aspectj, 那么或许剩下的工作就是如何管理使用AspectJ了.</p></div>
          <br/>
          <span style="color:red;">
            <a href="http://yimlin.javaeye.com/blog/54060#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 12 Feb 2007 23:40:48 +0800</pubDate>
        <link>http://yimlin.javaeye.com/blog/54060</link>
        <guid>http://yimlin.javaeye.com/blog/54060</guid>
      </item>
      <item>
        <title>Interface plays as Placeholder, without any class implement</title>
        <author>yimlin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://yimlin.javaeye.com">yimlin</a>&nbsp;
          链接：<a href="http://yimlin.javaeye.com/blog/53545" style="color:red;">http://yimlin.javaeye.com/blog/53545</a>&nbsp;
          发表时间: 2007年02月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>Interface play as Placeholder, without any class implement it<br />
</strong>Author：Anders小明</p>
<p>有个这个题目是在ApectJ的应用，以及博客园子中一个blog讨论什么时候用接口（一个很古老的话题）。<br />
接口作为一种重要的抽象能力，重要意义在于：<br />
模型上看：接口代表做什么，而实现表示怎么做！<br />
在框架上：接口代表稳定，而实现代表多变！<br />
在语言上：接口代表0，而实现代表1，通过对象类型消除if&hellip;else&hellip;。<br />
（这段话我&lt;&lt;Domain Model：业务流程的进一步分析2&gt;&gt;提过）<br />
既然是应用开发，模型角度看根据现实意义，没有AspectJ前的开发，都是设计好一个接口，然后至少一个实现类，然后通过factory获取正确的实现类，后来spring出现了，实现了IoC。然而由于AspecJ横空出世了，我们又多了一种手段：接口作为占位符, 而没有实现类(当然有实现)。这是我在看到partech的sunflower的代码后才想到的!虽然我在&lt;&lt;AOSD：应用AOP实现业务逻辑&gt;&gt;一文中已经想到了一些,然而还是没有partech来的狠!<br />
下面是一个简单的例子：<br />
Order类</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" alt="" /><span style="COLOR: #0000ff">package</span><span style="COLOR: #000000">&nbsp;placeholder;<br />
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" alt="" /><br />
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" alt="" /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000">&nbsp;java.util.Date;<br />
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" alt="" /><br />
<img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ExpandedBlockStart.gif" id="_46_77_Open_Image" onclick="this.style.display='none'; document.getElementById('_46_77_Open_Text').style.display='none'; document.getElementById('_46_77_Closed_Image').style.display='inline'; document.getElementById('_46_77_Closed_Text').style.display='inline';" align="top" alt="" /><img src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/ContractedBlock.gif" id="_46_77_Closed_Image" onclick="this.style.display='none'; document.getElementById('_46_77_Closed_Text').style.display='none'; document.getElementB