11

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

  1. 你总是使用工厂。
  2. 仅当您有除检查空值之外的不变检查时,您才使用工厂。
  3. 你总是使用构造函数
  4. 你很少使用工厂......那些情况是什么?

利弊

更新:我在我的项目中应用域驱动设计的工厂模式。创建工厂的原因之一是减少领域模型中的噪音。

谢谢

4

9 回答 9

13

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

于 2009-01-28T22:31:16.867 回答
5

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

于 2009-01-28T22:52:14.437 回答
3

与此主题相关的 C# 的一些有趣之处在于,对类定义中指定的泛型类型的 new() 约束强制由泛型容器类型处理的类型实现无参数构造函数。仅当打算在GenericType<T>类中创建类型 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 程序集和 System.ServiceModel.Channels 命名空间),您会注意到“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 类。

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

于 2009-01-28T23:51:08.547 回答
2

我不确定您是如何选择阈值的...

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

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

查看 Book of Four (Gamma et. al.) 模式书并详细查看工厂模式以获取有关何时使用此模式的更多信息。

于 2009-01-28T22:18:49.043 回答
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();

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

于 2009-02-24T21:31:57.687 回答
1

我喜欢将构造函数的数量保持在合理的低水平;超过两三个,我质疑这个物体的结构设计得有多好。

在引入其他构造函数以支持设置各种可选属性的情况下,我喜欢使用Builder,如Effective Java(Joshua Bloch,第 2 版)中所述。

于 2009-01-28T22:14:11.533 回答
0

当实例化哪个具体类的决定不取决于客户端时,请使用工厂。例如,当有多个对象“族”时,选择使用哪个族是在别处进行的。

于 2009-01-28T23:39:08.023 回答
0

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

  1. 参数数量多。
  2. 可选参数。(两者都使用内部类 Builder 模式)
  3. 由于参数类型相同但数据不同,您必须更改参数的顺序才能完成其他操作。
  4. 你需要一个单例类(最好用枚举)

对于工厂,在这种情况下,您可以为返回的对象状态指定一个正确的名称。

于 2009-01-28T22:16:35.490 回答
0

I think you are confusing the Builder pattern and the Factory pattern. I would suggest just using constructors and being done with it. It sounds like (without seeing the code) you're overthinking or overanalysing your code a bit.

于 2009-01-29T01:07:03.493 回答