<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>TaoWen's Blog</title>
    <description></description>
    <link>http://taowen.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>关于estimation的闲言碎语</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/213767" style="color:red;">http://taowen.javaeye.com/blog/213767</a>&nbsp;
          发表时间: 2008年07月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <ul><li> estimation只是一个开始,不是结束.好的estimation不是developer估的好,还要靠BA大人们来管理scope,不然就算developer牛成马了,estimation还是一坨.</li><li> 相对于给出一个精确的绝对值来说,维护内在的相对关系更重要,一致性为王.</li><li> story写得不好,再estimate也是枉费功夫.</li><li> 不要总是关注story的大小,把一些story加起来构成一个完整feature的大小也很重要.对于客户来说,他们往往关注在feature级别.</li><li> 一个feature的大小,往往还取决于BA对它写了多少了个story.</li><li> 没有"一天"的story,请慎用最乐观的情况.</li><li> 需求不明应对方案一,拒绝estimate,直到有人给你讲明白到你觉得可以estimate的时候为止.</li><li> 需求不明应对方案二,做出假设,并且记录在案.但是不要做出明显不合理的假设.并要注意开发过程中对于当时假设的校验,BA要负责维护这些假设.</li><li> change request要落到实处.至少要把假设被打破,estimation需要重新修改的情况,放到明处.</li><li> 不要给出"10"作为估计.10不是estimation,10代表"你不知道".PM也不要天真的拿10去做release planning.</li><li> 正常的story不会太大也不会太小,而且大部分的大小是相对一致的.estimation往往都是一些中间的数.</li><li> story的大小,与参与的系统组建数量有关(纯客户端,客户端服务器,客户端服务器加新的表)</li><li> story的大小,与变化点的数量有关(如果情况a,则如何如何,如果情况b,则如何如何)</li><li> story的大小,与在同一块区域,过往story的复杂度有关(在一个复杂功能区域上添加新的业务规则,要难于新写一块)</li><li> 学习新技术的时间,不明集成点,测试时间等,不便分配到每个story中.倾向于不添加到estimation中.而是以risk factor等其他形式体现出来.</li><li> 砍掉的story,总是那些estimation高,实现起来容易的.留下的story,总是那些estimation低,难于实现的.想想吧,我们该如何做.不要抱有侥幸心理.estimate的时候,就要考虑如果这个story被砍掉了,你觉得你亏不亏.</li><li> 拒绝用ideal hour做estimation,最小的单位不能小于半天(最好是一天)</li><li> 在没有得到更多信息的情况下,re-estimate等价于对开发人员的连夜审讯,你必将被屈打成招.拒绝做这样无谓的事情.</li><li> 好的BA,好的BA,好的BA....(好的标准是什么?温柔,贤惠,善解人意...)</li></ul>
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/213767#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, 11 Jul 2008 09:06:25 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/213767</link>
        <guid>http://taowen.javaeye.com/blog/213767</guid>
      </item>
      <item>
        <title>let's placeBid</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/192121" style="color:red;">http://taowen.javaeye.com/blog/192121</a>&nbsp;
          发表时间: 2008年05月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          这个例子很老啦，在之前的Domain Model的争论中被广泛引用（参见：http://www.javaeye.com/topic/11712）。我再来炒炒冷饭。<br /><br />这个Domain可以简化为这样：<br /><br /><pre name="code" class="java">
public class Item {
  private Set&lt;Bid> bids = new HashSet&lt;Bid>();
}
</pre><br /><br /><pre name="code" class="java">
public class Bid {
  private User bidder;
  private int amount;
}
</pre><br /><br />现在我们要添加一个行为叫placeBid。于是我们可以写出如下的贫血代码：<br /><br /><pre name="code" class="java">
    Bid currentMaxBid = itemDAO.getMaxBid(itemId);
    Item item = itemDAO.findById(itemId, true);
    if (currentMaxBid != null && bidAmount &lt;= currentMaxBid.getAmount()) {
        throw new BizException("Bid too low.");
    }
    Bid newBid = new Bid(this, bidder, bidAmount);
    item.getBids().add(bid);
    itemDao.save(item);
</pre><br /><br />这样的代码有什么问题吗？我觉得在没有更多的需求，系统规模不大的情况下，无法拿出有力的证据证明这样做的缺陷。那么我们再来看看如何用经典的Hibernate做法来做领域模型（参见robbin的第二种模型）：<br /><br /><pre name="code" class="java">
public class Item {
  public Bid placeBid(User bidder, int bidAmount, Bid currentMaxBid) {
     if (currentMaxBid != null && bidAmount &lt;= currentMaxBid.getAmount()) {
        throw new BizException("Bid too low.");
     }
     Bid newBid = new Bid(this, bidder, bidAmount);
     bids.add(newBid);
     return newBid;
  }
}
</pre><br /><br />在那篇老帖子（我知道，n年前了）中，七彩狼提出了一个问题，就是:<br /><br /><div class="quote_title">引用</div><div class="quote_div"><br />第三类DomainLogic，也就是需要依赖到复杂查询的Logic<br /></div><br /><br />那需要这样的逻辑的是什么？远在天边，近在眼前。currentMaxBid哪来的？不还是从DAO中取出来的嘛。我Item包含了自己的bids，它居然没有办法知道maxBid是什么，还需要从外边用参数传进来。这是不合理的，但是却是不得已的。Hibernate的经典解决方案制约我们必须把这样的查询逻辑放在Domain的使用者那一边中调用DAO来完成。如果改用（http://www.javaeye.com/topic/191261）我说的RichSet，就可以轻松解决：<br /><br /><pre name="code" class="java">
public class Item {
  private RichSet&lt;Bid> bids = new DefaultRichSet&lt;Bid>();
  public Bid placeBid(User bidder, int bidAmount) {
     Bid currentMaxBid = bids.max("amount");
     if (currentMaxBid != null && bidAmount &lt;= currentMaxBid.getAmount()) {
        throw new BizException("Bid too low.");
     }
     Bid newBid = new Bid(this, bidder, bidAmount);
     bids.add(newBid);
     return newBid;
  }
}
</pre><br /><br />max在缺省情况下的实现是遍历列表。在Hibernate增强之后就变成了SQL查询。类似于bids.add在缺省情况是列表操作，Hibernate存储的时候变成了SQL的INSERT。<br /><br />我的观点是，所谓的大批量操作不属于Domain Logic是由于Hibernate造成的限制。实际情况中，Domain中应该有一个叫Repository的对象。它封装了一个List。<br /><br /><pre name="code" class="java">
public class ItemRepository {
  private RichList&lt;Item> items
  public ItemRepository(RichList&lt;Item> items) {
    this.items = items;
  }
}
</pre><br /><br />作为领域对象的ItemRepository，它是不关心items是从哪里来的。它就认为这个items是domain中的所有的item。于是，addItem的item没名字不重复的校验就可以自然的放在ItemRepository上。<br /><br /><pre name="code" class="java">
public class ItemRepository {
  public void addItem(String name) {
    if (items.find("name").eq(name).size() > 0) {
       throw new BizException("name duplicated");
    }
    Item item = new Item(name);
    items.add(item);
    return item;
  }
}
</pre><br /><br />在Domain中，所有的Item列表，都应该经过ItemRepository包装。从而保证Item的名字唯一性。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/192121#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 May 2008 10:11:13 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/192121</link>
        <guid>http://taowen.javaeye.com/blog/192121</guid>
      </item>
      <item>
        <title>贫血的Domain Model</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/191261" style="color:red;">http://taowen.javaeye.com/blog/191261</a>&nbsp;
          发表时间: 2008年05月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          好老的话题啦。拿出来炒炒冷饭。各位见谅。<br />——————————————————————<br />Domain Model贫血是说属于Domain Model的逻辑没有放在Domain Model中。那是哪些逻辑没有放到Domain Model中，从而导致贫血一说呢？原因有很多，但是我认为最主要是Service中的那些逻辑。而这些逻辑又有一个共同的特点就是依赖于DAO，或者说需要查询数据库。Robbin的帖子：http://www.javaeye.com/topic/57075，举了一个很好的例子。我取其中的一个部分在这里做演示用。<br /><br /><pre name="code" class="java">
