随笔 - 41  文章 - 15 评论 - 1115 trackbacks - 21

.net asp web c# vb VS2005 VS2008 VS2003

    姓名 景春雷
    网名 1-2-3
    生日 1980.2.29
    城市 沈阳
又看了一遍大话西游,仍然十分感动。 9-26 10:30

与我联系

搜索

 

常用链接

我参与的团队

我的标签

随笔分类(42)

随笔档案(42)

文章分类(14)

相册

收藏夹(2)

积分与排名

  • 积分 - 143160
  • 排名 - 267

最新评论

阅读排行榜

评论排行榜

      你知道的,我儿啊,为父只有一句话要对你说,那是为父这一生来奉行的格言:
      “勿以恶小而不为,勿以善小而为之。”
      切记切记啊! 
                                                             ——Icecream 风聆
                                                           《魔王爸爸的16封信》

摘要

阅读本文并探索
    - 为什么Lazy Initialization只适用于ActiveRecord模式。
    - 芝麻饼公司的Boss是否应该批准降低成本的议案。
    - 为什么DomainObject会遭遇“巧妇难为无米之炊”的尴尬?
    - 如何用依赖倒置原则解除DomainObject的尴尬处境。
    - 如何使用泛型接口简化Value Holder(这个可是书上没有的哦)。
本文将探讨在分层模式下实现Lazy Load所遭遇的困难与迷思,并重点探索模式背后隐藏的思想和设计原则。文章的最后将对书上给出的三种Lazy Load作一个简单的分析和比较。

Lazy Load简介

对于Lazy Load,书上有一个相当精辟的定义。

An object that doesn't contain all of the data you need but knows how to get it.
一个对象,虽然它尚未包含所有你需要的数据,但是知道如何去获取这些数据。


对于把数据从数据库装载到内存中的工作,一种方便的设计是,在你装载了想要的对象的同时,将相关联的对象也一并装载进来。这使得使用这些对象的其它开发者的工作变得更容易——否则他们不得不自己显式地装载他们需要的那些对象。

可是,如果你对此作一个逻辑上的推论,就会发现,装载一个对象的效果往往是同时装载了大量的相关联的对象——而当你实际上仅需要其中的几个对象时,这样作有时就会影响性能。

Lazy Load暂时中断装载其它关联对象的工作,只在对象中放置一个记号,这样就可只在需要使用关联对象的时候才装载它们。就像许多人所知道的,如果你因为懒惰而没有干某件工作,那么当这件工作变成了其它人的职责而你完全不需要再去作的时候,你就胜利了。(以上三段为原文翻译)

需要注意的是Lazy Load往往不能提高性能反而会降低性能(感谢怪怪)。这是因为所谓的ripple loading(为得到100条数据而执行100个select语句而不是执行一个select语句得到这100条数据)对性能伤害极大,以至于Fowler建议一开始的时候不要用Lazy Load,只有当系统确实因为装载了过多无用的数据而导致性能很差时,才有的放矢地使用Lazy Load提高性能。这也给我们的设计提出了一个要求:可以以很小的代价在正常装载数据和Lazy Load之间切换。另,由于使用Lazy Load的时机并不是本文的重点,这里就不详谈了。

书中共给出了四种实现Lazy Load的方法,分别是lazy initialization, virtual proxy, value holder, 以及 gost。
让我们先来看看lazy initialization。

注:“书”指的是Martin Fowler写的《企业应用架构模式》一书。

Lazy Initialization

这是最简单的实现Lazy Load的方法。
lazy initialization的核心代码像这样:

class Department...
  public Person getManager() {
    if(manager == null) {
      manager = new PersonMapper().findById(managerId);
    }
    return manager;
  }
}

注:本文将沿用上篇给出的数据库结构和数据。另外,本文与上篇联系十分紧密,强烈建议先看上篇,再看本篇。

为什么说lazy initialization只适用于ActiveRecord模式

这是因为Data Mapper模式使用了分层设计,DataSource层是DomainObject层的上层,DomainObject是不允许知道Mappers的(关于这一点在上篇中有详细论述),而上面的那个代码中使用了PersonMapper,这个是绝对不允许的。


