【问题标题】:Is it confusing to call this class a "factory"?将此类称为“工厂”是否令人困惑?
【发布时间】:2010-12-17 04:52:32
【问题描述】:

我对工厂的理解是,它封装了所有继承公共抽象类或接口的具体类的实例化。这允许客户端与确定创建哪个具体类的过程分离,这反过来意味着您可以在程序中添加或删除具体类,而无需更改客户端的代码。您可能需要更改工厂,但工厂是“专门构建的”,实际上只有一个原因需要更改——添加/删除了一个具体的类。

我已经构建了一些实际上封装了对象实例化的类,但出于不同的原因:对象很难实例化。目前,我将这些类称为“工厂”,但我担心这可能是用词不当,会混淆可能查看我代码的其他程序员。我的“工厂”类不决定实例化哪个具体类,它们保证一系列对象将以正确的顺序实例化,并且当我调用 new() 时,正确的类将传递给正确的构造函数。

例如,对于我正在处理的一个 MVVM 项目,我编写了一个类来确保我的 SetupView 被正确实例化。它看起来像这样:

public class SetupViewFactory
{
    public SetupView CreateView(DatabaseModel databaseModel, SettingsModel settingsModel, ViewUtilities viewUtilities)
    {
        var setupViewModel = new SetupViewModel(databaseModel, settingsModel, viewUtilities);
        var setupView = new SetupView();
        setupView.DataContext = setupViewModel;
        return setupView;
    }
}

称其为“工厂”会不会令人困惑?它不会在几个可能的具体类中做出决定,它的返回类型不是接口或抽象类型,但它确实封装了对象实例化.

如果不是“工厂”,那是什么?


编辑

许多人认为这实际上是建造者模式。

来自dofactory的Builder Pattern的定义如下:

将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示。

这似乎也有点牵强。困扰我的是“不同的表示”。我的目的不是抽象构建视图的过程(不是说这不是一个有价值的目标)。我只是试图将创建特定视图的逻辑放在一个地方,这样如果创建该视图的任何成分或过程发生变化,我只需更改这一类。构建器模式似乎真的是在说,“让我们为创建视图创建一个通用过程,然后您可以为创建的任何视图遵循相同的基本过程。”很好,但这不是我在这里做的。


编辑 2

我刚刚在Wikipedia article on Dependency Injection 中发现了一个有趣的例子。

在“手动注入的依赖”下,它包含以下类:

public class CarFactory {

    public static Car buildCar() {
        return new Car(new SimpleEngine());
    }

}

这几乎是完全我的用法(而且,不,我没有写维基文章:))。

有趣的是,这是这段代码后面的注释:

在上面的代码中,工厂负责组装汽车,并将发动机注入汽车。这使汽车无需了解如何创建引擎,尽管现在 CarFactory 必须了解引擎的构造。可以创建一个 EngineFactory,但这只会在工厂之间创建依赖关系。所以问题仍然存在,但至少已经转移到 factoriesbuilder 对象中。 [我的重点]

所以,这告诉我,至少我不是唯一一个想创建一个类来帮助将东西注入另一个类的人,也不是唯一一个试图将这个类命名为工厂的人。

【问题讨论】:

  • 不要吹毛求疵,但您可以将方法重命名为 Create 或 CreateSetupView。
  • 如果这就是你想要完成的全部,那么它并不是真正的任何模式,只是在一些顶级课程中更新所有内容。称它为工厂是错误的。
  • 听起来像实例构造器模式。

标签: c# naming-conventions design-patterns factory


【解决方案1】:

在我看来,这与 SetupView 实例的构造函数没有太大区别。也许您的示例中省略了太多代码上下文,但我不明白为什么您在那里拥有的内容不能简单地写为构造函数,并将参数作为 args 传递给构造函数。

严格从 GoF 模式来看,我认为它与 Factory 不符有两个原因:

  1. 没有相关/依赖对象系列
  2. 确实指定了具体类

关于Builder,我认为它在产品的差异方面有所遗漏。

当我教授设计模式课程时,我告诉学生在尝试确定“最合适”时不仅要看模式的意图,还要看适用性和结构/参与者。意图可以帮助您排除问题,但适用性和结构/参与者将帮助您找到正确的方向。

如果您可以确定设计的哪一部分满足(抽象)工厂或构建器参与者的各种角色,那么您就为使用该命名法提供了一个很好的案例。

【讨论】:

  • 谢谢,@Chris,我同意你的看法。我的班级并没有真正遵循这两种模式,但老实说,这并不是真正的意图。我想知道的是,如果我没有真正实现该模式,那么从一个模式中劫持一个通用的类命名方案是否是一件坏事。对此有什么想法吗?另外,请参阅我对问题的第二次编辑。
  • @Chris,另外,请参阅我对@Jeff Sternal 回答的评论,了解为什么我不只是为 SetupView 编写构造函数。
  • 如果模式的主要目的之一是定义一种通用语言,那么,是的,我认为将其称为不是的东西是一种误导,尤其是 如果您以更“真实”的方式使用模式语言的其他元素。例如,我们可以在对话中使用“汽车”这个词,而不必指定轮子的数量、发动机的存在、门、尺寸等,因为这个词体现了一种共同的理解。对我来说,在我们的谈话中称某物为“汽车”,而实际上它是一辆半挂车是一种误导。
  • 另外,我通过你的解释阅读了你为什么不使用两个简单的ctors。最初,我在考虑两个简单的 ctor 和 SetupView 的“方便” ctor,它采用与 SetupViewModel 的 ctor 相同的参数集,并在幕后完成所有繁重的工作。但是,我理解在您的情况下需要将事情分开。尽管维基百科文章中有相似之处,但我仍然不确定您是否拥有 GoF 工厂或制造商,但这可能并不重要。 (续)
  • 归根结底,我认为 重要的是,如果一个人必须花费大量时间来解释你的东西是工厂、建造者、构造者或 ,那么它可能太过分了。称它为工厂式,或者只是解释它的作用。模式背后的一个中心思想是减少必须发生的显式通信量,所以如果你无论如何都必须这样做,你可能会比根本不使用名称更糟。
