【问题标题】:What is your threshold to use factory instead of a constructor to create an object?您使用工厂而不是构造函数来创建对象的阈值是多少?
【发布时间】:2009-01-28 22:08:44
【问题描述】:

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

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

优点和缺点

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

谢谢

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 工厂的重点是将对象创建与客户端分开,因此,工厂模式中的具体类和抽象类之间的比例为 1:1 并没有错。跨度>
    • @lorddev 是的,但是 YAGNI。当你真正需要它时,你可以使用工厂模式。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      与本主题相关的 C# 的一些有趣之处在于,在类定义中指定的泛型类型的 new() 约束强制由泛型容器类型处理的类型实现无参数构造函数。仅当打算在类中创建类型 T 的实例时才需要 new() 约束,如GenericType<T>。在我看来,这是明确支持类工厂,尤其是生成泛型类型的工厂。

      为了解决这个问题,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;
      }
      

      如果您在 Reflector 中查看类反汇编(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 类。

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

      【讨论】:

        【解决方案4】:

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

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

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

        查看 Book of Four (Gamma et. al.) 模式一书,详细了解工厂模式,了解有关何时使用此模式的更多信息。

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

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

            始终使用工厂方法!

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

            所以,而不是:

            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();
            

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

            【讨论】:

              【解决方案7】:

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

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

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

              【讨论】:

                【解决方案8】:

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

                【讨论】:

                  【解决方案9】:

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2014-05-03
                    • 2020-08-16
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-03-13
                    • 2018-10-31
                    • 2013-09-24
                    相关资源
                    最近更新 更多