教条主义?

的确,我们写代码是为了完成工作,而不是为了遵守什么活见鬼的设计原则!设计原则往往只是提供一个 guideline ——大方向不能搞错,但是可以根据需要灵活处理。不过这一次不同,因为下层不能访问上层的原则是分层设计中最重要的一个而且是唯一一个绝对不能违反的原则。因为层之间的双向依赖对设计的伤害极大:
    - 它使各层的职责不再清晰。
    - 它令各层不再能够被独立地修改。
    - 它使可读性变差。你将无法把层作为一个有清晰边界的系统来单独阅读,你的注意力必须在各层之间漂移。
    - 它令单元测试更加困难。
总之,分层带来的全部好处都会被它消磨殆尽。

但是lazy initialization的简单性太诱惑了,特别是当你被其它三种lazy load搞得晕头转向的时候。我们不禁要问:可不可以只在实现Lazy Load的时候小小地违反一下原则呢?在回答这个问题之前,不妨先来听个小故事。

芝麻饼公司的故事

芝麻胡同®是国际知名的芝麻饼生产商。在一次经营会议上,该公司的总经理提出了一个降低成本的议案。
“根据公司现在的生产规程,每张芝麻饼上应放置100粒芝麻。如果我们将这一数量降低到97粒,顾客是察觉不到的——这是我们经过长期的科学实验发现的。如果从下个月开始实施,那么,年终就可节约成本3000万美元,而利润亦会有相应的提高。”
总经理指着幻灯片上那动人心魄的鲜红的向上箭头,眼中似乎也放出了异样的光彩。
如果你是董事长,是否应该同意这一议案呢?
粗看起来,这个议案似乎不错——即降低了成本,又没有损害产品质量(虽然实际上是损害了,但是顾客察觉不到可以约等于没有损害^_^)。但是,问题在于,没有人知道芝麻数量的底线是多少。没准过了一段时间,又会有个什么销售经理提出一个“有效降低成本议案”:
“根据公司现在的生产规程,每张芝麻饼上应放置97粒芝麻。如果我们将这一数量降低到90粒,顾客是察觉不到的——这是我们经过长期的科学实验发现的。如果从下个月开始实施,那么,年终就可节约成本7000万美元......”
最后,芝麻饼恐怕就要变成烧饼了。

注:这个小故事改编自温伯格的《咨询的奥秘》。

是芝麻饼还是烧饼

同样,我们也不知道违反分层设计原则的底线到底是什么——既然实现Lazy Load可以违反原则,那么实现UnitOfWork的时候是不是也可以违反呢?最后依赖将失去控制,分层也名存实亡了。所以,我们决定要在不违反分层设计原则的前提下实现Lazy Load

巧妇难为无米之炊

再来看一下Lazy Load的定义:一个对象,虽然它尚未包含所有你需要的数据,但是知道如何去获取这些数据。很明显,要实现Lazy Load,必须让domain object知道如何去获取数据,但是根据分层设计原则,domain object又不允许知道mappers,天哪,这岂不是正应了那句巧妇难为无米之炊

抽象,我们的老朋友

解决这个问题的方法是:从mappers里把domain object需要的功能服务抽象出来,成为一个窄接口,然后让domain object依赖这个窄接口。具体点说,Department类需要使用PersonMapper类的findById()函数,但是我们又不想让Department类知道PersonMapper类,这时就可把PersonMapper类的“根据id从数据库中装载并创建一个Person对象”的功能抽象出来,成为一个接口:

public interface IValueLoader
{
  object load(long id);
}

然后Department类就可以只依赖这个接口,而不需要依赖PersonMapper了。


下面这张图包含了全部的源代码:


这样domain object既没有违反原则去依赖上层,又得到了想要的功能服务。虽然增加了一个接口之后程序变得稍稍有些复杂,但是对于一个复杂的大型系统来说,用增加一点复杂性来换取对依赖的可控性,还是很值得的。这个招数被Martin Fowler命名为Separated Interface(476)模式。

