<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>lane_cn</title>
    <description>--
--~--~---------~--~----~------------~-------~--~----~
My Blog: http://www.cnblogs.com/lane_cn
Msn ID: lane.cn#gmail.com
-~----------~----~----~----~------~----~------~--~---
</description>
    <link>http://lane-cn.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>软件的逻辑层次</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/39596" style="color:red;">http://lane-cn.javaeye.com/blog/39596</a>&nbsp;
          发表时间: 2006年12月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>基本层次<br />
</strong><br />
软件的逻辑结构可以划分为下面四个基本层次：</p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/lane_cn/layer-1.JPG" border="0" alt="" /></p>
<p>从下往上依次是：</p>
<p>1：基础设施层&mdash;&mdash;这个层次是纯技术层次，解决的是系统的物理问题，比如database gateway、网络通信、对象容器&hellip;&hellip;这个部分与业务需求关系不大，是系统的物理条件。</p>
<p>2：business对象&mdash;&mdash;在这个层次上，业务要素出现了，业务领域中的概念在这里实现。比如一个航运公司的系统，这里就应该有航线、航班、座位、乘客、登机牌&hellip;&hellip;这些对象应该拥有与实际业务领域相符的属性、方法。</p>
<p>3：business流程&mdash;&mdash;这个流程不是指程序解决问题的流程，而是用户的商业活动的流程。他体现的是端到端的业务流程。比如：检票员为旅客办理登机牌。business流程的输入参数是business对象，输出参数是business对象，产生的异常也是business对象。business对象在这里组合、串接，实现业务流程的自动化。这个层次是在直接实现用户的需求。</p>
<p>4：UI和接口&mdash;&mdash;这个层面调用business流程，将执行的结果交给软件的用户，或者别的系统。</p>
<p>这种逻辑层次划分是最基本的情况，各种复杂的层次都是这种方式的一种扩充。比如下面这样的形式：</p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/lane_cn/layer-2.JPG" border="0" alt="" /></p>
<p>在基础设施层和business对象之间，加入了一个DAO层。DAO层一方面负责数据的存储，体现了数据的存储方式，另一方面体现了业务对象的属性。这样就使business对象只需要负责纯粹的业务逻辑，不用关心物理问题。简单的说，业务对象里面不需要写SQL语句了。</p>
<p>business对象和business过程之间，加入了Service层。business对象也是具有行为的，但是这样的行为是比较细微的，需要调用者在多次调用之间保持必要的状态，需要用Service层来做一个封装，更明确的表达业务含义。</p>
<p><strong>单元测试<br />
</strong><br />
单元测试需要关心一个问题：层次之间的依赖关系。如果要测试某一个层次上的对象，必须同时建立他所依赖的每一个对象。层次之间的依赖越简单，测试越容易。</p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/lane_cn/layer-3.JPG" border="0" alt="" /></p>
<p>逻辑层次之间原则上是由上至下的依赖关系，同一层次内部的对象可以互相依赖。跨越层次的调用也是允许的，比如在UI Process中调用Business对象。UI层和UI Process层之间存在着互相的依赖。开发中我们最希望测试的是这三个层次：business过程、service、business对象。我们只要对下层对象建立stub对象，就可以对这三个层次上的对象进行测试。</p>
<p>对这三个层次的测试结果不仅保证了程序的运行时正确性，也是对程序的业务流程进行测试。在开发过程中和维护过程中，某个业务流程发生了变化，可以用单元测试保证其他流程不会受到危害。这样的构架可以保证迭代开发过程。</p>
<p><strong>和物理层次的结合<br />
</strong><br />
上面说的都是系统的逻辑层次。在系统中还存在着另一个层次&mdash;&mdash;物理层次。逻辑层次的目的是简化程序的逻辑复杂度，便于开发和维护；物理层次的实现需要考虑实际的物理分布情况，合理的安排每个物理节点的任务，最大限度提高系统的性能。逻辑层次和物理层次的划分依据和划分目的都是不一样的，他们之间存在着联系，但也不是绝对的。</p>
<p>逻辑层次和物理层次的结合有两种方式：</p>
<p>1、在基础设施层解决掉物理分布的问题，建立一个分布式的对象容器，把business对象和service放到容器中。这样，business对象和service就不必处理复杂的物理分布问题，business过程也不必关心他所调用的对象是在什么位置建立的。这样的方式最大限度的减少了物理结构对程序逻辑结构的影响，增加了物理分布的灵活性。但是在大部分情况下，对系统的效率都是有危害的。</p>
<p>2、在business对象内部处理物理分布的问题，或者制定一个技术无关的接口来体现business对象，在各物理节点编写各自的实现。这样物理层次和逻辑层次是搅在一起的，使系统的逻辑结构显得混乱，但是可以达到较高的运行效率。</p>
          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/39596#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, 19 Dec 2006 20:09:24 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/39596</link>
        <guid>http://lane-cn.javaeye.com/blog/39596</guid>
      </item>
      <item>
        <title>也讨论一把：不必非oo不可</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37797" style="color:red;">http://lane-cn.javaeye.com/blog/37797</a>&nbsp;
          发表时间: 2006年12月01日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>看了一篇讨论：<br /><a href="http://www.cnblogs.com/yimlin/archive/2006/11/30/578333.html">http://www.cnblogs.com/yimlin/archive/2006/11/30/578333.html</a><br />有些感想：并不是所有的行为都必须属于某个对象，有的行为似乎放在任何一个对象中都不合适，那就单独放在那里好了，没必要非要造出一个对象来，更不能把它硬安在某个对象上。</p>
<p>按照oop的方法，软件的逻辑架构可以分成下面几个层次：</p>
<p><img src="http://www.cnblogs.com/images/cnblogs_com/lane_cn/layer-hello.JPG" border="0" /></p>
<p>1：基础设施层——这个层次解决的是物理问题，比如database gateway、网络通信、对象容器&#8230;&#8230;这个部分与业务需求关系不大，是系统的物理条件。有很多技术框架帮助我们尽快的把这个层搭起来，比如web server、中间件、ACE。搞软件的公司会在这方面形成自己的技术积累。开始搞一个项目的时候，经常把以前的东西拿来复用，尽快进入后面的工作。</p>
<p>2：business对象——在这个层次上，业务要素出现了，业务领域中的概念在这里实现。比如一个航运公司的系统，这里就应该有航线、航班、座位、乘客、登机牌&#8230;&#8230;这些对象应该拥有与实际业务领域相符的属性、方法。长期在某个业务领域开发的公司，在这方面也应该形成自己的技术积累，形成可以直接复用的对象模型。这个层次上也是有一些现成的产品可以用的，比如SAP、工作流引擎，可以在这些东西的基础上做定制开发。</p>
<p>3：business流程——这个流程不是指程序解决问题的流程，而是用户的商业活动的流程。他体现的是端到端的业务流程。比如：检票员为旅客办理登机牌。business流程的输入参数是business对象，输出参数也是business对象。business对象在这里组合、串接，实现业务流程的自动化。这个层次是在直接实现用户的需求。</p>
<p>4：UI和接口——这个层面调用business流程，将执行的结果交给软件的用户，或者别的系统。</p>
<p>在&#8220;3：business流程&#8221;这个层次上，并不是每个行为都可以在系统中找到一个归属的对象，这是正常的。没有必要为了oo而oo，一定要给某个行为找到一个对象。尤其是不能把这些行为硬安在某个不相关的business对象上，那样反而会使系统变得混乱，难以理解。business流程的独立存在不会误导任何人。</p>
<p>从一个大的范围上说，这些business流程的所属对象，应该是系统的使用人。不应该花太大的精力，过多的考虑这些行为应该属于哪个实体。<br /><br /></p>

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37797#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>Fri, 01 Dec 2006 03:20:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37797</link>
        <guid>http://lane-cn.javaeye.com/blog/37797</guid>
      </item>
      <item>
        <title>无痛苦的软件维护——被遗忘的需求</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37798" style="color:red;">http://lane-cn.javaeye.com/blog/37798</a>&nbsp;
          发表时间: 2006年11月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>先说一个小笑话。有一个生产队队长，他对专家说：&#8220;现在我们生产队的地越来越多，牛越来越忙不过来了。我想要这么一种牛，他吃的草和普通牛一样多，但是干的活是普通牛的十倍。&#8221;专家说：&#8220;这种牛是可以造出来的，现在有基因工程。&#8221;队长说：&#8220;好吧，你给这造几头这样的牛。&#8221;于是专家找到了生物实验室，让生物实验室的人搞一个基因工程，把牛造出来。于是工程浩大，投资无法保证，合作多半是不愉快的收场。</p>
