【问题标题】:Where to create the AbstractFactory在哪里创建 AbstractFactory
【发布时间】:2009-07-22 15:41:38
【问题描述】:

在抽象工厂中,您声明一个负责创建对象的类型。

这将防止像这样要求开关:

 if( type == ONE ) {
     doOne();
  } else if( type == TWO ) { 
     doTwo();
  } etc. 

还是一样的:

 switch( type ) {
     case ONE: doOne(); break;
     case TWO: doTwo(); break;
     etc....
  }

进入这个:

   MyAbstractFactory factoryInstance = ... ? 

   SomeObject object = factoryInstance.createObject();

   object.doX();

据我了解,AbstractFactory 将创建正确的对象,而该对象又将以多态方式执行正确的行为。

然后,如果您在程序中使用该对象 10-20 或 100 次,则不必每次都重复切换。您只需执行相应的方法并让多态性完成这项工作。

   object.doY();

   object.doZ();

添加新类型就像创建新的具体工厂一样简单。

这一切我都很清楚。但是……

混凝土工厂最初是在哪里或如何创建的(一般而言)?

我一直使用一个点(通常在 main() 方法或 Configuration.init() 方法中),而这又具有 if/else|switch 结构,这是不可避免的,但至少它只被使用一次。

但是我是“本能地”(或根据常识)这样做的,但从未读过任何描述应该在哪里创建模式的文档。

:)

【问题讨论】:

  • 这是一个技巧问题吗?听起来您已经在倒数第二段中回答了它!
  • 为什么不直接使用 Spring 和它提供的 bean factory?
  • THE if else/switch 不是不可避免的,但很可能是性能最好的实现。 (你基本上需要一个跳转表,如果你打开一个 int 这就是你将从 switch 块中得到的。但你可以使用一个创建者数组和一些索引(你需要的类型)
  • @Jeff:差不多。我解释了如何使用它,但问题是这是否是正确的方法和/或有哪些替代方法,因为文献中没有任何地方(GoF、c2.com、维基百科等)说或建议如何实例化抽象工厂.
  • @Oscar:我更新了我的答案(这更像是一个讨论)。请看一看。

标签: c# java design-patterns oop instantiation


【解决方案1】:

由于抽象工厂的主要优势是将工厂正在创建的具体项目的知识与工厂的客户分开,因此您希望保留这些知识(显然包括具体工厂实现)从代码的其余部分。一种方法是创建一个“FactoryService”,在运行时提供对抽象工厂实例的访问。

您的客户端代码可能如下所示:

FactoryService service = FactoryService.instance();
MyAbstractFactory factory = service.getFactory();
SomeObject obj = factory.createObject();

这样,你就隐藏了服务内部实例化工厂的逻辑,服务可以例如从配置文件中读取类名。

许多框架都有组件生命周期管理,允许创建组件并设置它们的依赖项(如前所述,Spring 就是其中之一)。如果你想基于 Spring,你可以配置 Spring 将一个特定的工厂实例注入到你的工厂服务中。

此方法的另一个优点是可测试性:您可以配置测试运行程序以创建模拟工厂并将其注入您的服务。该服务的每个用户都将收到这个用于测试目的的模拟工厂,而无需对其代码进行任何更改。

【讨论】:

  • FactoryService 本身就是一个 AbtractFactory。问题重复了?什么时候实例化它?第二部分是一种选择,让依赖注入器读取配置文件。 :)
  • 不一定 - FactoryService 是一个具体的实现(单例,在上面演示的案例中),它隐藏了创建抽象工厂的具体实例的细节。没错,FactoryService 是工厂,但不是 AbstractFactory。
【解决方案2】:

Abstract Factory 有点像工厂的工厂。它提供了一种在不知道具体类型的情况下创建相关对象的方法。您仍然需要知道要“实例化”哪个“抽象工厂”,这意味着要根据某个变量实例化哪个抽象工厂的具体实现。

例如,假设您是汽车/卡车制造商,您拥有 CarSeatsCarStereo 等产品,并且还拥有 TruckSeatsTruckStereo,它们都实现了一些共同的接口,例如 IVehicleItem。此时,您可以有 2 个工厂 TruckFactoryCarFactory 都实现了抽象工厂,例如 VehicleFactory。现在你可以做这样的事情了。

VehicleFactory carFactory = new CarFactory();
IVehicle car = new Car(carFactory);

VehicleFactory truckFactory = new TruckFactory();
IVehicle truck = new Truck(truckFactory);

如您所见,我在需要时实例化了适当的工厂。这是我目前能想到的最好的例子,但我不认为工厂类中的 switch 语句或 if 语句是坏的。我通常使用 Enum 来确定我需要哪个类。

编辑添加:我认为人们误解了什么是抽象工厂时会产生混乱。也许我的“downvote”就是这种情况。 Abstract Factory PatternFactory Method Pattern 不同。