Separated Interface Pattern  (by 1-2-3)
在使用了分层设计的系统中,下层不应使用上层提供的功能服务。当下层必须使用上层提供的功能服务时,可以将这些功能服务抽象出来成为一个窄接口,然后让下层依赖这个窄接口。Separated Interface模式将上层和下层之间的双向依赖转变成上下两层对一个窄接口的单向依赖。

在Separated Interface模式背后,隐藏着一个十分重要的设计原则,它非常著名,却因高深莫测的定义而难以被初学者理解,它就是依赖倒置原则

依赖倒置原则

Dependency Inversion Principle
Depend upon abstractions. Do not depend upon concret classes.
依赖倒置原则
要依赖抽象。不要依赖实现类。

这听起来和“要针对接口编程,不要针对实现编程”似乎差不多。的确,两个设计原则都告诉我们应该依赖抽象——这个OO与生俱来的能力与万变不离其宗的原则。不过仅仅说“要依赖抽象”也太过抽象了,感觉与老子的那句“玄之又玄,众眇之门”有得一拼。其实,依赖倒置原则的特别之处并不在原则本身,而在于它的效果——将双向依赖转变成单向依赖
另外,在这里我不想解释“倒置”或“反转”的意思,因为我发现这些词除了时髦好听、令人印象深刻外,对问题的理解并无太大帮助,反而容易分散人们的注意力。

模板方法模式和工厂方法模式也是依赖倒置原则的应用

我们都知道,继承关系本身就是一种由子类到超类的很强的依赖关系。在下图中,我们通过显式地画出这个依赖关系来强化这一概念。


在下图中,因为DomainObject的whoAmI()函数引用了Person类和Department类,又增加了DomainObject对Person类和Department类的依赖。


现在,依赖倒置原则就可以派上用场了。
首先,要搞明白谁在上层谁在下层。子类Person和Department由于继承了DomainObject,所以可以随意使用DomainObject中定义的函数,这是与生俱来的依赖关系,我们无法消除,所以Person和Department是DomainObject的上层,DomainObject处于下层。那么,下层类DomainOjbect对上层类的依赖就是不好的东西了,我们要消除它。
下一个问题就是,下层类使用了上层类那些功能服务?答案是“判断对象是Person还是Department”。
接着,我们就可以把这个功能服务抽象出来,成为DomainObject的抽象函数getType(),DomainObject直接依赖这个抽象函数;而Person和Department则要实现这个抽象函数。


由此可见,所谓“抽象出来的窄接口”不一定非得是interface,也可以是抽象函数,还可以是delegate(书上的Ghost模式就是一个很好的例子)。

重构,提炼类

回到Department的定义中,可以看到一处潜在的重复代码(下面涂黄颜色的部分)。

public class Department...
{
  public Person getManager() {
    if (_manager == null) {
      _manager = _loader.load(managerId) as Person;
    }
    return _manager;
  }
}

以后只要每添加一个Lazy Load,就必须重复一遍这个代码,所以应该把它提炼出来。在作了一个Extract Class重构之后,这段代码被提炼到一个新的类ValueHolder中(被修改的部分涂了蓝色)。


使用泛型

现在我们的Lazy Load解决方案已经和书上的Value Holder模式基本差不多了,但是仍有一处坏味道如芒刺在背,让人浑身不自在,这就是Person#getManager()函数中的向下转型(downcasting)操作(下面代码中涂了黄颜色的部分就是向下转型操作)。

public class Department...
{
  public Person getManager()
  {
    return _manager.getValue(managerId) as Person;
  }
}

向下转型三宗罪  (by 1-2-3)
向下转型,其罪有三:
    一)破坏了抽象。接口就像魔术师手中的那块黑布,将实现细节隐藏在下边;而向下转型就像一个顽皮的孩子,非要掀开这块布看个究竟不可。
    二)必须知道额外的信息。例如getValue()的返回值类型是Object,那么调用这个函数的人怎么能知道应该把这个Object转型成Person还是转型成Women?抑或必须转型成List?
    三)必须等到运行期才能知道转型是否成功。你可能说我只要每次写了向下转型的代码之后立即就运行程序测试一下就可以了。我相信你足够勤奋,而且你的所有同事也都和你一样勤奋。但是,如果有一天写getValue()函数的那个人修改了函数真正返回的对象的类型呢?他未必会逐个通知所有调用了getValue()的人吧,再说他也未必就知道到底有多少人调用了这个函数。