<p>现实世界里很多人分析需求的过程就类似于这位专家，他们把注意力放在用户提出的功能点上，而对用户的实际需求没有兴趣。有不少软件公司和程序员，其实都在做类似的基因工程。如果这个专家把注意力放在生产队长的业务需求上，而不是太在乎他提出的功能点，他会说：&#8220;我认识一个卖拖拉机的，可以带你去看看。&#8221;</p>
<p>软件的维护为什么这么痛苦，一个很重要的原因在于：需求已经被遗忘了。</p>
<p>需求是对用户具有直接商业价值的活动，而不应该牵涉到任何的功能实现方式。实现同一个需求可以使用多个方案，每个方案有自己的功能方式，在某个方案中至关重要的功能点，也许在另一个方案中根本无关紧要。</p>
<p>瀑布式的开发过程，首先是由一批懂得用户业务的专家去调查用户的需求，分析出这个系统应该具有哪些功能，形成一个非常重要的文件——《xxx系统需求规格说明书》。客户认可了这个《说明书》，在上面签字盖章，加入合同附件，到时候项目验收就以此为准。这时候，需求就已经分解成了一个个功能点，从这时候开始，需求本身就渐渐被人们遗忘。设计人员围绕着这些功能点进行工作，考虑用什么样的技术手段把功能点制造出来。功能点的制作细节形成了另外一份重要的文件——《xxx项目设计规格说明书》。这个《设计规格说明书》交给程序员去进行编码。</p>
<p>这样的做法在开发中已经形成了很大的问题。</p>
<p>首先，面对这样的《需求规格说明书》，设计人员已经无法知道最初的需求是什么。假如这个《需求规格说明书》中的功能点没有出现互相矛盾的情况，而他们串起来却和用户的需求不同，设计人员没办法发现这样的情况，编码人员也无法发现。假如测试也是根据这个《需求规格说明书》来做的，测试人员也发现不了。直到最后客户看见这个程序展现在他们面前。需求的分析需要在随后的过程中不断得到反馈，传统的过程不是没有反馈，而是反馈的时间太长了。</p>
<p>其次，由于设计人员已经无法知道基本的需求是什么，也就无法对业务进行建模。这样的需求分析是以开发人员的需要为核心的，可是结果恰恰妨碍了开发人员对需求的理解。如果开发人员对用户的业务过程不甚了解，他们只有一种选择：不要试图去了解需求了，直接按照这些功能点做吧。于是，他们建立的对象模型就不是以需求为核心的，而是以功能、界面为核心的。我见到过很多这样的系统，开发者确实有很高的抽象思维水平，程序中设计了非常巧妙的控制器和界面，可以很方便的进行开发和变更。唯独业务层的对象非常简陋，一旦发生实际业务的变更，仍然十分辛苦。</p>
<p>更大的困难发生在维护程序的时候。</p>
<p>假设有一个移动通信公司需要制造一个系统，用来解决手机用户入网的问题。这个需求有下面几个要素：</p>
<p>1：用户付钱，得到一个SIM卡和一个号码，把这个SIM卡装到手机里就可以通话。<br />2：营业员收的钱要记录下来，提供给稽核人员，现金和帐目必须是平的。<br />3：用户付的话费要划入他自己的帐户，可以打印票据。<br />4：用户要在入网合同上签字，然后营业员把合同归档。</p>
<p>这几个要素都是和通信公司的商业利益直接相关的，没有牵涉到任何系统实现方式。如果不考虑通信公司内部的业务规范，实现方案可以有几十种，下面列举两种：</p>
<p>1：SIM卡发给营业员，用户入网的时候，选择一个号码，然后付钱。营业员把SIM卡号码和电话号码输入系统，在交换网络上进行注册，这个SIM卡就可以通话了。然后各种费用记入各人帐户，合同归档。</p>
<p>2：SIM卡在下发给营业员之前，先在交换网络上和注册，并且已经预先设置了一定的话费。用户选择了这个号码，付钱之后直接SIM卡拿走就可以打电话了。营业员事后再输入用户的合同资料，费用计入各人帐户，合同归档。</p>
<p>这两种方案在实现过程上是不同的，因此具有不同的功能点。比如第二种方案中的SIM卡在出售之前是可以进行通话的，所以必须对这样的号码的通话费用进行监控，这个功能在第一种方案中是根本不需要的。并且两种方案在帐目的核对方式上区别也是比较大的。这两种方案是不同的功能点的集合，他们完成的是同一个业务需求。</p>
<p>系统在开发阶段如果没有保留用户的业务需求情况，而是只留下一个功能点的列表，会给维护人员带来成很大的困难。维护人员无法从这样一堆功能点中发现最初的需求是什么样子。各位可以试试，假设我们忘记上面的四个需求要素，只看下面的某个实现方案，从这个复杂的实现过程中，我们很难知道用户现在的需求到底是什么。一旦需求发生了变化，这些功能点就会出错，或者是功能点的时序发生意料不到的错误，也许帐目核对不上了，也许是用户拿走的SIM卡不能打电话了。</p>
<p>看不见需求在哪里，不知道手里这段代码会触动需求的哪根神经。维护人员的痛苦大部分来源于此。</p>
<p>&#8220;不要紧，客户记得自己的需求。&#8221;但是客户通常不懂技术，即使他们懂技术，他们也不知道系统是如何实现的。如果开发人员依靠客户提出新需求的解决方案，结果就是让软件工程变成&#8220;生物工程&#8221;。到最后是钱基本花光，人基本累死，甲乙双方感情基本破裂。</p>
<p>软件开发必须划分成几个过程，但是各个步骤应该有一个统一的核心——业务需求。</p>
<p>在需求调查阶段要搞清楚用户的业务需求，为了达到这个目的，可以提问回答，可以对用户进行跟踪采访，或者做一个demo给用户看看，最终的目的是为了搞清楚用户在做什么事，遇到了什么问题，程序应该去解决什么问题，这就是这一阶段的工作。</p>
<p>然后开始进行设计，设计系统的逻辑结构和物理结构，逻辑结构要符合需求的概念，各个对象互相调用要能够实现需求中的业务过程，同时物理结构划分合理，符合实际的分布状况，可以达到要求的的性能，业务过程的物理运行方式合理高效。这一阶段仍然是以业务需求为核心。</p>
<p>接下来是编码。首先是编写一些基础设施，比如网络通信、数据库、文件的读写、分布式计算，这些基础设施和业务需求没有什么关系，有很多现成的框架，借助这些框架我们可以尽快度过这个黑暗的阶段。然后编写业务对象，这时候业务需求中的一些概念逐步出现在代码中，比如上面说的那个例子，&#8220;用户&#8221;、&#8220;号码&#8221;、&#8220;合同&#8221;、&#8220;入网&#8221;、&#8220;SIM卡资源&#8221;这样的业务要素逐渐出现，这些对象所拥有的属性、可以运行的行为也和现实的需求一样。接着这些业务对象串接起来，实现业务过程，现在业务需求又回到了人们的视野当中。业务需求是什么，如何实现，在这里一目了然。最后将这些过程在UI或者接口中调用，将功能提供给用户或者别的系统。</p>
<p>测试更是要围绕着业务需求来进行，正常的业务流程应该产生正常的结果，如果缺少某个资源，或者输入了不合适的数据，应该出现业务含义明确的异常。并且系统的业务对象是处在一个独立的层次上，与UI和基础设施没有很大的关联，这样可以方便的采用自动化的测试方法。</p>
<p>这样的系统维护起来一定少很多痛苦。<br /></p>

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37798#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, 13 Nov 2006 12:08:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37798</link>
        <guid>http://lane-cn.javaeye.com/blog/37798</guid>
      </item>
      <item>
        <title>无痛苦的软件维护——文档和代码</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37799" style="color:red;">http://lane-cn.javaeye.com/blog/37799</a>&nbsp;
          发表时间: 2006年11月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>程序维护的时候经常遇到两个困难：<br /><br />1、不知道这段代码是实现什么功能的（code —— function）；<br />2、不知道这个功能是实现什么需求的（function —— business）。<br /><br />解决第一个问题是比较容易的，大家都是搞技术的，一头扎进代码里去，看上几十分钟，通常就能明白：原来这段代码是从数据库里面找到前三个月一直处于停机状态的号码，然后把这些号码放到一个叫做QUIT_USER的数据表里面去。<br /><br />第二个问题就难了，经常从代码中是看不出来的，于是项目开发的过程中就制造出来了大量的文档，来帮助开发者交流这个问题，也让将来维护这段代码的人知道这个知识。<br /><br />我们可以查找与这段代码相关的文档，文档上说：这段代码把停机三个月的号码放到一个叫做QUIT_USER的表里面，一个月以后，这些号码由另外一段代码拿去筛一下，把最近刚交了费用的号码删掉，剩下的用户做退网，号码回收，冻结半年以后可以重新使用。<br /><br />令人难过的是，维护程序的人很难有幸看到如此贴心的文档。他们查了半天经常看到的是：这段代码按照xxx的规则，从xxx表中查询数据，然后再把结果的数据经过xxx的处理，放到QUIT_USER表里面，over。你仍然不知道他到底是在做什么。<br /><br />并且，文档与代码的同步也是一个难题。不仅是文档，即使是代码中的注释，谁又能保证他真实的描述了系统的运行方式呢？时间紧张、错误的理解都可能造成文档与实际情况不同。我们假定写文档和写注释的人是认真的、不犯错误的，他们也必须忽略一些细节，他们不能什么都写上去。而他们忽略的细节很有可能为以后的维护带来麻烦。<br /><br />很多项目都有这个问题：business和function是脱节的。熟悉客户业务的人设计出一系列的功能点，这些功能点按照最初的设计是可以完成客户的业务的。然后这些功能点就拿到开发人员那里去造出来。而开发人员对用户的business其实是不了解的，他们的眼中只有function。到了维护的时候，business发生了变化，function重新设计。经常是经过一番修改，程序按照开发人员的思路运行良好，但是用户却一个劲的摇头：&#8220;不不，不是这样的，我们要的是这个&#8230;&#8230;&#8221;程序到底解决了哪些business，已经成了一个迷。<br /><br />什么东西可以最准确的描述程序的运行过程呢？只有代码本身。并且，经过精心设计的代码也能很好的对business进行描述。比如刚才说的那件事情，一段代码把号码放到QUIT_USER里面，另一段代码从这里面筛除一些号码做退网。这两个function其实在business方面都属于一个点，那么就应该让这两段代码写在一起，封装起来——这就是高内聚。并且这一段代码内部的操作应该与其他的功能没有任何关系，除非那个功能与这两个功能具有business上的共同点，别的代码应该不知道QUIT_USER是个什么东西——这就是低耦合。<br /><br />一个项目的代码，总是由大尺度的构思开始的，然后越来越贴近细节，牵涉越来越多的技术。但是写到最后，代码应该回到对business本身的描述。代码越贴近business，对维护的帮助就越大。<br /><br />我现在要维护一个公司的财务管理程序，我想知道职员的工资里面是不是已经计入了他们的个人所得税。我找到Account（会计），他应该有一个方法，每个月运行一次，把Salery发给Emplyee，我找到这个Salery，看到里面已经包含了Tax。我发现Account计算这个Tax的时候使用了一个Stratagy（策略）。他调用的是一个名叫Stratagy1998的策略，以800元作为基数计算个人所得税。现在这个基数已经发生了变化，于是我修改这个Stratagy1998、或者替换掉一个新的Stratagy2005。这样就完成了一次变更。并且我知道这样修改不可能影响到business不相关的东西。<br /><br />文档永远只能表示&#8220;某时某刻我们曾经这样想过&#8221;，让文档时刻保持与代码的同步是不实际的。要想知道&#8220;现在程序是怎样运行的&#8221;，只有代码能够告诉我们。文档应该配合代码，做代码不能做的事情，配合把business说清楚。而不应该与代码发生冲突。<br /><br />文档应该去描述代码无法说清楚的事情上，比如用户的工作场景、某个需求是由谁提出来的、大尺度的程序设计、重要对象的运行时序、系统安装手册。比如下面这个图，他清楚的说明了&#8220;销户&#8221;这个行为在整体的需求中处于什么地位。这样的东西用代码说清楚是比较费力的。当然用代码也能说清楚，比如可以使用State - Action模式，但是总归不如一张图表示的这么清楚。<br /><br /><img src="http://www.cnblogs.com/images/cnblogs_com/lane_cn/state_chart.JPG" border="0" /><br /><br />XP和Agile方法所提倡的&#8220;尽量少写文档&#8221;，就是基于这样一种设计理念：尽量的用代码和测试代码来描述business，以达到知识的交流和维护的便利。代码是最重要的沟通语言。在代码说不清楚的情况下，文档也是必须的。<br /><br /></p>

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37799#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>Fri, 03 Nov 2006 16:00:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37799</link>
        <guid>http://lane-cn.javaeye.com/blog/37799</guid>
      </item>
      <item>
        <title>NGOSS的一点简单概念</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37800" style="color:red;">http://lane-cn.javaeye.com/blog/37800</a>&nbsp;
          发表时间: 2006年09月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>NGOSS（Next Generation Operational Support Systems）是由TMF（Tele Management Forum）提出的，他用于电信领域，是构建下一代OSS/BSS系统的框架。<br /><br />TMF提供了技术中立构架（TNA）作为NGOSS解决方案的技术构架，这样就把NGOSS建立成了一种标准，这个标准与实现他的技术相互独立。TMF还提供了一组测试方法，用于验证解决方案是否符合NGOSS的标准。<br /><br />完整的NGOSS框架有多个组成部分，这些部分也可以独立的实施，用于解决某个领域的业务问题。下面对这几个部分做个基本的解释：<br /><br />eTOM（Enhanced Telecom Operations Map），他体现了电信领域的业务过程，这些过程是开发一个系统所必须的。eTOM描述了业务过程在电信运营领域中是如何运作、如何互相交互的，各个过程涉及到哪些参与者。eTOM的定义尽量符合一般情况，尽量减少对技术的依赖性。<br /><br />SID（Shared Information/Data Model），他提供了一个数据的标准，或者是实体的定义，这样就保证了在不同的数据提供者之间建立的系统可以平滑的集成起来。SID为系统之间的交流提供了一个&#8220;协议&#8221;，他定义了实体的接口，以及实体间的关系，这个接口是独立与技术实现的。SID降低了一个系统与外界系统互联互通的难度。<br /><br />TNA（Technology Neutral Architecture），这是NGOSS的一个重要组成部分，他定义了eTOM里面的过程和SID模型是如何结合起来，构建成一个完整的系统的。TNA不是定义一个实际的技术构架，而是定义了这个技术构架必须遵从的原则，从而保证OSS系统的所有部分的整体性，即使在分布式的环境下，也可以完整有序的结合在一起工作。<br /><br />除此以外，NGOSS还提供了一组测试方法，用来验证解决方案是否符合NGOSS的标准。<br /><br />NGOSS的目标就是：让符合其标准的解决方案能够更快的响应市场的变化，更加的易于维护。<br /></p>

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37800#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, 27 Sep 2006 15:45:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37800</link>
        <guid>http://lane-cn.javaeye.com/blog/37800</guid>
      </item>
      <item>
        <title>需求从哪里来</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37801" style="color:red;">http://lane-cn.javaeye.com/blog/37801</a>&nbsp;
          发表时间: 2006年07月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>IT系统是根据需求建设的，而需求是从哪里来的呢？<br /><br />为什么这个世界需要一个这样的系统，为什么系统需要做成这样，不多做一些事情，也不少做一些事情，恰好就要做这么多事情？<br /><br />这些问题难道不是问题吗，难道需求是理所应当的吗，需求是从哪里来的呢，用户为什么有需求，需求为什么是这样？<br /><br />下面我做了这么一件事，把一个家庭的活动整理了一下，有下面一些内容：<br /><br /><img src="/images/cnblogs_com/lane_cn/family1.GIF" border="0" /><br /><br />一个家庭的活动有这几个内容：工作、娱乐、购物、文化教育、医疗保健、人际交往。<br /><br />这个东西是不是可以看作中国城市家庭的一个生活模型。<br /><br />还需要加上一个活动：财务管理。这个活动不应该是与前面几个活动并列的，他应该是一种横向的交叉活动，就像下面这样：<br /><br /><img src="/images/cnblogs_com/lane_cn/family2.GIF" border="0" /><br /><br />假如我们现在要规划一个家庭的IT系统，就可以先根据这个模型，规划这个IT系统需要划分成哪几个子系统，各个子系统需要具有哪些功能。然后就是确定如何结合现有的条件分步骤的实现这个IT系统。<br /><br />如果从一开始就没有一个整体的规划，每个系统都是分别规划，根据自己的需要去独立建设。当最后这些系统结合在一起的时候，肯定会发生这样的现象：各个系统的边界有所重叠，也存在空隙，有的事情没有人做，有的人没有事做。当一个人需要了解这个家庭的整体情况的时候，他观察每个系统，会发现每个系统的标准都有差别，对同一个概念的理解不一样。要得到一个整体的情况，必须在各个系统之间搞很多人工的翻译工作。<br /><br />这个模型的存在不是为了开发某个系统，而是为了搞清楚人的活动。分析这个模型可以知道，在这样的活动中有什么样的需求，需要开发哪些系统来满足这些需求，这些系统需要有什么样的功能。<br /><br />一个家庭还要有这样一个活动：信息的管理。比如：家用电器的使用手册，上次给汽车加机油是什么时间，家人朋友的生日&#8230;&#8230;现在的家庭正在越来越多的做这样的管理活动。加上信息管理，形成了下面这样的家庭模型：<br /><br /><img src="/images/cnblogs_com/lane_cn/family3.GIF" border="0" /><br /><br />设计一个系统的时候，不能把眼睛放到一个个的功能点上，不去研究系统的业务需求。要避免被功能点洗脑，功能点是可能被代替掉的。同样，一个独立的系统，也只有放到人的活动中才有价值。在这个范围上，任何一个独立的系统也是可能被代替掉的。<br /><br />这个家庭模型可能有问题，比如：人际交往这个活动可能不应该是和其他活动并列的，他应该也是一种横向的交叉活动，人际关系在其他活动中是可以发挥作用的。应该是这样：<br /><br /><img src="/images/cnblogs_com/lane_cn/family4.GIF" border="0" /><br /><br />从人的活动，到一个系统的开发部署，假设这其间的路程是100公里。很多情况下我们关注的是最后的10公里，前面的90公里，因为已经有人把结论告诉我们，所以经常被我们忽视，我们都认为那是理所应当的。<br /><br />如果开发者和客户之间签开发合同，客户提出需求，开发者只管照做，然后客户就要买单。需求是什么——这个问题是客户的事情。但是如果是开发一个产品呢，把一个造好的东西拿给客户，希望客户买单，开发者就必须自己研究这个问题。<br /><br />或者，读这篇文章的人自己就是那个客户，他怎样知道自己需要什么呢？<br /><br /></p>

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37801#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>Thu, 20 Jul 2006 13:36:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37801</link>
        <guid>http://lane-cn.javaeye.com/blog/37801</guid>
      </item>
      <item>
        <title>软件的灵活性来自哪里？</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37802" style="color:red;">http://lane-cn.javaeye.com/blog/37802</a>&nbsp;
          发表时间: 2006年06月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>灵活的软件，可以更好的适应用户的需求。什么样的软件才是灵活的？<br /><br />一旦用户提出灵活性方面的需求，设计者经常想到的一个对策是：增加配置。在不同的业务环节上增加功能配置，哪里需要灵活性，就把配置写到哪里。配置为软件系统提供了无数个可能——这就是灵活性。但是配置经常复杂无比，失去控制。很多配置项目已经失去了业务意义，完全成为一种数学意义上的排列组合。按照一些配置的路线，业务无法形成闭环流程，走进死胡同。<br /><br />这样的系统，维护配置和维护程序本身一样复杂。配置，就是不需要编译的代码。<br /><br />实际上，软件最大的灵活性，来自于完整的业务模型。一个完整的业务模型，不需要华丽的技术——设计模式、先进的平台、巧妙的构思——这些都不重要。重要的是，业务模型需要真实的反映真正的世界。<br /><br />真实的世界上有&#8220;顾客&#8221;，那么软件世界里就要有&#8220;Customer&#8221;，真实的世界上有&#8220;帐单&#8221;，那么软件世界里就要有&#8220;Bill&#8221;，真实的世界里顾客可以买东西，那么软件世界里的Customer就有&#8220;Buy&#8221;方法，参数是Order&#8230;&#8230;<br /><br />把这些基础的业务模型构思出来，就能够发现，具体的业务需求实现起来会十分的方便。就像用积木搭起一栋房子一样，只需要写一些胶水代码，把业务对象拿来组装一下。<br /><br />业务模型会有变化吗？当然有。但是业务决不会无中生有的变，也不会没有道理的变。业务模型的变化一定是在原有的基础上添一点东西、改一点东西。一个业务模型能很好的控制变化影响的范围。为了减少这样的变化对模型的影响，各种技术就可以登台了——设计模式、重构&#8230;&#8230;<br /><br />有一个业务模型，即使是很朴实无华的业务模型，也可以为程序带来极大的灵活性。可以在胶水代码里面增加配置，这样的配置比较容易与用户的业务需要紧密结合，真正的起到作用。也可以把业务对象暴露给脚本引擎，用脚本程序实现一些功能需求。<br /></p>

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37802#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, 21 Jun 2006 13:57:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37802</link>
        <guid>http://lane-cn.javaeye.com/blog/37802</guid>
      </item>
      <item>
        <title>做项目的公司如何做技术积累——对以前工作的一些回想</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37803" style="color:red;">http://lane-cn.javaeye.com/blog/37803</a>&nbsp;
          发表时间: 2006年06月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>我的第一个工作是在一家软件公司写程序，主要的客户是一家省级电力公司。工作主要是以项目的形式，项目签下来了，忙几个月，从需求调研到设计，编码，测试，然后现场调试，现场维护。做完了以后通常有一个空闲时间，然后进入下一个项目。每个项目的需求都有一定的差异，但是都是在同一家电力公司里，尽管具体的客户不同，解决的问题也不同，但是都属于同一个大的商业范围。<br /><br />现在已经离开这家公司近一年了。有时候思考以前的经历，更能摆脱那种身在此山中的迷惑，也能够在技术方面有一些回忆和反思。虽然已经是马后炮，毕竟工作还在继续，总结以前的经验对以后也是一种财富。对于其他人来说，也许能够有一些借鉴作用。也希望一代一代的程序员能够积累自己的经验，减少新人在黑夜中摸索的时间。<br /><br />以前的工作经常是项目性质的，一个项目做完，再做另一个。每个项目都有不同之处，解决的是特定的需求。每个项目都是新的，很难形成技术上的积累。因此，造成开发人员也是比较痛苦的，在dead line之前苦苦挣扎，加班熬夜是普遍现象。<br /><br />现在想起来，虽然以前做的每一个东西的具体情况是有差异的，但是总的来说，客户范围都是在电力公司里面，具有很大的共性。如果细细规划，还是能够形成一些有延续性的构架的。<br /><br />首先我们抛开具体的项目，更要抛开具体的技术，来考虑电力公司的基本业务需求：<br /><br />电力公司的业务需求最主要是围绕着电网展开的，每一个系统都是围绕着电网开发的，只是关注的侧面有所不同。因此要做一个有延续性的框架，电网的模型是必不可少的，可以说是处于核心的地位。这个模型应该包括：电网的拓扑结构、各种网元设备（比如：输电线路、变压器、开关、发电机、用电负荷、等等）。<br /><br />这个模型中还应该包括：电力公司的内部组织关系。比如：电力公司内的各部门、组之间的关系，以及他们与电网相关的一些重要的职能。<br /><br />还应该包括：省级电力公司和合作组织之间的关系模型，比如：和各地市电力公司之间的关系模型、和发电站之间的关系模型、和变电站之间的关系模型、和用电单位之间的关系模型。<br /><br />就像下面这样：<br /><br /><img src="/images/cnblogs_com/lane_cn/recall1.GIF" border="0" /><br /><br />构建这些模型，完全是和具体的技术无关的，可以采用合适的任何技术。可以选用java，也可以用c++，也可以用.net，或者其他合适的东西，要考虑技术的成熟性和未来的发展趋势。<br /><br />这些模型在电力公司的业务中是相对比较稳定的。有了这个东西，开发工作就不会每次都是从零开始，可以建立在一个稳固的基础上面。<br /><br />比如，我们现在需要建立一个系统，对电网设备的实时运行数据进行监控。那么我们可以把系统建立在电网模型上面，主要关注设备的实时数据、控制命令。对于监控的工作还有一些职权流程方面的控制，那么内部的组织模型也能发挥作用。对于一些监控工作，需要各地的电力公司、电厂、变电站、用电单位采取配合措施，那么可以使用合作组织模型。<br /><br />于是，这个系统的开发可以是下面这样的情况：<br /><br /><img src="/images/cnblogs_com/lane_cn/recall2.GIF" border="0" /><br /><br />再比如，我们现在需要开发一个设备检修工作的控制系统。这个系统需要了解网元设备的拓扑结构，以确定某些设备可以一起检修，某些设备不能同时断电，安排检修工作，对检修期间的电网稳定性进行评估。这个系统可以建立在电网模型的基础上，主要关注电网的拓扑结构。客户需要对检修任务做具体安排，那么可以使用合作关系模型。于是系统是下面这样：<br /><br /><img src="/images/cnblogs_com/lane_cn/recall3.GIF" border="0" /><br /><br />可以看出，各个系统都是把底层的几个模型拿来，关注其中的一些侧面，然后再加上个性化的需求。这样就能够加快开发的速度，减少公司开发新系统的成本和时间。<br /><br />这个模型该如何得到呢？<br /><br />首先应该借鉴一切可以得到的知识。比如：行业内的标准，国家颁布的规范。了解这些东西可以简化我们的分析过程，也能避免走弯路，犯错误。或者借鉴其他公司的相似产品，博采众长。<br /><br />更加重要的是，要在开发项目的过程中要对这个模型不断的建立、完善，要把图纸上的构思切切实实的落实到代码里。<br /><br />比如，我们在最初开发上面所说的那个设备监控系统的时候，首先应该考虑设计一个电网模型，建立网元设备的对象关系，实现电网的拓扑结构。而不应该急于进行数据库设计这样的细节工作。在建立电网模型之后，结合具体的项目需求进行分析，增强基础模型的健壮性。然后在这个基础上做具体的项目开发工作。<br /><br />通过这样一个过程，电网模型就能够很好的建立起来，为以后的开发工作所用。<br /><br />再比如，我们现在需要为电力公司做一个系统，对电网设备的资产情况进行管理。这时候我们需要充实电网模型，加入设备的资产属性的部分：<br /><br /><img src="/images/cnblogs_com/lane_cn/recall4.GIF" border="0" /><br /><br />加入这个属性的时候需要注意，这个属性要和其他的一些属性相分离（比如实时运行数据），以免对互相的发展和应用造成以不良影响。在此基础上，开发项目的实际功能就比较容易了。<br /><br />在这个过程中，需要处理一个难题，就是&#8220;急用为先&#8221;和&#8220;构架为先&#8221;之间的矛盾。关于这个话题，各个公司的情况千差万别，就不好说了。我只能说：即使在急用为先的压力下，也应该适当的考虑遵守底层模型的规定，并且一旦有条件，就应该把以前不符合构架的部分慢慢的并入一个正常的轨道。<br /><br />下面谈一些自己对于技术学习的看法。对于一个程序员来说，掌握先进的技术是必须的。当技术达到一定程度之后，对于各种设计方式都有了一定的认识，然后会产生一种困惑：这些先进的技术，他们到底应该如何应用？.net和java哪个更好、linux和windows哪个更强、struts应该怎么用、spring是什么东西、orm有啥好处、设计模式该怎么用&#8230;&#8230;<br /><br />其实，要回答这些问题，不能只在技术上考虑，要跳出技术的圈子。站在用户的角度、业务的角度，自上而下的进行分析，就能够了解哪些东西才是重要的环节，那些问题其实是可以忽略不计的。技术本身没有优劣之分，要结合具体的需要，才好做出决定。程序员应该掌握技术，然后要跳出技术，专注于特定领域的业务（也许这个业务就是开发本身，例如你需要开发一个IDE，或者正在开发操作系统），真正为客户、也为公司做一些有价值的事情。<br /></p>

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37803#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>Sun, 11 Jun 2006 16:22:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37803</link>
        <guid>http://lane-cn.javaeye.com/blog/37803</guid>
      </item>
      <item>
        <title>痛苦的系统，艰难的维护</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37804" style="color:red;">http://lane-cn.javaeye.com/blog/37804</a>&nbsp;
          发表时间: 2006年06月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          一个7&#215;24的帐务系统，一个每天都要开门营业的营业厅，运行了好几年了，小改小闹几乎天天不断，大的升级隔半年到一年就要有一次。外界还有新的系统要接在上面，不断的开拓新的接口，功能不断扩充&#8230;&#8230;<br /><br />系统最近一次大的升级，就在今年的3月。当时开发公司来了几十个人，平时人烟稀少的机房里面挤满了人。升级前一天的晚上，项目经理召集广大人民群众，发表升级前的最后一次讲话：&#8220;大家忙了n个月，就是为了这一天。再给我顶住，到明天这一切结束的时候，胜利将属于我们！&#8221;我站在边上，对我的同事说：&#8220;到明天，当新的系统开始运行的时候，一切只能说刚刚开始。&#8221;<br /><br />就像我以前见过的很多系统一样，这个系统开发的最初阶段先搞数据库的设计，数据库的设计就像下面这个图，一个一个的数据表，一对一的，一对多的，多对多的关系：<br /><br /><img src="/images/cnblogs_com/lane_cn/maintain1.GIF" border="0" /><br /><br />数据库设计的时候，需要用各种业务流程对数据库的设计进行验证，这两个设计是同时进行的。当数据库差不多设计完的时候，业务流程也应该明白的差不多了。业务A执行的时候增删查改这些数据表，业务B执行的时候增删查改那些数据表，系统的设计就这样渐渐形成了。<br /><br /><img src="/images/cnblogs_com/lane_cn/maintain2.GIF" border="0" /><br /><br />就这样，n个业务流程在数据库表的丛林中穿行，像迷宫一样，所过之处数据发生变化。一番抽丝剥茧，详细设计完成。然后大家分配工作，照着设计书写代码。忽然有人发现设计的问题，重要人物召开会议了解情况，商量对策。对策通常是数据库要加上几个表，几个字段，某几个流程再拐几个弯。大家改改代码，继续埋头工作。<br /><br />然后测试，然后补充各种文档，这时候时间越来越紧张，也许已经延期了好几次。终于，可以部署到现场运行调试了。按照开发商的说法，一切都结束了。<br /><br />对于开发商来说，一切都结束了，但是对于用户来说，一切才刚刚开始。维护一个这样的系统，是一件痛苦的事情。<br /><br />系统有Bug，总是改不完；<br />设计有缺陷，业务不完整，大家都很困惑，难道什么东西都要提出书面需求才能去做吗；<br />新需求不好做，业务流程的迷宫太复杂，无法避免对现有需求造成影响；<br />功能扩充了，数据增加了，业务扩展了，效率也下降了，买CPU吧，加上去试试。<br /><br />系统就这样维护，没办法，这就是工作。<br /><br />和一个开发人员谈过这个系统为什么要做成现在这个样子，他的说法是：这样做的理由有两个：<br /><br />1、这样的设计层次少，业务实现很直接，效率高。（这个原因没人相信，包括他自己）<br />2、一开始就是做成了这样，后来的项目时间紧，任务重，直接拿着前人创造的成果改一改，就可以交差了。（这是个很合理的理由）<br /><br />也许大家都在期待一个改变的机会。<br /><br />

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37804#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>Fri, 09 Jun 2006 00:36:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37804</link>
        <guid>http://lane-cn.javaeye.com/blog/37804</guid>
      </item>
      <item>
        <title>想对即将毕业的同学们说一些工作几年的感想</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37805" style="color:red;">http://lane-cn.javaeye.com/blog/37805</a>&nbsp;
          发表时间: 2006年05月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          很快又到了大学生毕业的季节了,很多同学将要走上工作岗位,回想一下自己工作这几年的经历,很想对他们说几句话,尤其是各位搞IT行业的.<br /><br />工作是一种责任,一个岗位必然意味着一种责任.一个人能得到什么收入,不是取决于他做的事情有多少技术含量,只取决于他做的事情需要承担多大的责任.无论是给老板打工,还是自己创业都是这样.要工作就要承担责任,需要在职责范围内去作出决定.<br /><br />工作需要协作.知道别人在做什么事情,让别人知道自己做什么,为别人做事情,找人为自己做事情,推掉自己无法完成的事情--这就叫协作.<br /><br />不明白的事情要及时去寻找帮助,求助你的上级,同事,拉人加班,请人吃饭...总之不择手段.任务可以靠IQ完成,也能靠EQ完成.但是要明白,无论找谁做,承担责任的永远是自己.<br /><br />工作中要注意学习,但是学习的方法和在学校不同.不是等着别人告诉自己正确答案ABCD,而是一种双向的行为.不要不好意思去"麻烦"别人,要主动去问别人,一个人没时间,就去再找一个人,要把别人拉到你的办公桌前听你说,说出自己的问题和思考,向别人一行一行的展示自己写出的程序,而不是简单的问出一句傻话,等着别人回答.<br /><br />对待事业的态度积极一点和消极一点,带来的差异是巨大的.大大超过学历的差别,智商的差别.<br />

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37805#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, 02 May 2006 19:47:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37805</link>
        <guid>http://lane-cn.javaeye.com/blog/37805</guid>
      </item>
      <item>
        <title>剔除设计中多余的概念</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37806" style="color:red;">http://lane-cn.javaeye.com/blog/37806</a>&nbsp;
          发表时间: 2006年04月24日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          软件设计首先要整理用户的业务模型，然后以此为参照，结合环境条件，建立软件系统模型。在这个过程中，很重要的一点是：要剔除软件模型中多余的概念。<br /><br />哪些是&#8220;多余的概念&#8221;呢？如果一个概念是从用户的业务模型中无法直接观察到的，而是设计者推想出来的，那么这个概念就是多余的概念。<br /><br />我们想象一个铁路公司，经营着ABCD四个城市之间的路线。这几个城市的位置如下：<br /><br /><img src="/images/cnblogs_com/lane_cn/ticket_price1.GIF" border="0" /><br /><br />铁路公司的老板想做一个售票系统，他找到一家软件公司。假设我就在这个软件公司工作，担任这个项目的负责人。铁路公司的老板这么对我说：从A到B的票价是50元，从B到C的票价是60元，从C到D的票价是30元，如下：<br /><br /><img src="/images/cnblogs_com/lane_cn/ticket_price2.GIF" border="0" /><br /><br />在这个基础上，两个城市之间的票价是各段之和。比如：从A到C的票价是AB+BC，就是110元。<br /><br />于是，回到办公室里，我开始设计这个系统。我想：这个系统很简单，以A城市作为起点，把从A城市到各个城市之间的票价记录下来。就像这样，我设计了一个数据表，保存票价，他是这样的：<br /><br />SQL&gt;SELECT * FROM TICKET_PRICE;<br /><br />CITY&nbsp;&nbsp;&nbsp; PRICE<br />---------------<br />A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br />B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50<br />C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 110<br />D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 140<br /><br />当旅客来买票，要从B到C去的时候，我们就用C的票价减去B的票价，得到从B到C的票价，得到60元。想从D到B，就用D的票价减去B的票价，得到90元。<br /><br />这个设计非常简洁，看上去没什么问题。按照这样的想法，我们开始写代码了。但是，很快我就遇到了麻烦。<br /><br />铁路公司的老板打来一个电话，他说：我们正在对票价做一些调整，从A到B的票价保持50元，B到C保持60元，但是从A直接到C的票价现在调整到100元。并且从C到A的票价要打一个8折，就是80元。<br /><br />现在麻烦了，TICKET_PRICE这个表面临着重大的变动，所有基于这个表的业务逻辑都要改了。<br /><br />烦恼的由来是什么呢？原因在于，这个设计是基于我的一个推论，而不是可以直接观察到的业务事实。直接观察到的业务事实是：A到B的票价是50元，B到C的票价是60元，A到C的票价是110元&#8230;&#8230;而我在这个基础上做了一个推断：A到C的票价是AB+BC。当然，我做出这个错误的推断，有铁路公司老板的一部分&#8220;功劳&#8221;。无论怎样，变更不可避免，铁路公司和我，都必须承担这个错误的后果。铁路公司要追加项目投资，我们的开发成本要上升，而我自己，肯定要加班了。<br /><br />这个设计中有一个多余的概念：A城市是一个基准点。这是一个在业务过程中无法直接观察到的概念，而我错误的将他作为整个系统的基础，让所有城市之间的票价以他们到A城市的票价为基准。<br /><br />从业务中能够直接观察到的是什么呢？只有两个城市之间的票价。一个乘客来到铁路公司买票，从A到B是50元，从B到D是90元&#8230;&#8230;他能观察到的只有这些，而没有作为某个基准点的A城市。系统的设计应该基于坚实的业务基础，而不是任何推断。&#8220;基准&#8221;这个概念需要从设计中剔除掉。<br /><br />重新设计这个系统，应该用下面这样的形式描述城市之间的票价：<br /><br />