Wiki - 抽象工厂模式是一种复合模式结合了工厂模式和接口模式的使用

您提到的不是 Wiki,也不是 GoF..etc 展示了如何实例化具体类。这是不正确的,如果您查看 Wikipedia 中有关工厂方法模式的文章,您会看到这个小 sn-p

public static ImageReader getImageReader(InputStream is) {    

 int imageType = figureOutImageType(is);

   switch(imageType) {
            case ImageReaderFactory.GIF:
                return new GifReader(is);
            case ImageReaderFactory.JPEG:
                return new JpegReader(is);
            // etc.
        }
}

如果您阅读有关抽象工厂模式的 Wikipedia 文章,您会看到这个小小的 sn-p 代码。

public static GUIFactory createOsSpecificFactory() {
        int sys = readFromConfigFile("OS_TYPE");
        if (sys == 0) {
            return new WinFactory();
        } else {
            return new OSXFactory();
        }
    }

关键是没有“正确”的方式。您根据您在应用程序中尝试执行的操作来实例化您的具体类。当然,“if”或“switch”语句是可以避免的。但我个人认为使用它们没有任何问题。

【讨论】:

  • 这就是我的观点。我不能硬编码 new CarFactory() 或者我可以吗?这是我的问题。正确的方法是什么?硬编码实现并在分发时更改它?这可能吗?那些应该动态加载的地方呢?
【解决方案3】:

有一些方法可以创建不涉及 if 语句的具体类,但在这种情况下,使用 if 语句确实没有任何问题。尽管很多 if 可能是最难阅读的代码,但在这种情况下,if 的目的很简单——一个用于创建对象的基本跳转表——这使得其他一切变得更简单。它是可读的,没有复杂的逻辑,所以可以接受(恕我直言)。

就替代方案而言,请考虑您是否需要工厂方法。您可能不会,您可能只是在需要的地方创建具体对象,而不是将参数传递给工厂,可以使用相同的逻辑来确定应该使用给定的对象实现。这并不总是合适的,但有时工厂过于抽象,您所需要的只是以策略模式工作的不同对象。

if 语句的另一种替代方法是映射(我相信 C# 中的字典),它根据类型查找构建器对象或对象本身(如果它是不可变的)或可以自我复制的对象。

另一种选择是使类型为 Enum,它具有可以实例化正确对象的方法。

【讨论】:

    【解决方案4】:

    我发现在使用 AbstractFactory 时有两种情况。

    第一个是只有一个具体的类将用于运行一个对象。这方面的典型示例是数据库访问代码:您不会在 SQL Server 中访问您的数据库,然后突然切换到 Oracle。具体类是稳定的。为此,我使用 Provider 模型:定义一个 ProviderBase 类、一个 ProviderCollection 类等。

    另一种情况是具体类取决于应用程序的状态。在这种情况下,我通常在抽象基类上提供一个工厂方法,该方法返回适当的具体类。在该方法中,我通常只有一个 switch 语句来决定要实例化哪个具体类。我曾经尝试过其他人的建议,并将所有具体类的副本存储在一个集合中,但是对于那个特定的应用程序,当时实例化所有这些类的性能是不可接受的。然而,这两种选择都是完全有效的,并且相当容易实现。

    【讨论】:

      【解决方案5】:

      在这个实例中使用一些 IoC 框架是最灵活的解决方案——之前的答案推荐使用 Spring,Google Guice 是另一个很好的选择,非常容易上手。

      但是,如果需要纯 Java 解决方案,枚举的工作非常好。请考虑以下示例:

      enum FactoryConfig {
        CONFIG_1 {
           IFactory instantiate() {
              return ...
           }
        },
        CONFIG_2 {
           IFactory instantiate() {
              return ...
           }
        },
        CONFIG_3 {
           IFactory instantiate() {
              return ...
           }
        };
      
        abstract IFactory instantiate();
      }
      ...
      // and its usage..
      
      IFactory factory = FactoryConfig.CONFIG_3.instantiate();
      

      有关枚举的类似用法的更多信息,例如 herehere

      【讨论】:

      • 你不需要工厂。您所需要的一切 - 为您的对象创建一个工厂。这个 convrete 工厂将返回具有您需要在任何地方使用的接口的类。由 this 创建的对象实现了此接口的不同实现。当您需要创建对象时,您将其委托给工厂,提供创建对象所需的一切。工厂返回具体对象,您正在根据通用接口使用它。就是这样,不需要创建多个工厂。
      猜你喜欢
      • 1970-01-01
      • 2011-11-04
      • 2012-08-24
      • 2014-10-04
      • 2015-12-09
      • 2016-03-15
      • 2015-11-09
      • 2018-09-27
      • 2011-02-06
      相关资源
      最近更新 更多