在 .net framework2.0 中新增的泛型功能可以很好地解决这个问题,而且使用起来也很简单,只要把IValueLoader和ValueHolder中的“object”换成“T”就可以了。下面列出源代码,修改的部分涂了黄颜色。

public interface IValueLoader<T>
{
  T load(long id);
}

public class ValueHolder<T>
{
  private IValueLoader<T> _loader;
  private T _value;

  public ValueHolder(IValueLoader<T> loader)
  {
    _loader = loader;
  }

  public T getValue(long id)
  {
    if (_value == null)
    {
      _value = _loader.load(id);
    }
    return _value;
  }
}

而Department和PersonMapper也要做些修改。

public class Department...
{
  private ValueHolder<Person> _manager;
  public void setManager(ValueHolder<Person> arg)
  {
    _manager = arg;
  }
  public Person getManager()
  {
    return _manager.getValue(managerId);
  }
}

public class PersonMapper : ..., IValueLoader<Person>
{
  public Person load(long id)
  {
    return findById(id);
  }
}

一对多的情况

例如,想统计每个部门的员工数量,需要为Department增加如下代码。

public Class Department...
{
  private ValueHolder<IList<Person>> _employees;
  public void setEmployees(ValueHolder<IList<Person>> arg)
  {
    _employees = arg;
  }
  public IList<Person> getEmployees()
  {
    return _employees.getValue(id);
  }
  public int employeeCount
  {
    get { return getEmployees().Count; }
  }
}

修改DepartmentMapper。

public class DepartmentMapper...
{
  public DomainObject findById(long id)
  {
    Department result = load from db by id;
    result.setManager(new ValueHolder<Person>(PersonMapper()));
    result.setEmployees(new ValueHolder<IList<Person>>(new PersonMapper()));
    return result;
  }
}

让 PersonMapper 实现 IValueLoader<IList<Person>> 接口。

public class PersonMapper : ..., IValueLoader<Person>, IValueLoader<IList<Person>>
{
  public IValueLoader<IList<Person>> load(long id)
  {
    return findByDepartmentId(id);
  }
}

这样就搞定了。

说再见之前

这篇有些长,所以首先感谢您耐心的读到这里。上面那个例子与书上的Value Holder模式十分接近,只不过加上了泛型而已。在附录中,我对书上的三种Lazy Load模式作了一个非常简单的分析和比较,并为每种模式绘制了一幅UML类图,希望能对您有所助益。

附录 书上给出的三种Lazy Load浅析

Virtual Proxy

优点
    - 对domain object没有侵入性,透明度为100%。
    - 在普通装载和Lazy Load之间切换只需要修改Mapper中的一行代码。
缺点:
    - 在静态类型语言里,实现一个domain object的virtual proxy非常麻烦,所以此方法最好只用于需要返回domain object的集合的情况。
UML图:



Value Holder

优点:
    - 既可以返回单个domain object,也可以返回domain object的集合。
缺点:
    - 对domain object有侵入性,当需要由普通装载转为Lazy Load时,必须修改domain object。
    - 需要对ValueHolder返回的结果作向下转型操作。
UML图:



Ghost

优点:
    - 既可以返回单个domain object,也可以返回domain object的集合。
    - 当由普通装载转为Lazy Load时对DataSource层的改动非常小。但是前提是在项目一开始的时候就要预料到将来使用Ghost的可能性非常大而在一开始的时候就把DataSource层设计成支持Ghost的形式。
    - 当由普通装载转为Lazy Load时对Domain Object层的改动非常小,前提是你的项目愿意并且能够支持AOP。
    - 这是最Lazy的Lazy Load,你可以在一开始的时候让所有的domain object都是ghost。