public class Employee {
    private Set&lt;Task> tasks = new HashSet&lt;Task>();
}
</pre><br /><br /><pre name="code" class="java">
public class Task {
    private String name;
    private Employee owner;
    private Date startTime;
    private Date endTime;
}
</pre><br /><br />这是一个很简单的一对多的关系。现在要查找指定员工的处理中的任务。如果忽略数据库的存在，我想大部分的同志都会这么实现：<br /><br /><pre name="code" class="java">
public class Employee {
    private Set&lt;Task> tasks = new HashSet&lt;Task>();
    public Set&lt;Task> getProcessingTask() {
       ...
    }
}
</pre><br /><br />这也符合OO数据隐藏的基本原则。但是如果有数据库存在，怎么写就不那么容易决定了。如果没有Hibernate这样的ORM。那肯定是：<br /><br /><pre name="code" class="java">
public class TaskDAO {
   public Set&lt;Task> getProcessingTasks(Employee employee) {
      ...//sql
   }
}
</pre><br /><br />那我觉得，这就导致了Domain Model的失血。因为没有数据库的时候，这这个方法本来应该在Employee上的，而不是在DAO上的。<br />如果有Hibernate呢？是不是我就可以把这段代码写到Employee里面去呢？<br /><br /><pre name="code" class="java">
@Entity
public class Employee {
    @OneToMany
    private Set&lt;Task> tasks = new HashSet&lt;Task>();
    public Set&lt;Task> getProcessingTask() {
       ...
    }
}
</pre><br /><br />还是有问题。因为访问tasks的时候，Hibernate会去加载数据。getProcessingTask会便利所有的task。如果task的数量很多，这降极大的影响性能。所以为了能够享受到关系数据库查询速度的好处，我们要还要利用SQL。于是DAO又再次地找到了自己的位置。那么怎么解决这个问题呢？在http://www.javaeye.com/topic/57075的回帖中nihongye同学提出了一个解决方案。本质来说就是不让hibernate来映射tasks，改由查询来获得。加上Spring支持的@Configurable标记，我们可以把代码写成这样<br /><br /><pre name="code" class="java">
@Entity
@Configurable
public class Employee {
    private TaskDao dao;
    public Set&lt;Task> getProcessingTask() {
        return dao.getProcessingTask(this);
    }
    public void setTaskDao(TaskDao dao) {
        this.dao = dao;
    }
}
</pre><br /><br />我们当然还可以把TaskDao替换成变的形式。比如http://www.javaeye.com/topic/65406里firebody提到的那样。但是本质上来说，都是让Employee能够直接去使用Hibernate做查询。但是坏处是给Domain纯净分子的口实。虽然，我认为和ActiveRecord类似，entity绑定在数据库上没啥不好。另外一个缺点就是，要么仍然有一个Dao来封装查询逻辑的实现，要么Employee的实现中出现太多的hibernate api，而且写法复杂。这也就是Robbin一再强调，ActiveRecord那样的api在Java世界中不是不可以，而是实现复杂难度高的原因。注入可以解决问题，但是对Hibernate的依赖强而且写法丑陋。<br />那么有没有更优美的方案呢？有：<br /><br /><pre name="code" class="java">
public class Employee {
    private RichSet&lt;Task> tasks = new DefaultRichSet&lt;Task>();
    public RichSet&lt;Task> getProcessingTasks() {
        return tasks.find("startTime").le(new Date()).find("endTime").isNull();
    }
...
}
</pre><br /><br /><br />RichSet是我自己编造的一个名字。它是一个”rich“的set。其实就是附加了一些find，sort，sum之类的操作。<br /><br /><pre name="code" class="java">
public interface RichSet&lt;T> extends Set&lt;T> {
    Finder&lt;RichSet&lt;T>> find(String expression);
    int sum(String expression);
}
</pre><br /><br />DefaultRichSet是这些附加操作的内存版本的实现。这个能解决问题么？还是不能，这时候getProcessingTasks的时候，richSet还是去遍历内部的_tasks，然后把结果过滤出来。而且，hibernate还拒绝接受这样set。为了让hibernate能够接受RichSet，我们需要这么写配置文件。<br /><br /><pre name="code" class="xml">
&lt;hibernate-mapping default-access="field" package="net.sf.ferrum.example.domain">
    &lt;class name="Employee">
        &lt;tuplizer entity-mode="pojo" class="net.sf.ferrum.RichEntityTuplizer"/>
        &lt;id name="id">
            &lt;generator class="native"/>
        &lt;/id>
        &lt;property name="name"/>
        &lt;property name="salary"/>
        &lt;many-to-one name="department"/>
        &lt;set name="tasks" cascade="all" inverse="true" lazy="true">
            &lt;key/>
            &lt;one-to-many class="Task" />
        &lt;/set>
    &lt;/class>
