13

我很难理解我的工厂类在我的 DDD 项目中应该做什么。是的,工厂应该用于创建对象,但它到底应该做什么。考虑以下工厂类:

    public class ProductFactory
    {
        private static IProductRepository _repository;

        public static Product CreateProduct()
        {
            return new Product();
        }

        public static Product CreateProduct()
        {
            //What else would go here?
        }

        public static Product GetProductById(int productId)
        {
            //Should i be making a direct call to the respoitory from here? 
            Greener.Domain.Product.Product p = _repository.GetProductById(productId);
            return p;
        }
    }

我应该从工厂内直接调用存储库吗?

从数据库中检索数据时,我应该如何管理对象创建?

我需要什么来完成这个课程,我应该有什么其他方法?

我应该使用这个类从域和存储库中创建产品对象吗?

请帮忙!

4

7 回答 7

13

我应该从工厂内直接调用存储库吗?

不,不要在检索东西时使用工厂,只有在第一次创建工厂时才使用工厂。

从数据库中检索数据时,我应该如何管理对象创建?

如果对象的初始创建需要,则将该数据传递到工厂。

我需要什么来完成这个课程,我应该有什么其他方法?

许多工厂甚至不是单独的类,它们只是提供对象创建的方法。如果您觉得它只是要调用无参数构造函数,您可以将工厂方法折叠到另一个类中。

我应该使用这个类从域和存储库中创建产品对象吗?

存储库用于获取(在某种意义上创建)现有对象,工厂是您第一次创建对象。

最初,许多工厂除了调用构造函数外不会做太多事情。但是一旦你开始重构和/或创建更大的对象层次结构,工厂就会变得更加相关。

解释和例子:

例如,在我正在处理的项目中,我有一个 excel 处理器基类和许多实现该基类的子类。我使用工厂来获取正确的工厂,然后在其上调用方法,不知道返回了哪个子类。(注意:我更改了一些变量名称并删除/更改了很多代码)

处理器基类:

public abstract class ExcelProcessor
{
      public abstract Result Process(string ExcelFile);
}

处理器子类之一:

public class CompanyAExcelProcessor : ExcelProcessor
{
     public override Result Process(string ExcelFile)
     {
      //cool stuff
     }
}

工厂:

 public static ExcelProcessor CreateExcelProcessor(int CompanyId, int CurrentUserId)
 {
      CompanyEnum company = GetCompanyEnum(CompanyId);
      switch (company)
      {
           case CompanyEnum.CompanyA:
                return new CompanyAExcelProcessor();
           case CompanyEnum.CompanyB:
                return new CompanyBExcelProcessor();
           case CompanyEnum.CompanyC:
                return new CompanyCExcelProcessor(CurrentUserId);
           //etc...
      }
 }

用法:

ExcelProcessor processor = CreateExcelProcessor(12, 34);
processor.Process();
于 2009-03-04T15:50:59.500 回答
8

注意,实例化一个新对象有两个原因:创建它并从数据库中重新水化它。

第一种情况由工厂处理。您可以提供多种方法来在工厂上创建对象。工厂方法应该返回有效的对象,因此您可以将参数传递给这些方法以提供所需的信息。

工厂方法也可以根据参数选择实际类型进行实例化。

您不应将其与从数据库中补水混为一谈。这种实例化应该从数据行中获取值并用它实例化对象。我通常将其称为数据构建器而不是工厂

主要区别在于工厂将实例化具有新标识的对象,而数据构建器将实例化具有现有标识的对象。

于 2009-04-09T08:54:23.330 回答
2

您工厂的 Create 方法中应该包含的内容是将一个全新的新对象置于 VALID 状态所必需的。

现在,对于某些对象,这意味着您不会做任何事情,除了这个:

public Product Create()
{
   return new Product();
}

但是,您可能有业务规则、默认设置或其他要在创建对象时强制执行的要求。在这种情况下,您可以将该逻辑放入该方法中。