缺点:
    - 对Mappers有一定的侵入性。如果你的项目一开始的时候没有考虑要使用Ghost,并且设计得内聚度不是很高的话,可能需要进行一番重构才能比较容易支持Ghost。
    - 在Mappers#AbstractFind()里调用CreateGhost()的确可以很方便地一下子把所有domain object都变成ghost,但是如果我们只想Lazy Load部分domain object,这招就不灵了。
    - 对domain object的侵入性非常大,每个属性都要加一个Load()函数。当然如果你的项目愿意并且能够支持AOP就没什么问题。
UML图:



参考文献

[Fowler POEAA]
Fowler, Patterns of Enterprise Application Architecture. Addison-Wesley, 2003.
影印版:企业应用架构模式(影印版)。中国电力出版社,2004。
[Fowler Refactoring]
Fowler, Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999.
影印版:重构——改善既有代码的设计(影印版)。中国电力出版社,2003。
[Fowler UML]
Fowler et al, UML Distilled: A Brief Guide to the Standard Object Modeling Language(Sencond). Addison-Wesley, 2000.
中文版:徐家福 译,UML 精粹(第2版)标准对象建模语言简明指南。清华大学出版社,2002。
[Fowler IOC]
Fowler, Inversion of Control Containers and the Dependency Injection pattern. Web, 2004.
中文版:透明 译,IoC 容器和Dependency Injection 模式。PDF.
[Fowler RC]
Fowler, Reducing Coupling. PDF, 2001.
[Freeman et al]
Freeman et al, Head First Design Patterns. O’Reilly, 2004.
影印版:深入浅出设计模式(英文影印版)。东南大学出版社,2005。
[王咏武 王咏刚]
王咏武 王咏刚, 道法自然:面向对象实践指南。电子工业出版社,2004。

工具箱

那个太极小图标来自《Head First Design Patterns》,用FireWorks 6.0和GIMP 2.2作了一些处理。UML图使用Visio 2003+Pavel Hruby's UML2.0 模板绘制。图片上使用了手写字体方正静蕾简体。文字部分使用Google 拼音输入法键入。

 

posted on 2007-10-30 09:28 1-2-3 阅读(4504) 评论(49)  编辑 收藏 所属分类: 探索Domain Model系列

FeedBack:
#1楼  2007-10-30 09:57 随风飘散      
好文,犹如瀑布流水。
  回复  引用  查看    
#2楼  2007-10-30 10:04 qiaoqiao [未注册用户]
好长的文章
  回复  引用    
#3楼 [楼主] 2007-10-30 10:18 1-2-3      
@随风飘散
谢谢。
  回复  引用  查看    
#4楼  2007-10-30 11:19 Jeffrey Zhao      
精品……

  回复  引用  查看    
#5楼  2007-10-30 11:23 weef [未注册用户]
图很好看,可惜居然用的google拼音。
  回复  引用    
#6楼 [楼主] 2007-10-30 11:25 1-2-3      
@Jeffrey Zhao
谢谢。俺确实花了不少的心思,不过俺的水平和写作能力都有限,已经是俺的极限了。
  回复  引用  查看    
#7楼 [楼主] 2007-10-30 11:30 1-2-3      
@weef
谢谢。
> 可惜居然用的google拼音。
我喜欢Google的大气和能力。不过和这篇的主题没什么关联,就不多说了。
  回复  引用  查看    
#8楼  2007-10-30 11:40 xiao_p      
顶 出书吧! :-)
  回复  引用  查看    
#9楼  2007-10-30 11:42 Jeffrey Zhao      
@1-2-3
已经非常好了!:)
  回复  引用  查看    
#10楼  2007-10-30 12:53 Justin      
最后这张UML图好可爱!呵呵
  回复  引用  查看    
和数字签名一样,又是一篇好文!

  回复  引用  查看    
#12楼  2007-10-30 12:54 idior      
表现形式非常不错,意思也表达的很清楚了,希望以后的文章更有深度。

  回复  引用  查看    
#13楼 [楼主] 2007-10-30 12:58 1-2-3      
@xiao_p
谢谢。不过俺肚子里就这么点墨水,要出书也只能出一本3页纸的书了^_^
  回复  引用  查看    
#14楼 [楼主] 2007-10-30 13:02 1-2-3      
@Justin
最后这张图是把类图和协作图合二为一了。我还是第一次这么做,感觉虽然乱了些,但是免去了在类图、代码、时序图之间来回跳转的麻烦。
  回复  引用  查看    