&lt;/hibernate-mapping>
</pre><br /><br />通过指定RichEntityTuplizer，我们可以控制Hibernate的动态增强过程。<br /><br /><pre name="code" class="java">
public class RichEntityTuplizer extends PojoEntityTuplizer {
    public RichEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
        super(entityMetamodel, mappedEntity);
    }

    protected Setter buildPropertySetter(final Property mappedProperty, PersistentClass mappedEntity) {
        final Setter setter = super.buildPropertySetter(mappedProperty, mappedEntity);
        if (!(mappedProperty.getValue() instanceof org.hibernate.mapping.Set)) {
            return setter;
        }
        return new Setter() {
            public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {
                Object wrappedValue = value;
                if (value instanceof Set) {
                    HibernateRepository repository = new HibernateRepository();
                    repository.setSessionFactory(factory);
                    wrappedValue = new HibernateRichSet((Set) value, repository, getCriteria(mappedProperty, target));
                }
                setter.set(target, wrappedValue, factory);
            }

            public String getMethodName() {
                return setter.getMethodName();
            }

            public Method getMethod() {
                return setter.getMethod();
            }
        };
    }
}
</pre><br /><br />这样，tasks就不再是DefaultRichSet了。Hibernate会尝试去增强为PersisentSet，但是被RichEntityTuplizer改写为增强HibernateRichSet了。这样就形成了HibernateRichSet -> PersisentSet -> DefaultRichSet -> HashSet 的包含关系。<br /><br />当用户尝试在tasks上做find的时候，就不再是DefaultRichSet来做collection遍历了，而是HibernateRichSet去拼装一个DetachedCriteria。最后当用户在查询的结果上取size()或者取具体元素的时候，这个criteria被拿去求值。<br /><br />通过使用RichSet，domain model具有了对自身进行查询的能力。更重要的是，这种能力的获得，不是通过把Hibernate session注入到domain model中。domain仍然是纯净的，没有依赖于数据库的东西。而且domain是可以脱离容器使用的。new Employee出来就可以直接使用，测试。区别只是经过repository增强的entity会使用sql，而transient的entity所有的查询都是通过遍历实现的。<br /><br />没有了DAO之后，Domain Model是不是能够摆脱贫血的困扰呢？这个还需要观察。不过我认为至少是向前迈了一步了。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/191261#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, 09 May 2008 00:18:47 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/191261</link>
        <guid>http://taowen.javaeye.com/blog/191261</guid>
      </item>
      <item>
        <title>图形界面自动化测试</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/189539" style="color:red;">http://taowen.javaeye.com/blog/189539</a>&nbsp;
          发表时间: 2008年05月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Windows<br />Win32 API (pywinauto, autoit)<br />MS Accessbility (?)<br />UI Automation (eft, white)<br />In CLR object model (Sharprobo)<br /><br />WEB<br />In browser DOM, Using frame (Selenium)<br />In browser DOM, Using proxy (Sahi)<br />Out browser DOM, Using specific browser pluggin (Watir)<br /><br />Java<br />In JVM object model/java.awt.Robot (abbot)<br />Java Access Bridge (?)<br /><br />Flash<br />In Flash VM object model, Using loader (fluorida)<br /><br />There are a LOT of frameworks to deal with different GUI APIs. They producing similar tools to describe a common concept - UI Automation. The process to auotmate a UI is actually qutite easy:<br /><br />1. Locate, find a control in the gui by a locator (ID, Criteria, XPath, Css Selector...)<br />2. Action, simulate a user interaction (Type, click...)<br /><br />The driver to drive the UI automation process should and have to be different from API to API. But the concept/language/tool/editor behind should be unified to save investment, and help smooth learning curve.
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/189539#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, 04 May 2008 22:16:14 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/189539</link>
        <guid>http://taowen.javaeye.com/blog/189539</guid>
      </item>
      <item>
        <title>the paint points of xaml</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/156363" style="color:red;">http://taowen.javaeye.com/blog/156363</a>&nbsp;
          发表时间: 2008年01月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Pain Point 1: XAML always create the controls by its default constructor<br /><br />This means, you need to have a default constructor for you control, and the constructor will always be used by XAML. So, you can not use constructor dependency injection to pass things like services, gateways to your control. Also, you will not have chance to pass data in constructor, although the data might be must-have for the specific type of control<br /><br />Paint Point 2: can not control XAML to create or not to create some part of GUI<br /><br />Sometimes, the GUI is not static. It could be dynamic because the GUI would be different for the data it is presenting, such as for a meeting in the past it should show a adding note button, for a meeting in the future it should not. And more often, the security control requires the GUI to be different according to the role.<br /><br />Paint Point 3: XAML is using XML, which contains too many visual noise<br /><br />compared to things like YAML, XML is definitely not very friendly to our eyes. The things worse than XML I can come up is the braces of Lisp. Also, XML makes it harder to edit manually<br /><br />Paint Point 4: Layouting in Grid<br /><br />Using grid layout currently requires you to specify the row and column for all the children of a grid. It is very error-prone when the grid becomes large. But grid is a must-have for any non-trivial GUI, and there is not replacement for it yet.<br /><br />Paint Point 5: Things not checked in compiling time<br /><br />There are lots of things not checked by the compiler in XAML. Things like binding, resource looking up for example. And it is harder to cross reference between xaml and code.<br /><br />Paint Point 6: More files<br /><br />one file for xaml one file for cs. It requires more steps to create a new user control and is confusing to new comers.<br /><br />Paint Point 7: Separating concerns<br /><br />the default way events get handled is in the partial class of the XAML. It is not a good way of separating concerns and not good oo design. the windows and user controls usually doing too much in rich client application. It is not the fault of XAML in general, but it is not promoting a good model either by its weird way of hooking up event in xaml.<br /><br />Paint Point 8: Hard to test<br /><br />It is hard to test in many ways. First, not easy to inject dependency means you can not mock those expensive things like network connection. Second, creating a real window is taking more than ten seconds. Third, many things are in a static singleton model like resource looking up and the single instance application object.
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/156363#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, 16 Jan 2008 08:56:13 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/156363</link>
        <guid>http://taowen.javaeye.com/blog/156363</guid>
      </item>
      <item>
        <title>lessons we have learnt about office integration</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/156361" style="color:red;">http://taowen.javaeye.com/blog/156361</a>&nbsp;
          发表时间: 2008年01月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Lesson 1: trust it<br /><br />Everything could cause problem. There is no API could be really stable. Even it is stable itself, the interop and version difference or mis-use could cause problems as well.<br /><br />Lesson 2: rely on 3rd party vendor<br /><br />Add-in express is causing lots of troubles. Should be no feature rely on some solo 3rd party vendor, at least we need some backup plan.<br /><br />Lesson 3: not taking resource management into consideration at the very beginning<br /><br />COM requires you to manage the resource. It can cause the outlook won't quit, or powerpoint crash sometime if you leave it uncared. Actually, pool might be a good option.<br /><br />Lesson 4: not wrapping the api<br /><br />Using the raw api directly is dangerous. Not only because it could be too low-level, and also you lose the chance to change the api you use, because there is no way to change the api, becaue they are COM and closed sourced. Also, not wrapping it means you need to startup the real office application to test your addin.<br /><br />Lesson 5: using office api in multi threading code<br /><br />massing with multi threading and office api could cause problem as well. As it is not designed to be called from thread other than the primary gui thread.<br /><br />Lesson 6: going into in-process addin model too early<br /><br />being a addin means you can have consistent life cycle with the office application you are integrating with. But if we only want to use office in some part not the whole application, we probably should not adopt the addin model too early. <br /><br />Lesson 7: integrate too much<br /><br />sometimes, we are integrating too much. When user talk about using excel user defined function, it could be just use that to input a formula, but not necessarily pulling the data or doing the calculation. And popping up a separate window or putting the window beside outlook window could be a lot easier than using form region or outlook customized form.<br /><br />Lesson 8: not taking deployment and version difference into consideration<br /><br />deployment of addin could be very hard, and addin written in VSTO is even harder. Think about it, all the time. The version of office could be problem as well. Such as outlook 2007 can not use WCF to seralize the IList but 2003 can.<br /><br />Lesson 9: annoying the end-user<br /><br />slowing down the outlook startup. flash a window when excel launches. popping up error message box when editing tables in powerpoint. or flash and slows opening up the email composer in outlook for one second. All those kinds of things are not acceptable. Whatever our application is doing, should not annoy the end-user.<br /><br />Lesson 10: mixing hacking code with rest of production code<br /><br />It is the reality of life, we have to hack using COM, C++, win32 api or anything that works to get things done when working with office. But mixing the hacking code with other production code is really a bad idea. We need to build a layer to wrap the office api, and locking all the monsters there.<br /><br />Lesson 11: expose .NET win form control as ActiveX and embed in outlook form or form region<br /><br />It is just unstable, and with limited security permission.<br /><br />----<br /><br />The projects I have worked on with office product include:<br /><br />a excel addin along with udf for pulling series data from energy server<br /><br />a powerpoint addin for analyst doing prototype & storyboarding better (www.viprototype.com)<br /><br />a outlook 2007 PoC project to demonstrate its extending potentials<br /><br />a crm project with tight outlook integration also can export meeting details to word to print.
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/156361#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, 16 Jan 2008 08:54:09 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/156361</link>
        <guid>http://taowen.javaeye.com/blog/156361</guid>
      </item>
      <item>
        <title>Outlook MAPIOBJECT</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/154522" style="color:red;">http://taowen.javaeye.com/blog/154522</a>&nbsp;
          发表时间: 2008年01月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Outlook的对象模型中，很多对象都有一个MAPIOBJECT属性。这个属性可以用来获得对应的MAPI对象。最简单的应用可能就是取得MailItem上的一些额外属性，比如取得Attachment的ContentId。<br />要正确使用这个属性不容易。首先，不要在进程外取得属性的值，也就是说不能用Office Automation来启动Outlook，必须是在AddIn中取值。用伪代码，示例如下：<br /><br /><ul><li>Marshal.GetIUnknownForObject(attachment.MAPIOBJECT)</li><li>Marshal.QueryInterface(pUnk, ref IID_IMAPIProp, out pMAPIPropObj); // Guid IID_IMAPIProp = new Guid("{00020303-0000-0000-C000-000000000046}");</li><li>pMAPIProp->GetProps((LPSPropTagArray)props, MAPI_UNICODE, &count, &propValue); // ULONG props[] = {1, 0x3712001E}; // property tag for content id</li></ul><br /><br />主要的障碍是IMessage没有.NET版本的，所以我使用了C++/CLI来做Interop。替代方案有Redemption，和MAPI33，但是都是要收费的。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/154522#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, 09 Jan 2008 18:06:44 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/154522</link>
        <guid>http://taowen.javaeye.com/blog/154522</guid>
      </item>
      <item>
        <title>北京地铁二号线</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/154226" style="color:red;">http://taowen.javaeye.com/blog/154226</a>&nbsp;
          发表时间: 2008年01月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          早上送女友去上班，从东直门到宣武门。因为反正要返回东直门的，所以索性坐了完整一圈。早八点的二号线，真是相当的恐怖啊。观察出了一个最高峰的地铁段，从复兴门到阜成门。也许大家都住在一号线沿线，然后去金融街上班吧。东直门的人反而不是很多。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/154226#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, 09 Jan 2008 09:45:42 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/154226</link>
        <guid>http://taowen.javaeye.com/blog/154226</guid>
      </item>
      <item>
        <title>汉语编程，有搞头</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/139947" style="color:red;">http://taowen.javaeye.com/blog/139947</a>&nbsp;
          发表时间: 2007年11月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          汉语编程上CSDN首页了。不少骂傻逼的人，不少人说没搞头。我觉得，还是有搞头的。但是搞法，不是现在的这种搞法。不是用<br />如果（真）<br />替代<br />if (true)<br />这就没搞头，谁都知道输入中文慢，谁都知道大部分的API是英文的。这个层次太低！在实现这个层面，搞中文编程，没有多少实际意义。<br /><br /><strong>提高层次是关键</strong><br /><br />if，else这个层面，是面对程序员的。作为一个程序员，你会用中文编程吗？需要中文的人，是那些懂业务不做编码的人。如果发明一种汉语编程语言来描述业务规则，而不是具体的实现，然后让懂业务的人去读，甚至让他们去写。说不定，还有搞头。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/139947#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 Nov 2007 15:19:56 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/139947</link>
        <guid>http://taowen.javaeye.com/blog/139947</guid>
      </item>
      <item>
        <title>Marshal.ReleaseComReference</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/136503" style="color:red;">http://taowen.javaeye.com/blog/136503</a>&nbsp;
          发表时间: 2007年10月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          someone told me, don't do that. Framework should free the com reference for you. but...<br /><br />if you are writing a AddIn for PowerPoint, you might have seen the same problem. That is, if you reference a element of GroupItems without release it immediately, next time, you group it into bigger group, and trying to reference the GroupItems of the biggger group, PowerPoint totally screwed up. Here is the common solution for such kind of situation.<br /><br />The idea is using your own object to wrap RCW(raw com wrapper). In the constructor, you register yourself in the ComObjectsPool. When you want to clean things up, clean com objects from the pool.<br /><br /><pre name="code" class="c#">
    public interface IComObject : IDisposable
    {
        bool Release();
    }
