使用工厂而不是构造函数来创建对象的阈值是多少?

时间:2009-01-28 22:08:45

标签: design-patterns domain-driven-design factory

使用工厂而不是构造函数创建对象的阈值是多少?

  1. 你总是使用工厂。
  2. 只有在检查空值时才进行不变检查时才使用工厂。
  3. 您总是使用构造函数
  4. 你很少使用工厂......那些情况是什么?
  5. 利弊

    更新:我在我的项目中应用了Domain Driven Design的工厂模式。创建工厂背后的原因之一是减少域模型中的噪声。

    由于

9 个答案:

答案 0 :(得分:13)

如果我有一个抽象基类(或接口),我使用工厂,以及几个具体的派生类,并且有一些逻辑,根据这些逻辑创建一个具体类。我在工厂实现了这个逻辑。

答案 1 :(得分:5)

工厂最明显的情况是在运行时选择实现接口的特定类,例如,从配置文件中选择。我没有大量使用工厂,但是当我希望两个对象高度分离时,我更有可能使用工厂来获取另一个工具。

答案 2 :(得分:3)

与此主题相关的C#有趣的是,对类定义中指定的泛型类型的new()约束强制通用容器类型处理的类型实现无参数构造函数。只有当打算在类中创建类型为T的实例(如GenericType<T>中)时,才需要new()约束。在我看来,这明确支持类工厂,特别是生产泛型类型的工厂。

为了解决这个问题,Windows Communication Foundation(WCF)有一个ChannelFactory类,它定义了以下静态工厂方法:

public static TChannel CreateChannel(Binding binding, EndpointAddress endpointAddress, Uri via)
{
    ChannelFactory<TChannel> factory = new ChannelFactory<TChannel>(binding);
    if (factory.HasDuplexOperations())
    {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxInvalidStaticOverloadCalledForDuplexChannelFactory1", new object[] { factory.channelType.Name })));
    }
    TChannel channel = factory.CreateChannel(endpointAddress, via);
    ChannelFactory<TChannel>.SetFactoryToAutoClose(channel);
    return channel;
}

如果在类反汇编(System.ServiceModel assembly&amp; System.ServiceModel.Channels命名空间)中查看Reflector,您会注意到“new()”不用作约束。

那是因为CreateChannel方法使用typeof(TChannel)来委托进一步向下创建对象......

public virtual TChannel CreateChannel(EndpointAddress address, Uri via)
{
    TChannel local;
    bool traceOpenAndClose = base.TraceOpenAndClose;
    try
    {
        using (ServiceModelActivity activity = (DiagnosticUtility.ShouldUseActivity && base.TraceOpenAndClose) ? ServiceModelActivity.CreateBoundedActivity() : null)
        {
            if (DiagnosticUtility.ShouldUseActivity)
            {
                ServiceModelActivity.Start(activity, this.OpenActivityName, this.OpenActivityType);
                base.TraceOpenAndClose = false;
            }
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
            if (base.HasDuplexOperations())
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxCreateNonDuplexChannel1", new object[] { base.Endpoint.Contract.Name })));
            }
            base.EnsureOpened();
            local = (TChannel) this.ServiceChannelFactory.CreateChannel(typeof(TChannel), address, via);
        }
    }
    finally
    {
        base.TraceOpenAndClose = traceOpenAndClose;
    }
    return local;
}

当传递Type类时,您可以更深入地跟踪委托链,直到最后调用以下方法:

RemotingServices.CreateTransparentProxy(this, classToProxy, stub, stubData);

它非常复杂,但它是我见过的最复杂的工厂。有趣的是,所有的机制最终都是WCF从System.Runtime.Remoting.Proxies命名空间创建一个RealProxy类。

总之,工厂是针对具有很多复杂性或需要从动态类型构造中受益的对象。

答案 3 :(得分:2)

我不确定你是如何选择门槛的......

如果您不想从构造中抽象出对象的使用者,那么工厂是合适的。可能与此相关的实例:

  • 您可能希望在运行时分出实现。通常,这与使用接口而不是具体类相结合。 Java中的一个例子是如何从Java中的DocumentBuilder获取Document对象。
  • 您可能希望限制对象的实例数。考虑使用有限数量的线程对象构建池,而不是一直只创建新的

查看Book of Four(Gamma等人)模式书并详细查看工厂模式,了解何时使用此模式的更多信息。

答案 4 :(得分:1)

我喜欢将构造函数的数量保持在合理的低水平;超过两三个,我质疑对象的结构是如何设计的。

如果引入其他构造函数来支持设置各种可选属性,我喜欢使用Builder,如 Effective Java 中所述(Joshua Bloch,2nd ed 。)

答案 5 :(得分:1)

这是一个激进的想法(我不是在提倡它,但我认为这不会有害):

始终使用工厂方法!

工厂方法更灵活,例如,它们可以缓存结果或返回子类。

所以,而不是:

class SomeClass {
  public SomeClass(/*parameters*/) { /*...*/ }
} 

始终使用:

class SomeClass {
  protected SomeClass(/*parameters*/) { /*...*/ }
  public static SomeClass New(/*parameters*/) {
    return new SomeClass(/*parameters*/);
  }
} 

来电者代码更改为:

SomeClass sc = new SomeClass();

要:

SomeClass sc = SomeClass.New();

您现在可以更改“构造函数”逻辑以返回子类或缓存实例,并且所有调用者都不会受到影响。您现在可以控制“构造函数”的返回值。

答案 6 :(得分:0)

我试着在这些之间进行衡量。我认为你应该在以下时间使用工厂:

  1. 大量的参数。
  2. 可选参数。 (两者都使用内部类Builder模式)
  3. 由于相同的参数类型但不同的数据,您必须更改参数的顺序才能执行其他操作。
  4. 你需要一个单例类(用枚举更好)
  5. 对于工厂,在这种情况下,您可以为要返回的对象状态指定正确的名称。

答案 7 :(得分:0)

当决定实例化哪个具体类不是由客户端决定时,请使用工厂。例如那里有几个“家庭”的物品,可以选择在其他地方使用哪个家庭。

答案 8 :(得分:0)

我认为你混淆了Builder模式和Factory模式。我建议只使用构造函数并完成它。听起来好像(没有看到代码)你过度思考或过度分析你的代码。