#15楼 [楼主] 2007-10-30 13:02 1-2-3      
@学海无涯,回头是岸
谢谢您一直的支持和鼓励。
  回复  引用  查看    
#16楼  2007-10-30 13:03 omnislash      
好文章!
特别钦佩博主的认真态度,看得很舒服,谢谢:)
  回复  引用  查看    
#17楼 [楼主] 2007-10-30 13:03 1-2-3      
@idior
谢谢您的鼓励。
  回复  引用  查看    
#18楼 [楼主] 2007-10-30 13:08 1-2-3      
@omnislash
谢谢。每件事都作最好的自己,这是我的目标。
  回复  引用  查看    
#19楼  2007-10-30 14:02 Klesh Wong      
Seqarated Interface(476)模式。
=>
Separated Interface(476)模式。

  回复  引用  查看    
#20楼 [楼主] 2007-10-30 14:08 1-2-3      
@Klesh Wong
哦,谢谢。
  回复  引用  查看    
#21楼  2007-10-30 14:23 怪怪      
好文, 再支持一次 :)
  回复  引用  查看    
#22楼 [楼主] 2007-10-30 14:30 1-2-3      
@怪怪
谢谢。最近很忙吧?好像很久没发贴了。
  回复  引用  查看    
#23楼  2007-10-30 15:47 踏雪无痕      
花了不少时间
  回复  引用  查看    
#24楼 [楼主] 2007-10-30 15:51 1-2-3      
@踏雪无痕
嗯,写了5天半,不算看书和思考的时间。
  回复  引用  查看    
#25楼  2007-10-30 16:06 deerchao      
又多了一层...
感谢楼主用很清晰的思路把我们带到了怎么看怎么糊涂的境地:我们的代码里很少有Person,大部分都是ValueHolder<Person>...
:)
  回复  引用  查看    
#26楼 [楼主] 2007-10-30 16:12 1-2-3      
@deerchao
> 又多了一层...
只是通用类库,没什么大碍的。

> 代码里很少有Person,大部分都是ValueHolder<Person>...
用了泛型之后确实会有些乱,特别是嵌套的时候。不过很容易习惯的^_^

  回复  引用  查看    
#27楼  2007-10-30 17:17 伍迷      
人才,文章写得很用心,专业且幽默,努力写下去。如果出书,你的书我会买。
  回复  引用  查看    
#28楼  2007-10-30 17:40 Artech      
@1-2-3
确实是精品,LZ很用心,受教了。
  回复  引用  查看    
#29楼  2007-10-30 22:02 怪怪      
@1-2-3
不是忙(来来回回重写一点代码倒是有), 而是puzzled, 我TM突然不会写程序了! 这是有史以来头一遭, 不是说笑话, 是真的感觉什么也写不出来了. 希望这关赶快过去...
  回复  引用  查看    
#30楼  2007-10-31 01:27 昊子      
泛型替代delegate这个用法比较新颖

  回复  引用  查看    
#31楼 [楼主] 2007-10-31 08:58 1-2-3      
@伍迷
谢谢您这么高的评价。俺的水平离出书有些远,不过俺会坚持写博的。
  回复  引用  查看    
#32楼 [楼主] 2007-10-31 08:59 1-2-3      
@Artech
谢谢。
  回复  引用  查看    
#33楼 [楼主] 2007-10-31 09:06 1-2-3      
@怪怪
估计您是到了JeffreyZhao所说的“某天早上一醒来,发现对于最近接触的事物有了新一层的认识,似乎什么都不会了,却好像什么都理解了”的境界了。
  回复  引用  查看    
#34楼 [楼主] 2007-10-31 09:08 1-2-3      
@昊子
> 泛型替代delegate这个用法比较新颖
有时感觉delegate是个不错的东东,特别是VS2008新增了Lambda表达式的功能,用起来就更爽了。
  回复  引用  查看    
#35楼  2007-10-31 10:02 小生      
好文﹐支持~
希望博主能繼續深入﹐再發一篇從aspx到數據庫的全局調用模式。
總覺得這些模式只有結合整個系統探討應該更有深度和指導意義
  回复  引用  查看    