</pre><br /><br /><pre name="code" class="c#">
    public abstract class ComObject : IComObject
    {
        private bool shouldRelease = true;
        public abstract void Dispose();

        public ComObject()
        {
            ComObjectsPool.PutIn(this);
        }

        public bool Release()
        {
            if (shouldRelease)
            {
                Dispose();
                return true;
            }
            return false;
        }

        public bool ShouldRelease
        {
            get { return shouldRelease; }
            set { shouldRelease = value; }
        }
    }
</pre><br /><br /><pre name="code" class="c#">
    public static class ComObjectsPool
    {
        private static readonly List&lt;IComObject> comObjects = new List&lt;IComObject>();

        public static void PutIn(IComObject comObject)
        {
            comObjects.Add(comObject);
        }

        public static void ReleaseAll()
        {
            List&lt;IComObject> unreleasedComObjects = new List&lt;IComObject>();
            foreach (IComObject comObject in comObjects)
            {
                if (!comObject.Release())
                {
                    unreleasedComObjects.Add(comObject);
                }
            }
            comObjects.Clear();
            comObjects.AddRange(unreleasedComObjects);
        }
    }
</pre> <br /><br /><pre name="code" class="c#">
    public class AdaptedShape : ComObject, IShape
    {
        private readonly Shape shape;

        public AdaptedShape(Shape shape)
        {
            this.shape = shape;
        }

        public override void Dispose()
        {
            Marshal.ReleaseComObject(shape);
        }
    }
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/136503#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, 29 Oct 2007 22:46:43 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/136503</link>
        <guid>http://taowen.javaeye.com/blog/136503</guid>
      </item>
      <item>
        <title>How to make it real?</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/132778" style="color:red;">http://taowen.javaeye.com/blog/132778</a>&nbsp;
          发表时间: 2007年10月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://www.infoq.com/news/2007/10/next-gen-functional-testing" target="_blank">http://www.infoq.com/news/2007/10/next-gen-functional-testing</a><br /><br />pretty easy:<br />UI automation testing is the key thing<br />Distributed running tests in Virtualized environment is critical<br />BA's lo-fi prototype should be testing script<br />BA's acceptance criteria should be testing script<br />QA's automation testing script should be combination of lo-fi prototype and acceptance<br />Screen recording + UI spy should help us create lo-fi prototype ui testing script easier, and evolve it with real look-and-feel
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/132778#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, 17 Oct 2007 09:45:34 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/132778</link>
        <guid>http://taowen.javaeye.com/blog/132778</guid>
      </item>
      <item>
        <title>简化测试代码</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/131987" style="color:red;">http://taowen.javaeye.com/blog/131987</a>&nbsp;
          发表时间: 2007年10月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          问题场景：<br />有一个方法类似于下面的状况：<br />...<br />if (condition1) {<br />...<br />}<br />else {<br />...<br />}<br />...<br />if (condition2) {<br />...<br />}<br />else {<br />...<br />}<br />...<br />那么你该写几个测试来覆盖这段代码呢？答案是四个。<br />如果你有三个条件呢？答案是八个。<br />那么更多呢？。。。<br />解决办法：<br />...<br />do_first_thing();<br />...<br />do_second_thing();<br />...<br />抽取出两个方法。然后针对每个抽取的方法来写测试。而对原来的那个方法，只需要写一个测试了。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/131987#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, 15 Oct 2007 16:10:39 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/131987</link>
        <guid>http://taowen.javaeye.com/blog/131987</guid>
      </item>
      <item>
        <title>五个类，解析单双引号</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/131789" style="color:red;">http://taowen.javaeye.com/blog/131789</a>&nbsp;
          发表时间: 2007年10月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="c#">
    public interface IBlock
    {
        string Quote();
        void Append(char c);
        bool ShouldAppendQuotedBlock(char c);
    }