这也是工厂优势的一部分。您现在只有一个地方可以存放该特殊逻辑,并且只有一个地方可以创建新对象。

于 2009-03-04T15:39:37.093 回答
1

我个人会在以下几种情况下使用工厂:

1)其他地方控制着这个工厂返回什么类型的对象(即它可以根据情况返回对象。例如,当我测试时返回一个存根对象,当我不是时返回一个实际的实现(这显然是更多的反转控制/依赖注入问题 - 但如果您还不想将容器添加到项目中))。

2) 我有相当复杂的对象,它们具有容器、依赖项、其他关系等,需要仔细构建它们以避免创建空引用或无意义的引用。例如,如果我有一个 Schedule 对象,我可能需要设置一些开始、结束日期字段 - 如果检索、确定这些日期的逻辑足够复杂,我可能不希望调用类知道它,而只是调用默认工厂方法创建了计划对象。

希望这可以帮助。

于 2009-03-04T15:20:41.820 回答
0

在上面给出的示例中,我对您的工厂和存储库之间的区别有点不清楚。我想知道您是否不应该简单地将 CreateProduct 作为方法添加到存储库中,并使用 DI 将存储库推送到需要它的代码中?如果工厂什么都不,等等......

或者,如果您只是希望它充当全局注册的存储库,可能类似于:

public static IFooRepository Default {get;private set;}
public static void SetRepository(IFooRepository repository) {
    Default = repository;
}

(在我看来,在这种情况下将“集合”分开似乎更清楚,但您不必同意)

并让来电者使用var product = YourFactory.Default.CreateProduct();

于 2009-03-04T15:19:38.250 回答
0

@ThinkBeforeCoding - 在@m4bwav 的示例中,工厂从辅助方法获取有效 ID,但它没有在任何地方的持久层中创建新记录。但是,如果我使用数据库自​​动生成的标识列作为我的标识,那么工厂似乎必须调用存储库来进行初始对象创建。你能评论哪种方法是“正确的”吗?

于 2009-04-16T15:22:39.720 回答
0

在构建器中,您可以拥有任何需要在实体上强制不变量的逻辑,一个使用 Java 作为开发语言的小例子......

我有一个用户实体,它有一个用户名、一个密码和一个电子邮件,所有属性都是必需的,所以我有:

public class User {
    private String username;
    private String password;
    private String email:

    /**
     * @throws IllegalArgumentException if the username is null, the password is null or the
     * email is null.
     */
    public User(final String theUsername, final String thePassword, final String theEmail) {
        Validate.notNull(theUsername);
        Validate.notNull(thePassword);
        Validate.notNull(theEmail);

        this.username = theUsername;
        this.password = thePassword;
        this.email = theEmail;
    }

    // Getters / Setters / equal / hashCode / toString
}

然后我有 UserBuilder:

public class UserBuilder {
    private String username;
    private String password;
    private String email;

    public UserBuilder withUsername(final String theUsername) {
        Validate.notNull(theUsername);

        this.username = theUsername;

        return this;
    }

    public UserBuilder withPassword(final String thePassword) {
        Validate.notNull(thePassword);

        this.password = thePassword;

        return this;
    }

    public UserBuilder withEmail(final String theEmail) {
        Validate.notNull(theEmail);

        this.email = theEmail;

        return this;
    }

    public User build() {
        User user = new User(this.username, this.password, this.email);

        return user;
    }
}

您可以像这样使用构建器:

UserBuilder builder = new UserBuilder();

try {
    User user = builder.withUsername("pmviva").withPassword("My Nifty Password").withEmail("pmviva@somehost.com").build();
} catch (IllegalArgument exception) {
    // Tried to create the user with invalid arguments
}

工厂的唯一目的是创建对象的有效实例。为了不重复创建和水合代码,您可以让存储库从数据库中查询行集,并将对象的创建委托给传递行集数据的构建器。

希望这可以帮助

谢谢巴勃罗

于 2011-09-27T14:25:34.557 回答