#36楼 [楼主] 2007-10-31 10:32 1-2-3      
@小生
谢谢支持。您的建议俺会好好考虑下的。
  回复  引用  查看    
#37楼  2007-10-31 12:51 craboYang      
绝对好文。 已经有近1个月没看见如此经典了。
  回复  引用  查看    
#38楼 [楼主] 2007-10-31 13:03 1-2-3      
@craboYang
谢谢。
  回复  引用  查看    
#39楼  2007-11-02 03:57 怪怪      
@1-2-3
"某天早上一醒来,发现对于最近接触的事物有了新一层的认识,似乎什么都不会了,却好像什么都理解了"

还真不是, 这种顿悟虽然也经常有, 对我来说, 当时心中窃喜, 一到实践当中, 才发现其实离真正的明白差的还远..., 可能我比较笨吧...
  回复  引用  查看    
#40楼  2007-11-03 00:47 Jeffrey Zhao      
@怪怪
其实我遇到瓶颈很久了,好久没有“顿悟”的感觉了。
  回复  引用  查看    
#41楼 [楼主] 2007-11-05 08:22 1-2-3      
@怪怪
@Jeffrey Zhao
俺倒是很少有顿悟的感觉,就是每次看完一本书,都觉得好像得了个银弹似的,觉得这下可以在实践中好好运用一下,可是到了实践中才发现和自己想像的完全不一样,特别是在做需求这方面,感觉真是好难啊。
  回复  引用  查看    
强人,
和我重名,但姓不同,

呵呵,支持
  回复  引用    
#43楼 [楼主] 2007-11-22 08:07 1-2-3      
@墙外行人
有趣。我有一个小学同学叫赵春雷,是女生。现在有一个同事叫牛春雷,这个最不爽,居然比我牛...
  回复  引用  查看    
#44楼  2007-11-23 16:03 txdlf      
to楼主
----------------------
public DomainObject findById(long id)
{
Department result = load from db by id;
result.setManager(new PersonMapper());
result.setEmployees(new ValueHolder<IList<Person>>(new PersonMapper()));
return result;
}
----------------------
result.setManager(new PersonMapper());我怎么也读不通,是不是应该为result.setManager(new ValueHolder<Person>(new PersonMapper()));

  回复  引用  查看    
#45楼 [楼主] 2007-11-28 10:24 1-2-3      
@txdlf
您说得很对,是我写错啦。难得您看这么仔细,说实话,刚才我看了半个钟头才看明白,呵呵,一个月前写的东东,现在全忘光了,所以突然觉得OO的一大好处是可以不用记忆很多东西^_^
  回复  引用  查看    
#46楼 [楼主] 2007-11-28 10:42 1-2-3      
修订记录:
“一对多的情况”节里面的代码

public class DepartmentMapper...
{
  public DomainObject findById(long id)
  {
    Department result = load from db by id;
    result.setManager( PersonMapper());
    result.setEmployees(new ValueHolder<IList<Person>>(new PersonMapper()));
    return result;
  }
}

改为
public class DepartmentMapper...
{
  public DomainObject findById(long id)
  {
    Department result = load from db by id;
    result.setManager(new ValueHolder<Person> (PersonMapper()));
    result.setEmployees(new ValueHolder<IList<Person>>(new PersonMapper()));
    return result;
  }
}

感谢txdlf的指正!
  回复  引用  查看    
#47楼  2008-01-18 16:37 xhan      
很不错!谢谢楼主!不过我觉得楼主好像把DomainObject和DataMapper的上下弄错了。DomainObject应该是上层,DataMapper是下层。分离接口是为了把DomainObject对DataMapper的强依赖转变成弱的依赖。本来是domainobject依赖datamapper分离出接口就变成两者都依赖与抽象的接口。这是依赖倒置。因为抽象是上层!
  回复  引用  查看    
长见识了,还需要多学习一下阿
  回复  引用    
#49楼  2008-05-27 13:47 Solog      
其实粗略看完了,其实没看懂啥。呵呵。
  回复  引用  查看    

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]