<table cellspacing="0" border="1" cellpadding="3" style="WIDTH: 320px; BORDER-COLLAPSE: collapse">
    <tbody>
        <tr>
            <td></td>
            <td>A</td>
            <td>B</td>
            <td>C</td>
            <td>D</td>
        </tr>
        <tr>
            <td>A</td>
            <td>0</td>
            <td>50</td>
            <td>110</td>
            <td>140</td>
        </tr>
        <tr>
            <td>B</td>
            <td>50</td>
            <td>0</td>
            <td>60</td>
            <td>90</td>
        </tr>
        <tr>
            <td>C</td>
            <td>110</td>
            <td>60</td>
            <td>0</td>
            <td>30</td>
        </tr>
        <tr>
            <td>D</td>
            <td>140</td>
            <td>90</td>
            <td>30</td>
            <td>0</td>
        </tr>
    </tbody>
</table>
<br />当然，我可以用这样的数据表来保存这样的票价：<br /><br />SQL&gt;SELECT * FROM TICKET_PRICE;<br /><br />START&nbsp;&nbsp;&nbsp;&nbsp; DESTINATION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRICE<br />---------------------------------<br />A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50<br />B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50<br />A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 110<br />C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 110 <br />B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 60<br />......<br /><br />如果我一开始就这样设计的话，接到铁路公司的电话后，心里就会舒服多了。<br />

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37806#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, 24 Apr 2006 01:58:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37806</link>
        <guid>http://lane-cn.javaeye.com/blog/37806</guid>
      </item>
      <item>
        <title>一个Outlook宏写的小程序，献给象我一样粗心大意的人</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37807" style="color:red;">http://lane-cn.javaeye.com/blog/37807</a>&nbsp;
          发表时间: 2006年03月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近经常有这样的事情出现：Email已经发出去了，却发现附件没有加上，于是再补上一个Email。<br /><br />不得已写了下面这个小程序，可以用在Outlook上面。在Email发送的时候，他会检查标题和正文里面有没有&#8220;附件&#8221;两个字，如果有这两个字，却又没有附件，就会出现提示。<br /><br />最近用了这个东西，还是不错的，预防了不少事故的发生。谨在此献给象我一样粗心的人，希望大家喜欢。<br /><br />代码如下：<br /><br />
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="/Images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #008000">'</span><span style="COLOR: #008000">'''''''''''''''''''''''''''''''''''''''''''</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />'<span style="COLOR: #008000">发送Email的时候，会触发这个过程</span><span style="COLOR: #008000"><br /></span></span><span style="COLOR: #008000"><img src="/Images/OutliningIndicators/None.gif" align="top" />'这段代码一定要写在发送Email的响应过程中<span style="COLOR: #008000"><br /></span><img src="/Images/OutliningIndicators/None.gif" align="top" />'</span><span style="COLOR: #008000">'''''''''''''''''''''''''''''''''''''''''''</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">Private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Sub</span><span style="COLOR: #000000">&nbsp;Application_ItemSend(ByVal&nbsp;Item&nbsp;</span><span style="COLOR: #0000ff">As</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Object</span><span style="COLOR: #000000">,&nbsp;Cancel&nbsp;</span><span style="COLOR: #0000ff">As</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Boolean</span><span style="COLOR: #000000">)<br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">On</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Error</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Resume</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Next</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Dim</span><span style="COLOR: #000000">&nbsp;message&nbsp;</span><span style="COLOR: #0000ff">As</span><span style="COLOR: #000000">&nbsp;Outlook.MailItem<br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Set</span><span style="COLOR: #000000">&nbsp;message&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Item<br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;<br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">If</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Not</span><span style="COLOR: #000000">&nbsp;CheckAttachment(message)&nbsp;</span><span style="COLOR: #0000ff">Then</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cancel&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">True</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Exit</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Sub</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">End</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">If</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">End&nbsp;Sub</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span></div>
<br />调用下面这个函数，检查是否应该有附件：<br /><br />
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="/Images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #008000">'</span><span style="COLOR: #008000">'''''''''''''''''''''''''''''''''''''''''''''</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />'</span><span style="COLOR: #008000">检查标题或者正文里&#8220;附件&#8221;字样，是否可以发送附件？</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />'</span><span style="COLOR: #008000">'''''''''''''''''''''''''''''''''''''''''''''</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">Private</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Function</span><span style="COLOR: #000000">&nbsp;CheckAttachment(message&nbsp;</span><span style="COLOR: #0000ff">As</span><span style="COLOR: #000000">&nbsp;Outlook.MailItem)&nbsp;</span><span style="COLOR: #0000ff">As</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Boolean</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;CheckAttachment&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">True</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">If</span><span style="COLOR: #000000">&nbsp;(message.Attachments.Count&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">And</span><span style="COLOR: #000000">&nbsp;_<br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(</span><span style="COLOR: #0000ff">InStr</span><span style="COLOR: #000000">(message,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">附件</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">Or</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">InStr</span><span style="COLOR: #000000">(message.Body,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">附件</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">))&nbsp;</span><span style="COLOR: #0000ff">Then</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Dim</span><span style="COLOR: #000000">&nbsp;answer&nbsp;</span><span style="COLOR: #0000ff">As</span><span style="COLOR: #000000">&nbsp;VbMsgBoxResult<br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;answer&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">MsgBox</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">没有附件,&nbsp;是否继续发送?</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;vbYesNo&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;vbQuestion,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Microsoft&nbsp;Office&nbsp;Outlook</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">If</span><span style="COLOR: #000000">&nbsp;answer&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;vbNo&nbsp;</span><span style="COLOR: #0000ff">Then</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CheckAttachment&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">False</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">Else</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CheckAttachment&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">True</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">End</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">If</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">End</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">If</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">End&nbsp;Function</span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span></div>
<br />要运行这个宏，可能要改变Outlook的宏安全设置。 

          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37807#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>Thu, 16 Mar 2006 17:12:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37807</link>
        <guid>http://lane-cn.javaeye.com/blog/37807</guid>
      </item>
      <item>
        <title>单元测试应该测什么，不应该测什么？</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37808" style="color:red;">http://lane-cn.javaeye.com/blog/37808</a>&nbsp;
          发表时间: 2006年02月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>刚才看了<a href="/idior" target="_blank">idior</a>的一篇文章：<a href="/idior/archive/2005/08/09/210320.html" target="_blank">Enterprise Test Driven Develop</a>。看后有一些感想，在这里写下这篇文章，讲讲我对这个问题的看法：自动化的单元测试应该测什么。<br /><br />最近有朋友提出意见，觉得我写的文章比较空洞，写的很长，但是很不实在。可能原因是这样的：代码太少了。今天就从一段代码开始吧，这段代码描述电信营业系统中的缴费开机的过程：</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="/Images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">User&nbsp;user&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;User.getUserByServiceId(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">13309790280</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">通过电话号码找到用户</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">Account&nbsp;account&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;user.getAccount();</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">与用户关联的帐户</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">user.pay(</span><span style="COLOR: #000000">100</span><span style="COLOR: #000000">);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">用户缴费100元<br /><img src="/Images/OutliningIndicators/None.gif" align="top" /><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">判断用户余额＋帐户的信用度-用户欠费是否大于0</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(user.getBalance()&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;account.getCredit()&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;user.getDebt()&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)<br /><img src="/Images/OutliningIndicators/ExpandedBlockStart.gif" id="Codehighlighter1_229_390_Open_Image" onclick="this.style.display='none'; Codehighlighter1_229_390_Open_Text.style.display='none'; Codehighlighter1_229_390_Closed_Image.style.display='inline'; Codehighlighter1_229_390_Closed_Text.style.display='inline';" align="top" /><img src="/Images/OutliningIndicators/ContractedBlock.gif" id="Codehighlighter1_229_390_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_229_390_Closed_Text.style.display='none'; Codehighlighter1_229_390_Open_Image.style.display='inline'; Codehighlighter1_229_390_Open_Text.style.display='inline';" align="top" style="DISPLAY: none" /></span><span id="Codehighlighter1_229_390_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="/Images/dot.gif" /></span><span id="Codehighlighter1_229_390_Open_Text"><span style="COLOR: #000000">{<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;Service&nbsp;service&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;user.getService();</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">与用户相关的服务<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">判断这个服务是否处于欠费停机状态</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" /></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(service.getState()&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">欠费停机</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">||</span><span style="COLOR: #000000">&nbsp;service.getState()&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">限制呼出</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)&nbsp;<br /><img src="/Images/OutliningIndicators/ExpandedSubBlockStart.gif" id="Codehighlighter1_368_388_Open_Image" onclick="this.style.display='none'; Codehighlighter1_368_388_Open_Text.style.display='none'; Codehighlighter1_368_388_Closed_Image.style.display='inline'; Codehighlighter1_368_388_Closed_Text.style.display='inline';" align="top" /><img src="/Images/OutliningIndicators/ContractedSubBlock.gif" id="Codehighlighter1_368_388_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_368_388_Closed_Text.style.display='none'; Codehighlighter1_368_388_Open_Image.style.display='inline'; Codehighlighter1_368_388_Open_Text.style.display='inline';" align="top" style="DISPLAY: none" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span id="Codehighlighter1_368_388_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="/Images/dot.gif" /></span><span id="Codehighlighter1_368_388_Open_Text"><span style="COLOR: #000000">{<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">向交换系统发出开机指令</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" /></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span></div>
<p>这是电信营业系统中最常见的的一个业务。电信系统最基础的模型应该说是"三户模型"，三户模型描述的是客户（Customer）、帐户（Account）、用户（User）以及服务（Service）等等概念的一个关系模型。实际的模型比代码里的复杂，这里简化了很多，主要用来举个例子。<br /><br />要开发一个电信系统，首先要做的就是在系统中实现最基础的几个模型，比如三户模型、联机指令模型、业务办理模型、合作伙伴模型&#8230;&#8230;，其中三户模型处于非常重要的地位。首先要做的是在软件系统中真实的反映这个模型，绝不可以将其隐含扩散在各个业务过程中。然后就可以在这个基础上，从一到二、从二到三、从三到万，实现各个子系统和复杂多变的业务需求。<br /><br />按照TDD的开发思路，我们应该先写测试代码，再来写实际程序，用测试来推动开发的前进。这里先把代码写出来，表达一下业务。下面我就说一下，单元测试代码应该测什么。<br /><br />我们拿User举个例子，现在我们要写User类的测试代码。<br /><br /><strong>需要测什么？</strong><br /><br />我们需要测试User类的外在表现。比如我们要测试pay方法，应该这样：<br /><br />我们建立一个User对象，这个User余额是0，欠费38元。现在缴费100元，缴费完成后，余额应该是62元，欠费应该为0，这是一个Case。<br /><br />我们建立一个User对象，这个User余额是30，欠费0。现在缴费50元，缴费完成后，欠费应该仍然是0，余额应该是80元。这是第二个Case。<br /><br />我们建立一个User对象，这个User的余额是0，欠费150元。现在缴费70元，缴费完成后，欠费应该是80元，余额应该是0。这是第三个Case。<br /><br />我们应该测试的是User类的外在表现，而不应该过问他如何实现。<br /><br />在测试的时候，我们需要一个可以重复的稳定的环境（真实环境往往不行），有时候无法直接建立User对象（比如User对象要依赖一个数据集），有时候真实的环境很难实现一些测试条件（比如边界值、非正常值）。这时候，我们就可以使用Mock、Stub这样的方法，把User建立起来，也把环境建立起来，然后测试User的表现。<br /><br /><strong>不需要测什么？</strong><br /><br />还是拿User举例子，还是测试pay方法，我们不应该测试这些东西：<br /><br />缴费如何实现，这个费用保存到哪个数据表的哪个字段里去了，是否符合某种数据关系。<br /><br />缴费以后的余额、欠费按照什么变量加减或者从某个数据字段中得到自己的值。这个不需要测。User类应该负责自己的费用问题，不应该把任何业务逻辑暴露给其他类（费用保存到哪里、什么数据结构，User应该负责）。外界只关心User的表现。<br /><br />缴费后应该不应该向交换机发送开机的指令，这个就更不需要测试了，这是User的调用者应该负责的事情。<br /><br /><strong>测试代码保证了什么？<br /></strong><br />测试代码只保证接口是正确的，至于业务正确不正确不是最关心的事情。比如User的测试代码，User把费用保存在数据表里、还是写在文件里，他是不关心的。甚至根本没有持久保存，他也不管，只要接口正确就通过。当然，内在实现上的错误总会通过接口表现出来。假如User没有把用户缴费保存起来，当我们在另外的空间中建立相同ID的另一个User对象时，就会得到错误的余额和欠费。<br /><br /><strong>单元测试究竟有什么用？</strong><br /><br />从某种角度上说，单元测试并不能保证业务的正确性，而只能保证接口的正确性（严格的说，其实连接口的正确性也无法保证，他只能保证接口"没有这些错误"）。如果一个类总是自己在默默的工作着，不提供接口告诉外界他干了什么，测试他的业务是很难的（也不应该去测试，否则必然要插手他的业务）。那么单元测试究竟有什么用呢？<br /><br />单元测试保证模块修改后的兼容性。可以通过单元测试来判断一个模块在修改后是否与修改前保持兼容、多大程度上不兼容、影响范围有多大。开发者就可以更加专注于模块内部的业务。单元测试无法代替人工的工作，他能够做到的是衡量一个模块修改后会不会影响其他模块的工作。这对程序的维护和升级有非常大的好处。<br />&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37808#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, 07 Feb 2006 14:07:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37808</link>
        <guid>http://lane-cn.javaeye.com/blog/37808</guid>
      </item>
      <item>
        <title>我对系统重构的理解</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37809" style="color:red;">http://lane-cn.javaeye.com/blog/37809</a>&nbsp;
          发表时间: 2006年02月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>什么是重构<br /></strong><br />重构，用最简单的一句话说：就是要在不改变系统功能的情况下，对系统的内部结构进行重新调整。重构的最直接目的在于改进软件系统的内部架构。一个好的结构可以更加适应于需求的变化，更好的满足客户的需求，最大限度的延长软件系统的生命周期。<br /><strong><br />为什么要重构<br /></strong><br />在不改变系统功能的情况下，改变系统的实现方式。为什么要这么做？投入精力不用来满足客户关心的需求，而是仅仅改变了软件的实现方式，这是否是在浪费客户的投资呢？<br /><br />重构的重要性要从软件的生命周期说起。软件不同与普通的产品，他是一种智力产品，没有具体的物理形态。一个软件不可能发生物理损耗，界面上的按钮永远不会因为按动次数太多而发生接触不良。那么为什么一个软件制造出来以后，却不能永远使用下去呢？<br /><br />对软件的生命造成威胁的因素只有一个：需求的变更。一个软件总是为解决某种特定的需求而产生，时代在发展，客户的业务也在发生变化。有的需求相对稳定一些，有的需求变化的比较剧烈，还有的需求已经消失了，或者转化成了别的需求。在这种情况下，软件必须相应的改变。<br /><br />考虑到成本和时间等因素，当然不是所有的需求变化都要在软件系统中实现。但是总的说来，软件要适应需求的变化，以保持自己的生命力。<br /><br />这就产生了一种糟糕的现象：软件产品最初制造出来，是经过精心的设计，具有良好架构的。但是随着时间的发展、需求的变化，必须不断的修改原有的功能、追加新的功能，还免不了有一些缺陷需要修改。为了实现变更，不可避免的要违反最初的设计构架。经过一段时间以后，软件的架构就千疮百孔了。bug越来越多，越来越难维护，新的需求越来越难实现，软件的构架对新的需求渐渐的失去支持能力，而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本，这就是这个软件系统的生命走到尽头的时候。<br /><br />重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后，使用重构的方式，不改变系统的外部功能，只对内部的结构进行重新的整理。通过重构，不断的调整系统的结构，使系统对于需求的变更始终具有较强的适应能力。<br /><strong><br />拒绝变化 VS 拥抱变化<br /></strong><br />按照传统的软件设计方式，软件的生产分为需求调查、概要设计、详细设计、编码、单体测试、联合测试、现场部署几个阶段。虽说这几个阶段是可以互相渗透，但是总的来说是有一定次序的，前一个阶段的工作是后一个阶段工作的基础。这就向下面这样一种V形的模式：<br /><br /><img src="/images/cnblogs_com/lane_cn/vdevelope.GIF" border="0" /><br /><br />往下的方向将系统进行分解，往上的方向将系统进行整合。这样的开发形式将软件开发分为设计前和设计后两个阶段，开发过程中存在一个重要的&#8220;里程碑&#8221;——设计说明书的。在设计说明书完成前，工程处于&#8220;设计&#8221;阶段，而在设计说明书完成之后，工程则进入&#8220;实施&#8221;阶段。一旦到了实施阶段，任何需求或者设计上的变更都是非常困难的，需要花费大量的成本。通常为了保证工程的顺利实施，开发人员常有这样一种冲动：按住客户的手，在需求说明书上签字。并且告诉客户：&#8220;从今天开始，任何需求变更都要停止，直到我们把现在这个东西做完。&#8221;这是一种拒绝变化的开发方式。<br /><br />软件系统要保持与企业的目标一致。时代在发展，人们的要求在不断的提高，客户的业务在不断的发展。在这种情况下，传统的先设计、再施工的V形式已经不能适应日益复杂的业务需要。软件工程逐渐演化成下面这样的过程：<br /><br /><img src="/images/cnblogs_com/lane_cn/sdevelope.GIF" height="160" hspace="0" width="163" /><br /><br />说明一下：<br />1、软件开发的目标要与企业目标保持一致，一个开发周期不宜时间过长，一般控制在半年到一年。系统部署后，并不意味着开发工作结束了，而是进入了下一个周期。<br /><br />2、工程以循环迭代的方式前进，这并不意味轻视了设计，不是要搞边调研、边设计、边施工的&#8220;三边&#8221;工程，相反，是更加重视设计的地位。软件开发的全过程都需要设计，软件开发是&#8220;持续设计&#8221;的过程。同时，设计工作也不只是简单过程分解、任务分配，而是概念设计、逻辑设计、物理设计等各个方面互相交织、齐头并进。<br /><br />传统的软件开发方式使用一种非常理想化的流程——先与客户讨论项目的范围，确定哪些需要做，哪些不需要做，然后规划一个完美的设计，不仅可以满足现在的需求，还能很好的适应未来的需求，设计完成后开始编码，然后测试组装，送到现场安装调试运行。这一系列过程就类似与发射一颗炮弹，首先要找到目标，然后根据地形、风力、目标的位置、移动速度等各种因素，计算提前量、炮弹发射的角度，计算出一个抛物线轨道，最后在合适的时间把炮弹发射出去。这一切都符合最正确的物理定律，一切都听起来很理想。如果没有意外条件，当然是可以击中目标的。但是炮弹一旦发射出去，一切就失去了控制，任何环境的变化都会造成偏离目标。尤其是对于一个运动的目标来说，计算过程十分复杂，很多情况下只能靠人估计。对于不规则的运动目标只能碰碰运气。这样的方式，命中率是很低的。<br /><br />新的软件开发过程不追求完美的、长期的、理想的计划，更加重视实际情况，重视需求的变化，提倡采用短期的计划。这是一种拥抱变化的过程。就象是在炮弹上安装了一个反馈装置，锁定目标后，确保大方向的正确，然后就将炮弹发射出去。炮弹在运行过程中不断的将目标位置偏移量输入反馈电路，根据反馈输出调整自己的运行路线，无限的逼近目标。这样，炮弹就拥有了制导能力，命中率大大增加。<br /><br />重构就可以增加工程的调整能力，他可以把产品回复到一个稳定的状态，可以基于这个状态达到下一个目标。如此反复前进，更好的满足客户的需求。<br /><br /><strong>保持兼容性<br /></strong><br />重构的目的在于改变系统的实现方式，而不改变原有的功能。这个过程中，判断兼容性就十分的重要。一个子系统、模块、类、函数是否与升级前保持兼容，如何判断这个兼容性，如何保持这个兼容性，这关系到重构的成本和重构的可能性。<br /><br />程序员学习写程序代码时，会发现随着程序代码愈来愈多，许多的程序代码不断重复出现和被使用，因此很自然的开始使用例程、子程序或是过程、函数等机制帮助我们进行程序代码整理的工作。于是很自然的，字体的分析方式演化成这个样子：将客户的需求过程进行分解，一步一步的分解，直到可以直接的实现他。这就是面向过程的分析方式。<br /><br />面向过程的分析方式对变化的能力是很弱的。为什么呢？因为面向过程的分析方式很容易造成一种倾向——不区分行动的主体。一个过程是没有主体的，他不是在为自己工作，而是在为&#8220;别人&#8221;工作。当我们修改了一个过程之后，我们很难判断这个过程是否保持向后兼容，其他过程会不会受到影响。因为这个过程对外界有意义的不仅是他的输入和输出，还包括每一步过程，每一步过程都可能含有一个非常隐讳的业务意义，对外界产生影响。<br /><br />因此，修改一个过程是非常困难的，通常升级一个面向过程的系统，可以采用两种方式：<br />1、写新的过程；<br />2、在原有的过程上加开关参数。<br /><br />除此以外的升级办法都很难保证原过程和新过程的兼容性，容易造成错误。<br /><br />为了更好的保证升级后模块的兼容性，应该采用面向对象的分析方式。按照这样的分析方式，一个对象为&#8220;自己&#8221;工作，他有完整的、独立的业务含义。对象之间通过接口发生联系，一个对象对外界有影响的部分只有接口，至于他做什么、如何做、做的对不对，则不是外界需要关心的事情。<br /><br />判断一个接口升级后是否保持兼容性就是一件比较容易的事情了。我们可以判断接口的输入输出是否符合下面两条规则：<br />1、升级后的输入是升级前的输入的超级；<br />2、升级后的输出是升级前的输出的子集。<br /><br />只要符合这两点，他就仍然可以在系统中运行，不会对其他对象造成危害。在实际的工程中，判断这个兼容性有一个更好的办法：<strong>自动化的单元测试</strong>。<br /><br />在重构的过程中，自动化的单元测试是非常好的保障。采用自动化的单元测试，不断运行测试，可以保证系统的结构改变的过程中，业务行为不发生改变。<br /></p>
          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37809#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>Sun, 05 Feb 2006 19:34:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37809</link>
        <guid>http://lane-cn.javaeye.com/blog/37809</guid>
      </item>
      <item>
        <title>存储过程——天使还是魔鬼</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37810" style="color:red;">http://lane-cn.javaeye.com/blog/37810</a>&nbsp;
          发表时间: 2006年01月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          看了<a href="http://heroman.cnblogs.com/">Heroman</a>的一篇文章，谈论<a href="http://heroman.cnblogs.com/archive/2006/01/10/314631.aspx">该不该在项目中使用存储过程代替SQL语句</a>。看后有一些感想，因为最近工作接触到一个系统，业务过程几乎完全是用存储过程实现的。随着系统的不断发展，新的需求逐渐难以支持。这个原因当然很复杂，即使不使用存储过程，可能也有同样的问题。但是既然谈到具体技术上，就来看一下一个主要以存储过程实现的系统到底有哪些问题。<br /><br />存储过程和嵌入程序中的SQL哪个更好，要用一种合理的比较方式来比，不能拿写的好的存储过程和写的烂的程序比，当然也不能拿写的烂的存储过程和写的好的程序比。我们先假设开发人员具有同样水平，项目组具有同样的组织协调能力，他们写出的存储过程和代码具有同样的质量，都已经根据产品的具体情况做出了最优的选择。<br /><br />很多人这样认为：存储过程运行在最靠近数据的地方，最大限度减少了业务处理的环节，因此具有最高的运行效率。对于一个独立的程序片断来说确实如此。但是当一个软件规模逐渐增大，业务逻辑逐渐变得复杂以后，这一点差距已经不会对运行效率造成决定性的影响了。这时候，影响程序效率的因素变的更加复杂，比如：系统对于并发任务的处理是否合理、是否具有分布式的能力、是否可以将常用的数据缓存&#8230;&#8230;这些能力靠存储过程来实现是非常难的，甚至是不可能的。一旦采用了存储过程，在程序漫长的生命周期中，要提高程序的运行效率就只有一个办法了：增加硬件投资。但是这个办法不一定有很好的效果，因为再好的硬件条件也无法弥补一些根本的缺陷。<br /><br />软件开发发展到现在，总的来说有个规律，就是要让开发者越来越少的考虑技术问题，越来越接近客户的业务思维。现代的软件开发，已经越来越接近这样的方式：研究客户的需求，为业务建立模型，分析系统的外部和内部需求，以最接近业务模型的方式建立软件系统模型。这样的方式建立的系统才能最好的满足客户的需要，同时也能较好的适应需求的发展。<br /><br />如果采用存储过程作为建立业务层的形式，结果就是回到&#8220;排列需求——数据库设计——界面设计——编码——测试&#8221;的道路上。这样当然是可以把系统做出来的。系统部署了，这只是他生命周期的开始，一切才刚开始。存储过程不仅实现了当前的业务需求，也建立了一系列的API。在漫长维护过程中，维护人员在这些API的基础上实现新的需求、修改这些API的错误。如果发现某个API&#8220;似乎&#8221;错了，或者不满足新的业务需求，没有人敢修改他们，最好的办法是：再加一个新的API。存储过程的数量越来越多，越来越难以命名——函数难命名不是编码的问题，而是设计的问题。于是，在项目运行两年以后，还是难以形成一个优质稳定的业务开发平台，人们还在探究数据表、字段、VARCHAR500、1403 data not found&#8230;&#8230;<br /><br />说到这里我也许应该得出结论，存储过程是不好的。可以这么说：用存储过程实现主要业务逻辑不是一个好办法。但是这个东西既然被创造出来，一定也有他适用的地方。<br /><br />如果我有这样的需求：数据库中有大量的实时运行数据，需要定期把这些数据进行简单的行列归整，放到另外一个数据库中做分析统计之用。在这种情况下我首选存储过程。存储过程应该处理&#8220;数据&#8221;，而不要处理&#8220;业务&#8221;。并且在这种情况下存储过程极大的减少了IO消耗，真正的体现了他的效率优势。<br />
          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37810#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 Jan 2006 23:20:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37810</link>
        <guid>http://lane-cn.javaeye.com/blog/37810</guid>
      </item>
      <item>
        <title>统一版本的诱惑</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37811" style="color:red;">http://lane-cn.javaeye.com/blog/37811</a>&nbsp;
          发表时间: 2005年12月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>几个月前从一家软件企业来到一家通信公司工作，进入了计费与信息系统部。这个部门维护着公司中的几个重要的系统：营业、计费、帐务、客户服务、资源管理、经营分析、产品销售，这些系统是公司业务的基础。通过几个月的忙碌，渐渐的熟悉了同事，也熟悉了工作环境，对于所维护的系统也有了越来越深的了解。<br /><br />营业和帐务系统的开发商是由一家位于东北的软件公司。这是一家上市公司，CMM5级，在通信、医疗、教育等多个行业都有产品，其中通信系统遍布全国的十几个省。开发商在我们的现场有十多个现场维护人员，协助我们进行营业和帐务系统的运行维护工作。系统的升级、新需求的分析都需要他们的协助。<br /><br />通过几个月的接触，了解了开发商的一些特点。最近有机会来到他们的总部，更是近距离的体验了他们的研发过程。开发商的一个很大的特点，就是采用&#8220;统一版本&#8221;的方式。具体的说就是：现场的维护人员原则上是不可以对代码做改动的。如果现场程序出现了问题，维护人员必须将现象和原因报告给总部的开发人员，由开发人员进行修改，然后总部发布新的版本，现场人员重新部署。客户提出了新的需求，也要现场人员进行分析以后提交到总部，进行统一的评估和测算，决定是否可以实现，用什么方式实现，然后将升级后的版本下发到各个省。这就是统一版本。全国各个省，都只使用同一个版本序列，各省根据自己的需要采用不同的版本号。各版本在总部统一维护，统一的规划，统一的标准，统一的软件。<br /><br />统一版本的实质是追求思想的统一，避免在各个地区之间出现分歧。人的思想是一种非常复杂的东西，尤其软件开发是一种以脑力为主的劳动，需要大量的沟通才能减少误会。一百个人总会有一百个想法。在各个地区间采用统一的版本，由总部实行严格的管理，在软件的设计上和实施上就避免了意见分歧。<br /><br />从项目管理的角度来说，采用统一版本的方式有利于对项目的范围和成本进行控制。客户的需求做不做，如何实现，什么时候实现，都由总部进行统一的规划，统一安排开发任务，实现在统一的版本中，然后下发到各地实施。这样就最大程度的避免了各地各自为战，原则不一致的现象。总部对各地维护人员的控制力是非常强的。<br /><br />采用这样一种开发和维护方式，由总部的研发人员进行设计和开发，测试之后下发到各个省，由各省的现场维护人员进行部署和维护。软件系统在运行过程中发现错误，现场人员分析现场的状况，然后将情况反应到总部，由总部寻找具体的原因，进行修改后，现场人员重新部署。客户如果有新的需求，可以和现场人员一起进行探讨，现场人员向总部提出。总部对各个省的需求进行统一的规划，安排研发任务，测试完成以后再重新到现场部署。总部的开发人员和现场维护人员之间采用这样的分工方式：总部人员集中了技术能力，可以统一安排各个省的研发任务，统一调度。现场人员了解实际的软硬件环境，了解客户的实际需求。这样的分工看起来是非常合理的。<br /><br />对于通信企业的应用系统来说，尽管在各个地区在业务方式上有各自的特殊情况，对系统的要求也不尽相同，但是毕竟是同样的经营领域，同样的企业性质，以共性为主，各省之间共同点是非常多的。采用统一的版本管理方式，也能够避免在不同版本之间重复实现一些共同的功能，避免重复的劳动，提高了开发工作的效率。<br /><br />然而，采用这样的统一版本的方式，也带来不少问题。<br /><br />最显然的问题就是：采用这样的方式，现场出现的问题和客户提出的需求无法得到及时的解决。通常是在现场发现一个故障，经过检查只不过是一个非常容易解决的问题。但是在统一版本的规则下，现场维护人员对版本没有控制权，必须向总部反应情况，得到解决之后再下发新的版本。这个过程有时候是相当长的。对于客户提出的新的需求，现场人员在经过分析后，也要提交到总部进行统一的规划，总部的人员对现场情况要进行了解，然后再统一的规划设计，安排开发力量。这样一个过程下来，环节很多，时间往往相当的长，要受到很多条件的限制。信息系统部经常因此面对业务部门大量的压力。如果能够将部分控制权交给现场的维护人员，对于简单的明显的故障、本地化的需求可以自行解决，在过程中保持和总部的协调，应该是一种更加理想的方式。<br /><br />在系统的运行维护过程中，现场的维护人员经常比较忙碌，压力也是比较大的。由于总部的开发人员发过来的程序总是会出现各种问题，造成现场运维人员处于救火状态，难以抽出时间和人力对需求进行思考，没有精力分析客户的业务。总部的开发人员远在东北，他们了解现场需求的唯一途径就是通过现场维护人员。这样一来，形成了研发人员不了解需求的局面，开发质量难以保证。并且，一些地区的个性化需求无法在总部进行有效的测试，这些代码没有经过测试就发到现场，交给维护人员测试、部署。这样就使得总部的开发人员受到的质量压力过小，不注重提高质量，进一步造成质量下降。然后运维人员更加忙于救火，更加没有精力分析需求。<br /><br />对需求没有了解，对现场的环境也缺乏直观的感受，造成设计和开发人员不注重用户的需求。研发人员经常是局限在技术细节中，将生动的客户需求生硬的理解成数据库的增删查改，顺带再向交换发几条指令。这样的研发过程，生产出来的软件是非常不好用的，功能凌乱，文档不着边际，bug也很多。造成用户操作和极其复杂，思想压力也比较大。<br /><br />将所有的版本都放在总部统一管理，实际效果也并不理想。统一版本的管理工作是十分复杂的。各省都有特定的环境，都有一些特殊的需求，为了适应本地的环境、实现这些需求而开发了一些功能。每个地市的版本都处于动态的变化中，新的需求要做，旧版本上还有bug要改，整体结构的开发仍然在进行，造成分支复杂，版本很多，管理难度巨大，经常出现错误。总部发到地区的程序部署以后，总会发现以前曾经实现的需求又不见了，以前消灭过的bug又出现了。客户和运维人员很多精力耗费在丢失的功能和重现的bug中。由于版本管理方面有这方面的问题，产品的升级工作带来了巨大的困难。<br /><br />电信企业需求可以分为两个层次，首先是具有行业特点的普遍性需求，这种需求较为稳定。其次是各运营商的个性化需求，这种需求复杂多变。各层次的需求应该由不同的人员、在不同的子系统中实现。核心的部分由一个小组进行开发，然后各个小组在这个基础上实现各地区特定的需求。底层的子系统的在进化的过程中，只要保持向后的兼容性，各地区的版本就不会受到影响。这样化整为零，减少了版本的复杂程度。升级维护都比较方便，也能够快速的响应个别客户的特别的需求。这样的开发形式是比较合理的。然而，开发商采用的是总部全权负责研发，现场全权负责维护的形式。总部研发人员负责全部的设计、编码、测试工作。在这种管理模式下，系统形成了一个统一的大版本，各地区的需求都实现在这个版本中。各地的一些需求相差较大，强行统一在一个版本中，甚至出现了下面这样的代码：<br /><br />if 四川 then<br />　　......<br />else if 云南 then<br />　　......<br />else<br />　　......<br />end if<br /><br />总部的开发人员没有在普遍需求和特定需求之间进行合理的分工，无法专注于解决行业内的普遍需求，有限的人力纠缠在各个地区独特的软硬件环境、应付独特的商业需求，耗费在细节上。而各地的维护人员面对着总部发过来的低质量的代码、僵化的设计、混乱的版本，部署维护困难重重。需求不能得到及时满足，程序bug需要漫长的过程才能改正，运营商得不到有力的支持。<br /><br />软件系统的开发，技术无疑是重要的，但决不是最重要的，业务需求始终要在最重要的位置。技术的目的就是要在项目中消除技术问题，到最后开发者和客户在一起谈论的是用户的需求，互相之间不是用技术语言交谈，而是用客户的业务语言交谈。做到这一点，才是技术的成功之处。开发商现在已经为这个项目工作了多年，在多个地区都有了部署，现在仍然在和客户谈论数据库表、存储过程，不能深入到客户的业务中去。这不是一种理想的状态。<br /><br />好的软件是用什么做出来的？不是用手，也不是用脑，而是用脚做出来的。要走出办公室，多跑一些地方，仔细的研究调查，才有可能了解客户，理解客户的需要。最后才能站在客户的角度考虑问题。这样做出来的东西，才是客户真正需要的东西。<br /></p>
          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37811#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, 27 Dec 2005 22:49:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37811</link>
        <guid>http://lane-cn.javaeye.com/blog/37811</guid>
      </item>
      <item>
        <title>好的法规为什么不能得到遵守</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37812" style="color:red;">http://lane-cn.javaeye.com/blog/37812</a>&nbsp;
          发表时间: 2005年11月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>很多法规的想法是非常好的，如果大家都能遵守的话，对于整个社会是很有好处的。但是经常有人不遵守这些法规，这是为什么呢？是这些人没有认识到这样的法规对大家都有好处吗，思想觉悟低。于是加强宣传，思想教育，效果不明显。加强违规的惩罚力度，有时候违法的现象依然严重。在很多情况下制度不过是摆设，另有一套潜规则被大家默默奉行着。<br /><br />举一个例子：银行存款实行实名制。这是个好规定，按照这个制度的设想，能够大量的避免经济诈骗、信用卡透支、贪污腐败。但是这个制度看起来只对老实人有效，仍然有人可以轻易的获得匿名存则用于诈骗活动。这是为什么呢？<br /><br />一个制度能否得到遵守，在于他是否顺应了利益的规律。政府颁布的法规，企业公布的制度，都是一种明规则。明规则反映了人们的一种美好的理想。但是这样的规则不一定能顺应利益的规律。一旦明规则违反了利益规律，就会失去效力。同时，另外一套规则就会取而代之。这套规则没有明确的文字，也没有人去刻意宣传。在实践中逐渐进化，建立在实际的力量对比基础上，具有坚强的生命力，这就是潜规则。<br /><br />比如存款实名制，这个制度的直接执行者是银行。对于银行来说，能够吸引到客户无论对银行整体，还是对银行中的员工都是有利的。而客户使用存款帐户进行诈骗，则不需要他们承担任何责任。在这样的情况下，这个实名制能不能得到贯彻，能不能发挥预想的效果，就是可想而知的了。<br /><br />明规则不能起作用，潜规则就来了。用别人的身份证，假的身份证都可以在银行开户，在纸上写下自己的号码塞到柜台里去有时候也可以，这就是潜规则。遵守这个潜规则，大家就都方便了。明规则忽视了权利的平衡，潜规则重新找到了这个平衡。那些坚持制度的银行会因为办理业务不方便，反而失去客户。不遵守潜规则要受到惩罚，真正起作用的是潜规则。一个制度的初衷是好的，但是没有符合利益的规律，又没有合理的奖惩手段来强化这种利益关系，就起不到效果。有时候甚至造成老实人吃亏，淘汰的都是清官。<br /><br />中国古代有不少皇帝，为了实现自己的理想，颁布了不少法令。皇帝有着最高的政治权利，但是这些法令也有很多成为摆设。只要忽略了利益方面的协调和引导，皇帝也管不住。<br /><br />最近有人提出一个意见：电信商提供移动通信服务要采用实名制。如果靠电信商、代理商自己执行，这个规定肯定还是个摆设。<br /></p>
          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37812#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, 19 Nov 2005 01:00:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37812</link>
        <guid>http://lane-cn.javaeye.com/blog/37812</guid>
      </item>
      <item>
        <title>为什么我们常忘记使用正则表达式</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37813" style="color:red;">http://lane-cn.javaeye.com/blog/37813</a>&nbsp;
          发表时间: 2005年10月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          有这么一个奇怪的现象，设计人员常常忘记使用一个非常了不起的发明：正则表达式。他们宁愿自己实现非常复杂的字符串处理逻辑，在一堆if、else里面晕头转向，面对变化的需求感觉束手无策。<br /><br />今天系统出现一个错误，判断一个电话号码类型的时候出现错误，误将一个0133开头的号码当作中国电信的号码。在一番寻找后，认为错误可能在一段程序里。打开程序的配置，定义了各种通配府、开闭范围标记、号码头，比较复杂。查到最后，是这个配置的解释程序出现了错误。配置中有一个项目：以01开头的号码属于中国电信。这个配置的位置在0133的前面，程序又没有使用最长匹配，而是用的最快匹配，从而导致了错误。错误很简单，但是比较隐蔽，开发人员找了半天。<br /><br />更麻烦的是，现在这个错误不好改了。系统已经运行了很长的时间，配置项目的序号不敢改，添加配置项目也要评估一下影响有多大。改程序也很麻烦。<br /><br />系统里面很多地方有这样的判断，判断字符串的开头、结尾、长度、区间&#8230;&#8230;全是使用if－else的判断。有了新业务，现有的配置规则不一定能支持，修改起来一定很麻烦。<br /><br />为什么不用正则表达式呢？判断规则大大的简化，程序逻辑简单了，可配置性强，完全是数据驱动的形式，测试起来也更容易，万一有了错误，改改表达式就行了。需求变化也不怕，有什么串不能用正则表达式来表示呢？<br /><br />还有什么情况下可以使用正则表达式呢：输入框的合法性检查，命令行的解释，字符串的替换，IP地址的范围界定&#8230;&#8230;<br /><br />应该把&#8220;正则表达式&#8221;五个字写在纸条上，贴在墙上。在设计的时候，要提醒自己不要忘记使用这么一个伟大的小发明。<br /><br />
          <br/>
          <span style="color:red;">
            <a href="http://lane-cn.javaeye.com/blog/37813#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>Fri, 14 Oct 2005 22:03:00 +0800</pubDate>
        <link>http://lane-cn.javaeye.com/blog/37813</link>
        <guid>http://lane-cn.javaeye.com/blog/37813</guid>
      </item>
      <item>
        <title>应用软件的层次划分</title>
        <author>lane_cn</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lane-cn.javaeye.com">lane_cn</a>&nbsp;
          链接：<a href="http://lane-cn.javaeye.com/blog/37814" style="color:red;">http://lane-cn.javaeye.com/blog/37814</a>&nbsp;
          发表时间: 2005年09月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>谈到应用程序的层次，我们平时所说的层次有两种：逻辑的层次（layer）和部署的层次（tier）。这两种层次划分的目的是不同的，因此划分方式也有一些差异，能够为应用程序带来的好处也是不同的。<br /><strong><br />逻辑层次<br /></strong><br />逻辑层次（layer）划分的最重要的目的在于<strong>调整应用程序各部分之间的依赖关系</strong>。应用程序可以看作数据和业务规则的集合，这个集合通过用户界面与用户发生交互。如果不划分层次，或者只划分最简单的层次，系统的结构就会是这样：数据库处于系统的中心地位，在此之上建立用户界面，业务规则写在用户界面里。<br /><br />这样做的问题在于：数据库作为应用程序的中心是不合适的，因为数据库只负责存储数据，而不能对数据做出解释（这是业务规则的任务），而业务规则分散在非中心的位置，零散的表达在用户界面中。一旦需求改变，业务规则必须随之改变，而业务规则是分散在各处的，我们就要四处寻找业务规则，进行修改。随着应用程序规模的扩大，这是一项非常艰难的任务。<br /><br />为了解决复杂的依赖关系，我们创建了业务层。典型的逻辑分层结构就是：表示层-&gt;业务层-&gt;数据层。<br /><br />建立业务层的方式可以非常简单：假如我们的应用程序要进行多项业务，我们分析这些业务的流程，找出这些流程中共同的部分，提取他们作为独立的过程，这就形成了最简单的业务层。这样，不同的界面之间