</pre><br /><br /><pre name="code" class="c#">
    public abstract class NestedBlock : IBlock
    {
        private readonly NestedBlock parent;
        private readonly List&lt;IBlock> blocks;
        private IBlock currentBlock;

        public NestedBlock(NestedBlock parent)
        {
            this.parent = parent;
            blocks = new List&lt;IBlock>();
        }

        public virtual string Quote()
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (IBlock block in blocks)
            {
                stringBuilder.Append(block.Quote());
            }
            return stringBuilder.ToString();
        }

        public abstract void Append(char c);
        public abstract bool ShouldAppendQuotedBlock(char c);

        protected NestedBlock Parent
        {
            get { return parent; }
        }

        protected IBlock CurrentBlock
        {
            get { return currentBlock; }
        }

        public void AppendBlock(IBlock block)
        {
            blocks.Add(block);
            currentBlock = block;
        }
    }
</pre><br /><pre name="code" class="c#">
    public class QuotedBlock : NestedBlock
    {
        private readonly IQuoteStrategy quoteStrategy;
        private readonly char expectedQuote;

        public QuotedBlock(IQuoteStrategy quoteStrategy, char expectedQuote, NestedBlock parent) : base(parent)
        {
            this.quoteStrategy = quoteStrategy;
            this.expectedQuote = expectedQuote;
            AppendBlock(new RootBlock(quoteStrategy));
        }

        public override string Quote()
        {
            return expectedQuote + quoteStrategy.Quote(base.Quote()) + expectedQuote;
        }

        public override void Append(char c)
        {
            if (c == expectedQuote)
            {
                Parent.AppendBlock(new UnquotedBlock());
            }
            else
            {
                CurrentBlock.Append(c);
            }
        }

        public override bool ShouldAppendQuotedBlock(char c)
        {
            return false;
        }
    }
</pre><br /><pre name="code" class="c#">
    public class UnquotedBlock : IBlock
    {
        private readonly StringBuilder stringBuilder = new StringBuilder();

        public string Quote()
        {
            return stringBuilder.ToString();
        }

        public void Append(char c)
        {
            stringBuilder.Append(c);
        }

        public bool ShouldAppendQuotedBlock(char c)
        {
            return IsQuotationMark(c);
        }

        private static bool IsQuotationMark(char c)
        {
            if (c == '"')
            {
                return true;
            }
            return c == '\'';
        }
    }
</pre><br /><pre name="code" class="c#">
   public class RootBlock : NestedBlock
    {
        private readonly IQuoteStrategy quoteStrategy;

        public RootBlock(IQuoteStrategy quoteStrategy) : base(null)
        {
            this.quoteStrategy = quoteStrategy;
            AppendBlock(new UnquotedBlock());
        }

        public override void Append(char c)
        {
            if (CurrentBlock.ShouldAppendQuotedBlock(c))
            {
                AppendBlock(new QuotedBlock(quoteStrategy, c, this));
            }
            else
            {
                CurrentBlock.Append(c);
            }
        }

        public override bool ShouldAppendQuotedBlock(char c)
        {
            throw new ViScriptSystemException();
        }
    }
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/131789#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, 14 Oct 2007 19:14:09 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/131789</link>
        <guid>http://taowen.javaeye.com/blog/131789</guid>
      </item>
      <item>
        <title>.NET Remoting Callback</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/131531" style="color:red;">http://taowen.javaeye.com/blog/131531</a>&nbsp;
          发表时间: 2007年10月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          有三个主要的障碍：<br />1、服务器解析不到客户端的assembly，因为callback的代码在客户端的assembly中<br />2、TypeFilterLevel默认不是Full<br />3、普通callback代码不能访问客户端的变量，因为不是serializable的<br /><br />解决办法：<br />1、利用AppDomain的AssemblyResolve事件，让自己来处理assembly的查找逻辑。并且让客户端在开始的时候把自己的assembly位置发给服务器。<br />2、<br /><pre name="code" class="c#">
BinaryServerFormatterSinkProvider serverFormatter = new BinaryServerFormatterSinkProvider();
            serverFormatter.TypeFilterLevel = TypeFilterLevel.Full;
            Hashtable properties = new Hashtable();
            properties.Add("portName", "thoughtworks");
            IpcChannel channel = new IpcChannel(properties, null, serverFormatter);
