The DoYouHaoBaby PHP Framework中文开发文档手册
Copyright(c) DoYouHaoBaby.NET All Rights Reserved.
[ 工厂 ]

实体和聚合通常会很大很复杂,根实体的构造函数内的创建逻辑也会很复杂。实际上通过构造器努力构建一个复杂的聚合也与领域本
身通常做的事情相冲突,在领域中,某些事物通常是由别的事物创建的。

一个对象的创建可能是它自身的主要操作,但是复杂的组装操作不应该成为被创建对象的职责。组合这样的职责会产生笨拙的设计,
也很难让人理解。

因此,有必要引入一个新的概念,这个概念可以帮助封装复杂的对象创建过程,它就是工厂(Factory)。工厂用来封装对象创建所必
需的知识,它们对创建聚合特别有用。当聚合的根建立时,所有聚合包含的对象将随之建立,所有的不变量得到了强化。
将创建过程原子化非常重要。如果不这样做,创建过程就会存在对某个对象执行了一半操作的机会,将这些对象置于未定义的状态,
对聚合而言更是如此。当根被创建时,所有对象服从的不变量也必须被创建完毕,否则,不变量将不能得到保证。对不变的值对象而
言则意味着所有的对象被初始化成有效的状态。如果一个对象不能被正常创建,将会产生一个异常,确保没有返回一个无效值。

因此,转变创建复杂对象和聚合的实例的职责给一个单独的对象,虽然这个对象本身在领域模型中没有职责,但它仍是领域设计的一
部分。
提供一个封装了所有复杂组装的接口,客户程序将不再需要引用要初始化的对象的具体的类。将整个聚合当作一个单元来创
建,强化它们的不变量。

工厂方法是一个对象的方法,包含并隐藏了必要的创建其他对象的知识。这在一个客户程序试图创建一个属于某聚合的对象时是很有
用的。解决方案是给聚合的根增加一个方法,这个方法非常关心对象的创建,强化所有的不变量,返回对那个对象的引用或者拷贝。



Container 包含着许多组件,这些组件都是特定类型的。当这样的一个组件被创建后能自动归属于一个Container 是很有必要的。客户
程序调用Container 的createComponent(Type t)方法,Container实例化一个新的组件。组件的具体类取决于它的类型。在创建之
后,组件被增加到Container 所包含的组件的集合中,并且返回给客户程序一个拷贝。

当创建一个极其复杂的对象或者创建的对象涉及到创建其他一系列的对象时(例如,创建一个聚合),需要大量的时间。隐藏聚合的
内部构建所需要的任务可以用一个单独的工厂对象来完成。让我们考虑一个程序模块的案例,计算一辆汽车从起点行驶到终点的路
线,而且被给定了很多的约束。用户登录Web 站点后运行应用程序,并指定其中一个要遵守的约束条件:最短的路线,最快的路
线,或者最便宜的路线。被创建的路线可以被登录的用户评论,而且这些信息都需要被保存下来。这样就可以在客户下次登录时检索
出它们。

如图:


路线ID 的生成器被用来给每条路线创建一个唯一的标识符,这对一个实体而言是非常必要的。

当创建一个工厂时,我们被迫违反一个对象的封装原则,而这应该小心行事。每当对象中发生了某种变化时,会对构造规则或者某些
不变量造成影响,我们需要确认工厂也被更新以支持新的条件。工厂和它们要创建的对象是紧密关联的。这可能是个弱点,但它也有
长处。一个聚合包含了一系列密切相关的对象。跟的构建与聚合内的其他对象的创建是相关的。会有一些逻辑放置到聚合中,这些逻
辑不属于任何一个对象,因为它总跟其他对象的构建有关。看上去,使用一个专用的工厂类,给定创建整个聚合的任务比较合适,
聚合中的包含的规则、约束和不变量将被确保在聚合内有效。这个对象会保持简单,并将完成特定的目的,不会造成复杂的构建逻辑
的混乱。

实体工厂和值对象工厂是有差异的。值通常是不可变的对象,并且其所有的必需的属性需要在创建时完成。当一个对象被创建时,它
是有效的,也是最终的,不会再发生变化。实体是非不可变的。它们会在以后发生变化,前面提及过设置某些属性时需要考虑所有的
不变量。另一个差异源于实体需要标识符,而值对象不需要。有时工厂是不需要的,一个简单的构造函数就足够了。

在如下情况下使用构造函数:

  1. 构造过程并不复杂。
  2. 对象的创建不涉及到其他对象的创建,所有的属性需要传递给构造函数。
  3. 客户程序对实现很感兴趣,可能希望选择使用策略模式。
  4. 类是特定的类型,不涉及到继承,所以不用在一系列的具体实现中进行选择。


其他观察结果是工厂需要从无到有创建一个新对象,也或者它们需要从先前已经存在但可能已经持久化到一个数据库中的对象进行重
建。将实体对象从它们所在的数据库中取回内存中,涉及了一个和创建一个新对象完全不同的过程。对象已经有一个了,对不变量的
违反将区别对待。当一个新的对象从无到有创建时,任何对不变量的违反都会产生一个异常。我们不能在对象从数据库重建时也这样
处理。这个对象需要的是如何修复,这样它们才是可工作的,否则就会有数据的丢失。