【解决方案2】:

在我看来,您所拥有的更多的是 Builder 模式,而不是 Factory 模式。

【讨论】:

    【解决方案3】:

    我认为,它是一个“工厂”。当我查看类声明时,我很高兴看到这是一个“工厂”,而且它是一个 SetupView 对象的“工厂”。所以我知道这个 Factory 会为我提供具体且正确构建的 SetupView 实例。

    【讨论】:

      【解决方案4】:

      GoF 称之为“Builder

      【讨论】:

      • 据我所知,建设者需要导演和产品
      • Director 是客户端代码,Product 显然是一个SetupView。
      【解决方案5】:

      “工厂”是封装新对象实例化的任何东西。为什么,或者对象是什么,并不重要。最常见的用途是您所描述的,但其他不符合该描述的用途也可以称为工厂。基本的想法,(我认为除了最讨厌或最挑剔的开发人员之外,其他人都不会误解),就是这个东西为你实例化了新的对象......

      【讨论】:

        【解决方案6】:

        这对我来说似乎有点奇怪。它不是一个抽象工厂,但也不完全符合 Builder 模式。对我来说最有意义的是不是让 SetupViewFactory 与 CreateView 方法一起使用,而是将此方法移动到 SetupView 类并使其成为静态。所以你会有...

        public class SetupView
        {
            public static SetupView CreateView(DatabaseModel databaseModel, SettingsModel settingsModel, ViewUtilities viewUtilities)
            {
                var setupViewModel = new SetupViewModel(databaseModel, settingsModel, viewUtilities);
                var setupView = new SetupView();
                setupView.DataContext = setupViewModel;
                return setupView;
            }
        }
        

        现在您有了所谓的静态工厂或工厂方法。您可以更进一步,将 SetupView 构造函数私有化,以便获取 SetupView 实例的唯一方法是调用工厂方法。

        回应 OP 的评论:

        如果需要分离,我会避免在这部分代码的任何位置创建 SetupViewModel。相反,只需传入要与 SetupView 关联的 SetupViewModel,而不是传递给 SetupViewModel 构造函数的组成属性。于是代码就变成了……

        public class SetupView
        {
            public static SetupView CreateView(SetupViewModel model)
            {
                var setupView = new SetupView(); { DataContext = model };
                return setupView;
            }
        }
        

        【讨论】:

        • @Jason 肯定会把它带到工厂方面。相反,您可以将 CreateView 拆分为单独的功能,并让最后一步返回 SetupView,这将倾向于 Builder。我认为它肯定可以双向进行。
        • 有趣的想法,但在我目前的设计中,我认为 SetupView 和 SetupViewModel 之间存在理想的分离。我可能希望在我的 View 中使用不同的 ViewModel,这意味着我可以创建另一个工厂(或编辑工厂)而不是弄乱我的 View。
        • @Jason,感谢您对您的回答的补充,但现在我们又回到了第一点 :) 我要做的全部重点是自动化并确保创建 ViewModel 的过程,创建一个视图,设置 DataContext,并返回一个“准备就绪”的视图。您在这里所做的是将此责任的一部分转移到 SetupView 类中,这没关系(甚至可能很好),但是现在,负责调用 SetupView.CreateView() 的任何类仍然需要一个名称。 (请注意,我还有比这个更复杂的其他示例。)
        • @DanThMan...呃。什么栗色。当然,这与您想要的方向相反。我有时是个傻瓜……
        【解决方案7】:

        为什么不是两个具有简单构造函数的类?

         public class SetupViewModel {
              public SetupViewModel(DatabaseModel databaseModel, SettingsModel settingsModel, ViewUtilities viewUtilities) {
                  this.DatabaseModel = databaseModel;
                  this.SettingsModel = settingsModel;
                  this.ViewUtilities = viewUtilities;
              }
         }
        

         public class SetupView {         
              public SetupView(SetupViewModel model) {              
                  this.DataContext = model;
              }
         }
        

        这样,两个类都清楚地说明了它们需要构造的内容。

        【讨论】:

        • 谢谢,杰夫。这几乎正​​是我已经为SetupViewModel 所拥有的。我没有 SetupView 设置它自己的 DataContext 主要是因为这不是用于 MVVM 模式的约定。这个想法是 View 应该由设计师/艺术家使用例如 Expression Blend 生成,因此最好在代码隐藏中包含尽可能少的代码。此外,您可以随时插入新视图,而无需添加设置 DataContext 的代码。
        • 啊,明白了!在那种情况下,这似乎是完全合适的——尽管我同意其他人的观点,这非常接近于成为一名建设者。如果你添加一个虚拟的 CreateSetupView 方法(代替 new 语句调用),你就在那里 - 这可能很有用。
        【解决方案8】:

        你可以称它为“巫师”。

        【讨论】:

          【解决方案9】:

          我倾向于同意上面的 Jason。对我来说,它更像是一个 Builder 模式,但如果你把 SetupView 变成一个界面,它更像是一个工厂模式。工厂模式的关键是让工厂模式完成所有工作来决定从函数调用中返回什么。因此,我认为您必须在给定输入参数的情况下在各种选项之间进行切换。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-01-21
            • 2022-01-17
            • 1970-01-01
            • 2011-11-09
            • 1970-01-01
            • 1970-01-01
            • 2011-02-03
            相关资源
            最近更新 更多