【问题标题】:Constructor vs. Factory in .NET Framework.NET Framework 中的构造函数与工厂
【发布时间】:2009-12-08 20:10:20
【问题描述】:

下面是一篇关于 .net 框架使用模式的文章。我不确定我是否理解下面摘录中的粗体部分。是否暗示如果您更改创建对象的细节,您(可能)会更改构造函数参数?

在框架中有很多情况下,您 可以获得结构或类的新实例 无需自己调用其构造函数。这 System.Convert 类包含大量静态 像这样工作的方法。转换整数 一个布尔值,例如,你可以调用 Convert.ToBoolean 并传入整数。这 此方法调用的返回值是一个新的布尔值 如果整数非零,则设置为“true”并且 否则为“假”。 Convert 类创建 具有正确值的布尔值。其他类型 转换方法的工作方式类似。解析 Int32 和 Double 上的方法返回新实例 这些对象设置为适当的值 只给出一个字符串。

这种创建新对象实例的策略是 称为工厂模式。而不是调用 对象的构造函数,可以问对象 工厂为您创建实例。那样, 工厂类可以隐藏复杂性 对象创建(例如如何解析 Double 出 一个字符串)。如果您想更改详细信息 创建对象,您只需要更改 工厂本身; 您不必更改每个 构造函数所在的代码中的单个位置 调用。

发件人:http://msdn.microsoft.com/en-us/magazine/cc188707.aspx

【问题讨论】:

  • 好问题——工厂的替代品难道不是新的构造函数吗? (与在整个代码库中重复解析代码相反。)构造函数还可以“隐藏对象创建的复杂性(例如如何从字符串中解析出 Double)。”

标签: .net design-patterns factory-pattern


【解决方案1】:

我其实认为他们提供的例子不一定是很好的例子。

当您构建类时,工厂模式在 .NET 中变得更加有用。例如,查看WebRequest class

这个类通常通过调用来实例化:

WebRequest request = WebRequest.Create(url);

WebRequest.Create 方法使用工厂模式。根据 URL 的类型,它会创建不同类型(子类)的 WebRequest。例如,如果您传递一个http:// url,您实际上将创建一个HttpWebRequest 实例-ftp:// URL 将创建一个FtpWebRequest

通过在此处使用工厂模式,以后可以添加更多 URL 类型,而无需更改客户端的任何代码 - 您只需传入不同的 URL(作为字符串),并获取一个新对象。

【讨论】:

  • 你说的是工厂方法模式。但最初的问题是指工厂模式(参见en.wikipedia.org/wiki/Factory_pattern),它涵盖了我在下面描述的工厂方法模式和抽象工厂模式。
【解决方案2】:

工厂模式是我在程序中使用最多的一种。在某些情况下它可能非常有用,但是在使用它时你当然应该小心。 如果您的工厂看起来像构造函数重载,那么它可能应该是构造函数重载。 MSDN 文章中给出的示例并不好。事实上,很长一段时间以来,我一直认为像 Int、String 等对象不会考虑彼此重载,因为它们是 structs 而不是类,并且通常, struct 构造函数不应抛出异常(例如,如果您可以将“hello world”提供给 int,则会发生这种情况)。但我是这么认为的。

当然,网络上有很多地方可以找到关于何时使用工厂模式及其好处的更好解释。 Reed 给出的示例是最好的示例之一,并且遵循我使用工厂的规则:当我有一个类层次结构或多个实现某个接口的类并且我想构建其中一个对象但接收到一个对象时,我会使用工厂超类/接口而不是对象本身。这样,调用工厂的对象不必担心实现细节。它知道它期待某个类的对象,这就是它得到的,即使它是一个演员表。

在我正在构建的当前应用程序(程序生成器)中,我需要解析数据表 SQL 定义。我使用自制解析器来做到这一点。每次解析器遇到一个变量(“IDENTIFIER VARIABLE-TYPE”)时,我都会调用一个工厂并将字符串传递给它。然后工厂检查变量类型的子字符串并返回给我一个 SqlVariable,它实际上可能是一个 IntegerSqlVariable 或一个 CharSqlVariable

主解析器对象不知道它得到了什么变量。它将 SqlVariable 推送到列表并读取下一行。当然,我可以有一个类来处理所有变量类型;我个人选择不这样做。

【讨论】:

    【解决方案3】:

    工厂的整体理念似乎不同。这不仅仅是隐藏实现的复杂性,还涉及控制反转 (IoC)。

    • 与其自己创建SenderReceiver 对象,不如让MessageFactory 对象负责创建它们。
    • 假设我们最初实现了一个TcpMessageFactoryMessageFactory后代)创建TcpSenderTcpReceiver对象。所以现在我们的应用程序可以使用 TCP。
    • 稍后我们发现 UDP 更快。所以我们实现了UdpSenderUdpReciever。此外,我们还实现了UdpMessageFactoryMessageFactory 的后代)
    • 现在我们可以允许用户选择要使用的协议。基于此,我们创建工厂对象(TcpMessageFactoryUdpMessageFactory)并在我们的应用程序中进一步使用它。

    在这个例子中MessageFactory.CreateSenderMessageFactory.CreateReciever必须是抽象方法; SenderReciever 的方法形成其公共 API(合同)也必须是抽象的。

    顺便说一句,最初工厂主要用于对象创建:想象 C++ 库 (DLL) 必须允许为主 .exe 创建一组自己的类型。在不同模块之间传递实例几乎是不可能的,因为它们的结构取决于特定的编译器。但是可以通过接口。所以这样的 DLL 应该:

    • 导出返回工厂接口的函数,例如IDllTypeFactory
    • 实现它的对象必须在调用其CreateXxx 方法时返回IDllTypeXxx 对象。这些IDllTypeXxx实际上是由我们原计划导出的类型实现的。

    但如您所知,.NET 以其统一的汇编格式使得在不同模块之间传递对象成为可能。此外,反射允许创建一个你根本不知道的类型。所以 .NET 中的工厂很少使用。

    【讨论】:

      【解决方案4】:

      是的,您可以使用构造函数重载来更改构造函数,而不必更改源代码中的每个区域,但是如果您不小心的话,这将很快导致构造函数的数量变得荒谬。

      我认为这篇文章的总体思路是隐藏实现,而不是为了解释构造函数的变化而必须防止大量的构造函数重载。作为对象的想法不应该知道如何构造它。

      在给定的示例中,您不希望 Int 类如何从字符串或双精度值变为 int 类,因此您创建自己的工厂,其唯一职责是从 Y 参数创建 X 对象。

      我绝对会推荐 Head First Design Patterns 这本书作为学习设计模式的一个很好的介绍。这些示例是基于 Java 的,但无论您使用哪种语言,它们背后的逻辑都适用。

      【讨论】:

      • 荒谬的构造函数数量和“大量构造函数重载”,或​​者荒谬数量的工厂方法和“大量工厂方法重载”?这都一样。你不会“阻塞你的 Int 类”,那里有足够的空间。
      猜你喜欢
      • 2012-01-31
      • 1970-01-01
      • 2010-10-12
      • 1970-01-01
      • 2014-11-03
      • 2014-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多