什么时候应该在聚合根中应用工厂方法?
When should apply factory method in the aggregate root?
领域驱动设计建议我们应该通过使用工厂来创建聚合根来隐藏复杂性。我们可以使用以下方式创建聚合根:
- 工厂中的静态工厂方法class
- 具有工厂方法的聚合根
我们在(1)和(2)之间做出选择的依据是什么?
工厂并不完全特定于 DDD。例如,您可以在 GoF 中找到工厂模式。
通常,您会根据以下因素做出选择:
- 如果你的工厂returns不同类型,根据参数,应该放在class
- 如果您的工厂总是 returns 一个 class 的实例,则应将其作为此 class
中的静态方法
当谈到 DDD 时,您通常会在聚合根上有工厂方法,以封装复杂的创建逻辑,当然如果您有这样的逻辑的话。工厂根据您的通用语言命名,并确保只能创建一致的聚合。
当生成的代码与您的通用语言更好地对齐并且 AR 具有一些可以简化创建过程的知识时,请考虑在 AR 上使用工厂方法。
例如,如果在您的域中您可以将任务添加到项目并且任务被建模为 AR,那么 Task task = project.addTask(taskId, taskName);
比 Task task = new Task(taskId, taskName, projectId);
更具表现力和更简单。
你的第一个建议
Static factory method in a factory class
是您通常应该避免的事情。 Public 如果静态方法具有服务特性(工厂有),那么它们就是一种代码味道。 最好创建一个非静态的class,在任何使用它的地方注入它并调用它的实例方法。如有必要,创建一个接口。这种方法提高了可测试性并使 class 之间的依赖关系明确。
如果你听从这个建议,你的问题就变成了工厂方法模式 vs 抽象工厂模式.
工厂方法
当有多种方法构造某个class的对象时使用工厂方法。在这种情况下,工厂方法比直接调用构造函数更可取,因为您可以为它们指定一个描述性名称。如果你使用它们,将构造函数设为私有通常是有意义的,这样客户就会意识到他们不应该调用构造函数,而是调用适当的工厂方法。
工厂方法只是包装构造函数。因此,他们无法真正简化对象的构造(除了给它命名)。
抽象工厂
如果构造一个对象很重要,那么选择的模式是抽象工厂模式。请注意,您不需要拥有多个 classes 就可以使用 Alexey Zimarev 所建议的抽象工厂。抽象工厂只对正在创建的一种类型的对象非常有意义。
抽象工厂是一种特殊的服务,即创建对象的服务。因此,它们可以具有依赖关系,例如它们可以将这些依赖关系提供给创建的对象。
示例:假设您要创建一个需要字符串值和对 ISomeService
的依赖项的对象。在这里,抽象工厂可以通过提供 ISomeService
来提供帮助。工厂的界面将如下所示:
interface IFooFactory {
IBar CreateBar(string value);
}
这个工厂为客户简化了 IBar
的创建,因为他们不必自己提供 ISomeService
。
在 DDD 聚合的上下文中
聚合通常包含对域服务的引用。 因此,聚合的实例化通常很重要,因此抽象工厂模式非常适合这里。
基本相同。选项 1. 可能会帮助您避免臃肿的实体 class。选项 2. 如果在以实体名称为前缀时创建更自然地读取,则更好,如
LoyaltyCard.ForCustomer(...)
SavedSearch.WithCriteria(...)
领域驱动设计建议我们应该通过使用工厂来创建聚合根来隐藏复杂性。我们可以使用以下方式创建聚合根:
- 工厂中的静态工厂方法class
- 具有工厂方法的聚合根
我们在(1)和(2)之间做出选择的依据是什么?
工厂并不完全特定于 DDD。例如,您可以在 GoF 中找到工厂模式。
通常,您会根据以下因素做出选择:
- 如果你的工厂returns不同类型,根据参数,应该放在class
- 如果您的工厂总是 returns 一个 class 的实例,则应将其作为此 class 中的静态方法
当谈到 DDD 时,您通常会在聚合根上有工厂方法,以封装复杂的创建逻辑,当然如果您有这样的逻辑的话。工厂根据您的通用语言命名,并确保只能创建一致的聚合。
当生成的代码与您的通用语言更好地对齐并且 AR 具有一些可以简化创建过程的知识时,请考虑在 AR 上使用工厂方法。
例如,如果在您的域中您可以将任务添加到项目并且任务被建模为 AR,那么 Task task = project.addTask(taskId, taskName);
比 Task task = new Task(taskId, taskName, projectId);
更具表现力和更简单。
你的第一个建议
Static factory method in a factory class
是您通常应该避免的事情。 Public 如果静态方法具有服务特性(工厂有),那么它们就是一种代码味道。 最好创建一个非静态的class,在任何使用它的地方注入它并调用它的实例方法。如有必要,创建一个接口。这种方法提高了可测试性并使 class 之间的依赖关系明确。
如果你听从这个建议,你的问题就变成了工厂方法模式 vs 抽象工厂模式.
工厂方法
当有多种方法构造某个class的对象时使用工厂方法。在这种情况下,工厂方法比直接调用构造函数更可取,因为您可以为它们指定一个描述性名称。如果你使用它们,将构造函数设为私有通常是有意义的,这样客户就会意识到他们不应该调用构造函数,而是调用适当的工厂方法。
工厂方法只是包装构造函数。因此,他们无法真正简化对象的构造(除了给它命名)。
抽象工厂
如果构造一个对象很重要,那么选择的模式是抽象工厂模式。请注意,您不需要拥有多个 classes 就可以使用 Alexey Zimarev 所建议的抽象工厂。抽象工厂只对正在创建的一种类型的对象非常有意义。
抽象工厂是一种特殊的服务,即创建对象的服务。因此,它们可以具有依赖关系,例如它们可以将这些依赖关系提供给创建的对象。
示例:假设您要创建一个需要字符串值和对 ISomeService
的依赖项的对象。在这里,抽象工厂可以通过提供 ISomeService
来提供帮助。工厂的界面将如下所示:
interface IFooFactory {
IBar CreateBar(string value);
}
这个工厂为客户简化了 IBar
的创建,因为他们不必自己提供 ISomeService
。
在 DDD 聚合的上下文中
聚合通常包含对域服务的引用。 因此,聚合的实例化通常很重要,因此抽象工厂模式非常适合这里。
基本相同。选项 1. 可能会帮助您避免臃肿的实体 class。选项 2. 如果在以实体名称为前缀时创建更自然地读取,则更好,如
LoyaltyCard.ForCustomer(...)
SavedSearch.WithCriteria(...)