</pre><br />3、把callback放在MarshalByRefObject之中。并且让客户端注册自己的channel。这样服务器就会真的callback了，callback代码也是执行在客户端中。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/131531#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, 12 Oct 2007 20:42:41 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/131531</link>
        <guid>http://taowen.javaeye.com/blog/131531</guid>
      </item>
      <item>
        <title>WPF/WCF/Outlook/Addin-Express的Bug</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/128761" style="color:red;">http://taowen.javaeye.com/blog/128761</a>&nbsp;
          发表时间: 2007年10月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>WPF</strong><br /><ul><li> Drag drop not working when used in outlook</li><li> Topmost is not "topmost" when change screen resolution</li><li> Context menu overlapped by transparent window sometime</li><li> Click through, underlying window can get the mouse click event [*] even it do not have mouse capture</li><li> focus could remain in collapsed visual element</li></ul><br /><br /><strong>WCF</strong><br /><ul><li> Serialize IList when used in outlook 2007 will cause .NET runtime fatal execution error</li></ul><br /><br /><strong>Addin-Express</strong><br /><ul><li> commandbar buttons disapper sometime when using word as email editor</li><li> outlook popped up every time when we change html body (word as email editor)</li></ul><br /><br /><strong>Outlook</strong><br /><ul><li> Send mail to multiple recipients using word as email editor </li><li> Drag drop object from wpf application to the email composer "to" field will throw COMException</li></ul>
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/128761#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, 02 Oct 2007 10:48:12 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/128761</link>
        <guid>http://taowen.javaeye.com/blog/128761</guid>
      </item>
      <item>
        <title>写C++代码时头脑中要有触发器</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/128616" style="color:red;">http://taowen.javaeye.com/blog/128616</a>&nbsp;
          发表时间: 2007年10月01日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          新建class时：<br />考虑对象的拷贝语义（copyable？）<br /><br />新建member function时：<br />如果不改变对象状态，在声明后面加const（和ruby的!开头的方法一样）<br /><br />新建function parameter时：<br />如果是传值的，而且是复杂对象，尽量使用const ValueType &<br />尽量把值放在shared_ptr中，然后把只传递指针（和C#与JAVA一样）<br /><br />新建virtual member function时：<br />是不是要把destructor也声明成virtual的？<br />是不是要声明成pure virtual的<br /><br />实例化对象时：<br />如果能实例化在栈上最好<br />不能，把指针放入shared_ptr中
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/128616#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, 01 Oct 2007 15:32:29 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/128616</link>
        <guid>http://taowen.javaeye.com/blog/128616</guid>
      </item>
      <item>
        <title>企业应用开发者使用WPF的三个理由</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/80428" style="color:red;">http://taowen.javaeye.com/blog/80428</a>&nbsp;
          发表时间: 2007年05月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <strong>让控件更灵活的Data Template</strong><br /><br /><pre name="code" class="c#">
            GridViewColumn gvcName = new GridViewColumn();
            gvcName.Header = "Name";
            DataTemplate nameTemplate = new DataTemplate();
            FrameworkElementFactory nameFactory = new FrameworkElementFactory(typeof(ContactPropertyLabel));
            Binding binding = new Binding("Name");
            binding.Mode = BindingMode.TwoWay;
            nameFactory.SetBinding(ContentProperty, binding);
            nameTemplate.VisualTree = nameFactory;
            gvcName.CellTemplate = nameTemplate;
            gvContactList.Columns.Add(gvcName);
</pre><br /><br />这段代码可以给一个ListView的一列做双向绑定，并且指定这一行的渲染控件。<br /><br /><strong>比Windows Forms更强的Binding</strong><br /><br /><pre name="code" class="c#">
public class Contact : DependencyObject
    {
public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof (string), typeof (Contact));

        public string Name
        {
            get { return (string) GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }
}
</pre><br />DependencyObject + DependencyProperty使得属性设置可以自动触发ValueChanged事件，从而让Binding进行更新。<br /><br /><strong>让排版更灵活的各种Layout控件</strong><br /><br />对于普通界面的排版，用Grid+Border<br />对于要动态添加删除的界面排版，在需要动态增删的位置使用StackPanel
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/80428#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, 16 May 2007 23:02:00 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/80428</link>
        <guid>http://taowen.javaeye.com/blog/80428</guid>
      </item>
      <item>
        <title>用UIAutomation做验收测试</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/80420" style="color:red;">http://taowen.javaeye.com/blog/80420</a>&nbsp;
          发表时间: 2007年05月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          这是被测的应用程序：<br /><br /><img src="http://taowen.javaeye.com/upload/picture/pic/3365/b3b4e70f-3a2d-45fb-9598-2200c704077d.png" /><br /><img src="http://taowen.javaeye.com/upload/picture/pic/3366/c6038ae3-566b-4f0f-a7a4-4e96c192012b.png" /><br /><br />应用.NET 3.0提供的UIAutomation，我们可以用以下步骤来进行测试：<br /><br />1. 启动应用程序<br /><br /><pre name="code" class="c#">
string path = @"The Path To The Application";
Process process = Process.Start(path);
</pre><br /><br />2. 获得主窗口对应的AutomationElement<br /><br /><pre name="code" class="c#">
Thread.Sleep(1000);
AutomationElement aeMainWindow = AutomationElement.FromHandle(process.MainWindowHandle);
</pre><br /><br />3. 获得按钮对应的AutomationElement<br /><br /><pre name="code" class="c#">
AutomationElement aeHelloButton = aeMainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));
</pre><br /><br />4. 点击按钮<br /><br />此处的GetCurrentPattern相当于Query Interface，Pattern也是UIAutomation的精髓所在。给不同的UI框架做不同的adapter，实现各种各样的pattern，也就是建立了一个概念上的大一统UI。<br /><br /><pre name="code" class="c#">
InvokePattern ipHelloButton = (InvokePattern) aeHelloButton.GetCurrentPattern(InvokePattern.Pattern);
ipHelloButton.Invoke();
</pre><br /><br />5. 获得文本框对应的AutomationElement<br /><br /><pre name="code" class="c#">
AutomationElement aeHelloTextBox = aeMainWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
</pre><br /><br />6. 取得文本框中的文本<br /><br /><pre name="code" class="c#">
TextPattern tpHelloTextBox = (TextPattern) aeHelloTextBox.GetCurrentPattern(TextPattern.Pattern);
string text = tpHelloTextBox.DocumentRange.GetText(-1);
</pre><br /><br />7. 监听窗口被关闭事件<br /><br /><pre name="code" class="c#">
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, aeMainWindow, TreeScope.Element, HandleMainWindowClose);
private static void HandleMainWindowClose(object sender, AutomationEventArgs e)
{
  Console.WriteLine("Main Window Closed");
}
</pre><br /><br />8. 关闭窗口<br /><br /><pre name="code" class="c#">
process.Kill();
</pre><br /><br />此时控制台上就会打印出"Main Window Closed"<br /><br />整个过程演示了三个主要功能：<br />1、如何主动去操纵界面（点击按钮）<br />2、如何取得界面的状态（获得文本）<br />3、如何监听界面的事件（关闭窗口事件）<br /><br />结论：<br />UIAutomation可以给Win32, WindowsForms， WPF编写的应用程序撰写验收测试。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/80420#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, 16 May 2007 22:19:52 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/80420</link>
        <guid>http://taowen.javaeye.com/blog/80420</guid>
      </item>
      <item>
        <title>mock框架搞什么搞？</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/78824" style="color:red;">http://taowen.javaeye.com/blog/78824</a>&nbsp;
          发表时间: 2007年05月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天早上一时兴起，去网上下载下来JMock，EasyMock最新版本来玩玩。用来测试的行为很简单。就是有一个窗体，上面有一个文本框，一个按钮。如果点击按钮，就会把文本框的内容设置为“Hello”。应用MVP模式，这个行为应该在Presenter中，而View接口应该是这样的：<br /><br /><pre name="code" class="java">
public interface View {
	public void setText(String text);
	public void addActionListener(ActionListener actionListener);
}
</pre><br /><br />我这里就不TDD了，直接给出Presenter的实现，然后我们来看JMock和EasyMock如何处理这个问题。<br /><br /><pre name="code" class="java">
public class Presenter {
	public Presenter(final View view) {
		view.addActionListener(new ActionListener() {
			public void actionPerformed() {
				view.setText("Hello");
			}
		});
	}
}
</pre><br /><br />好的，我先来写一段伪测试代码：<br /><br /><div class="quote_title">引用</div><div class="quote_div"><br />创建MockView<br />创建Presenter，传以MockView做为参数<br />在MockView上触发事件<br />验证MockView的SetText方法被调用了<br /></div><br /><br />然后就用JMock来实现这段伪代码。<br /><br /><pre name="code" class="java">
@Test
public void test_click_button_should_set_text_hello() {
		Mockery mockery = new Mockery();
		final View mockView = mockery.mock(View.class);
		final ActionListenerMatcher actionListenerMatcher = new ActionListenerMatcher();
		mockery.checking(new Expectations() {
			{
				one(mockView).addActionListener(
						with(actionListenerMatcher));
				one(mockView).setText("Hello");
			}
		});
		new Presenter(mockView);
		actionListenerMatcher.fireActionPerformed();
		mockery.assertIsSatisfied();
	}
</pre><br /><br />由于JMock没有对触发事件提供直接的支持，所以是自己写了一个ActionListenerMatcher来达到这个目的的。这个Matcher的实现如下：<br /><br /><pre name="code" class="java">
public class ActionListenerMatcher extends BaseMatcher&lt;ActionListener> {

	private ActionListener actionListener;

	public boolean matches(Object item) {
		actionListener = (ActionListener) item;
		return true;
	}

	public void fireActionPerformed() {
		actionListener.actionPerformed();
	}

	public void describeTo(Description description) {
	}

}
</pre><br /><br />可以看到之所以要这个Matcher是因为，我们需要有一个地方来保存Presenter传进来的listener，并且提供一个用来触发事件的方法。<br /><br />EasyMock的实现与之非常类似，此处不再赘述。代码可以参见附件。<br /><br />我们可以看到什么？我看到的是，测试时候的Intention（意图）完全掩盖在冗长复杂的代码之中了。如果不用Mock该怎么做？很简单：<br /><br /><pre name="code" class="java">
@Test
public void test_click_button_should_set_text_hello() {
		MockView mockView = new MockView();
		new Presenter(mockView);
		mockView.fireActionPerformed();
		Assert.assertEquals("Hello", mockView.getText());
	}
</pre><br /><br />这段代码是不是和上面的伪代码一模一样？MockView是这样的：<br /><br /><pre name="code" class="java">
private class MockView implements View {
		private ActionListener actionListener;
		private String text;
		public void addActionListener(ActionListener actionListener) {
			this.actionListener = actionListener;
		}
		public void setText(String text) {
			this.text = text;
		}
		public String getText() {
			return text;
		}
		public void fireActionPerformed() {
			actionListener.actionPerformed();
		}
	}
</pre><br /><br />做完这个实验，我不得不说。Mock框架，搞什么搞！简单一点不好么？
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/78824#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, 11 May 2007 11:11:32 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/78824</link>
        <guid>http://taowen.javaeye.com/blog/78824</guid>
      </item>
      <item>
        <title>持久化框架烽烟再起</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/78643" style="color:red;">http://taowen.javaeye.com/blog/78643</a>&nbsp;
          发表时间: 2007年05月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          ADO.NET Entity Framework<br />Linq<br />Active Record<br />Hibernate<br /><br />从传统到新贵，从框架到语言。RoR 和 C#3.0 以及传统的 Hibernate。持久化框架烽烟再起。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/78643#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, 10 May 2007 21:44:51 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/78643</link>
        <guid>http://taowen.javaeye.com/blog/78643</guid>
      </item>
      <item>
        <title>主动重构 =&gt; 被动重构</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/78579" style="color:red;">http://taowen.javaeye.com/blog/78579</a>&nbsp;
          发表时间: 2007年05月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: 18pt">引言</span><br /><br />最近杂七杂八地思考了不少东西。但是很惊异地发现这三三两两的杂思竟然彼此互相联系。隐隐地道出了一个共同的主题。这个主题就是重构。修饰词是被动的。<br /><br />主动重构就是我们所熟悉的重构。比如说在Eclipse中按下Shift+Alt+R，出现一个对话框提示你输入新的名字，然后Eclipse把要改名的类或者方法本身改名，以及所有对这个名字的引用都改成新的。那么什么是被动重构呢？<br /><br />所谓被动重构就是你不直接告诉你所使用的工具，你要做重构。相反，是由工具来检测到你做了什么改动。其余的都基本一样。比如就上面这个例子，如果用被动重构来做的话，就是在改名之前check一次，然后你来改名字，改完之后再check一次。分析两次check的结果，就能够知道你改动了什么，然后再由工具把这个改动给传播到其余的部分。<br /><br />被动？这不是更麻烦了么？非也。且看我举三个例子罢。<br /><br /><span style="font-size: 18pt">例子一：PowerPoint Prototype 重构</span><br /><br />（待续）<br /><br /><span style="font-size: 18pt">例子二：类定义带动数据库表结构重构</span><br /><br />（待续）<br /><br /><span style="font-size: 18pt">例子三：测试脚本的重构</span><br /><br />（待续）<br /><br /><span style="font-size: 18pt">推论</span><br /><br />（待续）
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/78579#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, 10 May 2007 16:34:13 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/78579</link>
        <guid>http://taowen.javaeye.com/blog/78579</guid>
      </item>
      <item>
        <title>我的酒窝.NET</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/76356" style="color:red;">http://taowen.javaeye.com/blog/76356</a>&nbsp;
          发表时间: 2007年04月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          ajoo同学的<a href="http://www.javaeye.com/topic/38299" target="_blank">酒窝</a>有.NET版本啦！<br /><br />项目主页：<br /><a href="http://dotnet.dimple.googlepages.com/home" target="_blank">http://dotnet.dimple.googlepages.com/home</a><br /><br />存在意义：<br />快速制作测试用的stub。手工继承，mock框架之外的第三种选择。<br /><br />简单使用：<br /><pre name="code" class="c#">
public class StubDbCommand
        {
            public object ExecuteScalar()
            {
                return "Hello";
            }
            public static DbCommand New()
            {
                return NDimple.Implement&lt;DbCommand>(new StubDbCommand());
            }
        }
Console.WriteLine(StubDbCommand.New().ExecuteScalar());
</pre><br /><br />Output:<br /><div class="quote_title">引用</div><div class="quote_div"><br />Hello <br /></div><br /><br />独家特性：<br /><pre name="code" class="c#">
public abstract class AbstractClass
        {
            protected abstract string AbstractMethod1();
            protected abstract string AbstractMethod2();
            public string InvokeAbstractMethod1()
            {
                return AbstractMethod1();
            }
        }

public abstract class StubAbstractClass : AbstractClass
        {
            protected override string AbstractMethod1()
            {
                return "Hello";
            }
            public static AbstractClass New()
            {
                return NDimple.Implement&lt;AbstractClass>(typeof (StubAbstractClass));
            }
        }

Console.WriteLine(StubAbstractClass.New().InvokeAbstractMethod1());
</pre><br /><br />Output:<br /><div class="quote_title">引用</div><div class="quote_div"><br />Hello<br /></div>
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/76356#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, 30 Apr 2007 16:59:24 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/76356</link>
        <guid>http://taowen.javaeye.com/blog/76356</guid>
      </item>
      <item>
        <title>Naive Container 发布1.0版本</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/76173" style="color:red;">http://taowen.javaeye.com/blog/76173</a>&nbsp;
          发表时间: 2007年04月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          二进制文件和源代码可以从这里下载到：<br /><a href="http://naive.container.googlepages.com/home" target="_blank">http://naive.container.googlepages.com/home</a><br /><br />存在的意义：<br />最简单原始的组件装配<br /><br />使用：<br /><pre name="code" class="c#">
public class Susan : ContainerBound
    {
        public void FallInLove()
        {
            Console.WriteLine("Susan has fallen in love with " + Get&lt;Boy>().Name);
        }
    }
</pre><br /><br /><pre name="code" class="c#">
public class Lily : ContainerBound
    {
        public void Kiss()
        {
            Console.WriteLine("Lily is kissing {0}", Get&lt;Boy>().Name);
        }
    }
</pre><br /><br /><pre name="code" class="c#">
public class Lucy : ContainerBound
    {
        public void Marry()
        {
            Console.WriteLine("Lucy is marrying " + Get&lt;Boy>().Name);
        }
    }
</pre><br /><br />配置：<br /><pre name="code" class="c#">
Containers.GetContainerInContext&lt;object>().Put(new GenericBoy("Van"));

Containers.GetContainerInContext&lt;Lucy>().Put(new GenericBoy("Tom"));

Containers.GetContainerInContext&lt;Lily>().Put(new GenericBoy("Joy"));

Containers.Close();
</pre><br /><br />更多请参见项目主页
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/76173#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, 29 Apr 2007 17:50:10 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/76173</link>
        <guid>http://taowen.javaeye.com/blog/76173</guid>
      </item>
      <item>
        <title>在C# 2.0上写另外一种语言来创建对象</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/75565" style="color:red;">http://taowen.javaeye.com/blog/75565</a>&nbsp;
          发表时间: 2007年04月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          创建对象有很多种方式，可以用工厂，可以用容器装配。所以不在乎再多一种啦。。。在C#上写了一种古怪的创建对象的方式，给大家kk。<br /><br />首先，这是配置代码。意思是说在Lily的宇宙中（每个人都有一个小宇宙，圣斗士。。。），她的Boy是tom，也就是我的眼中只有你。相应的在Lucy的眼中只有joy。<br /><pre name="code" class="c#">
Tom tom = Void.GiveMe&lt;Tom>();
Joy joy = Void.GiveMe&lt;Joy>();
Void.UniverseOf&lt;Lily>().Exist(tom);
Void.UniverseOf&lt;Lucy>().Exist(joy);
</pre><br /><br />然后是创建两个女孩子的代码：<br /><pre name="code" class="c#">
Lily lily = Void.GiveMe&lt;Lily>();
lily.Kiss();
Lucy lucy = Void.GiveMe&lt;Lucy>();
lucy.Kiss();
</pre><br /><br />运行这段代码就可以看到少儿不宜的场景了。。。：<br /><div class="quote_title">引用</div><div class="quote_div"><br />Lily is kissing Tom<br />Lucy is kissing Joy<br /></div><br /><br />产生这样的结果的原因是：<br /><pre name="code" class="c#">
public abstract class Girl : God
{
    public abstract string Name { get; }
    public void Kiss()
    {
        Boy boy = GiveMe&lt;Boy>();
        Console.WriteLine("{0} is kissing {1}", Name, boy.Name);
    }
}
</pre><br /><br />Girl的男朋友从哪里来的？偷来的？抢来的？骗来的？。。。<br />GiveMe是哪里来的方法？它是上帝的安排啊：<br /><br /><pre name="code" class="c#">
public abstract class God
{
    protected abstract T GiveMe&lt;T>();
}
</pre><br /><br />旁白：其实本来没有神，每个人都是神。。。<br />上帝怎么实现GiveMe的呢？这就是Void关心的事情了。因为女孩子们是这么创建的：<br /><br /><pre name="code" class="c#">
Lily lily = Void.GiveMe&lt;Lily>();
Lucy lucy = Void.GiveMe&lt;Lucy>();
</pre><br /><br />所以说，这根本就是一个圈。。。因为我要做的其实是完全废弃标准的new和构造函数，采用自己的方式来创建和初始化对象，所以我不把这个叫做某某框架，独立使用也没有价值，它其实是一种语言。这种语言在后面我会用来干一些fancy的事情，但是还没想好怎么弄。。。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/75565#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, 27 Apr 2007 18:19:36 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/75565</link>
        <guid>http://taowen.javaeye.com/blog/75565</guid>
      </item>
      <item>
        <title>当Remoting遇见COM</title>
        <author>taowen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://taowen.javaeye.com">taowen</a>&nbsp;
          链接：<a href="http://taowen.javaeye.com/blog/60612" style="color:red;">http://taowen.javaeye.com/blog/60612</a>&nbsp;
          发表时间: 2007年03月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          谁动了我的assembly？<br />你有没有曾经把.NET的用对象变成过COM对象？你有没有用过.NET Remoting？那你有没有两者同时用呢？如果你有，可能你也遇过同一个问题。.NET抱怨说，找不到要序列化的Assembly。因为COM对象被执行的时候，其所处的位置是宿主的位置，而不是Assembly所在的位置了。由于Binary Formatter查询Assembly的一个小问题，导致了这个错误信息。在MSDN论坛上抄来如下代码，解决了问题：<br /><pre name="code" class="c#">
        private static Assembly CurrentDomain_AssemblyResolve(object sender, 
 ResolveEventArgs args)
        {
            Assembly ayResult = null;
            string sShortAssemblyName = args.Name.Split(',')[0];
            Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly ayAssembly in ayAssemblies)
            {
                if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0])
                {
                    ayResult = ayAssembly;
                    break;
                }
            }
            return ayResult;
        }
</pre><br />把这段代码注册到Current Domain的AssemblyResolve事件上。注册的最佳时机是静态构造函数：<br /><pre name="code" class="c#">
static YourClass()
{
  AppDomain.CurrentDomain.AssemblyResolve += 
new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
</pre><br />希望上天保佑我们的奶酪不会再被乱动了。
          <br/>
          <span style="color:red;">
            <a href="http://taowen.javaeye.com/blog/60612#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, 22 Mar 2007 09:52:00 +0800</pubDate>
        <link>http://taowen.javaeye.com/blog/60612</link>
        <guid>http://taowen.javaeye.com/blog/60612</guid>
      </item>
  </channel>
</rss>