3

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

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

 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 结构,这是不可避免的,但至少它只使用一次。

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

:)

4

5 回答 5

1

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

例如,假设您是一家汽车/卡车制造商,您拥有CarSeatsCarStereo等项目,并且还拥有TruckSeatsTruckStereo,它们都实现了一些对所有人都通用的接口,例如IVehicleItem。此时,您可以拥有两个工厂TruckFactoryCarFactory,它们都实现了一个抽象工厂,比如VehicleFactory。现在你可以做这样的事情了。

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

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

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

编辑补充:我认为人们误解了抽象工厂是什么而引起的混乱。也许我的“downvote”就是这种情况。 抽象工厂模式不同于工厂方法模式

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

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

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 文章,您会看到这段代码。

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

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

于 2009-07-22T16:44:58.400 回答
1

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

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

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

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

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

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

于 2009-07-22T17:53:37.827 回答
0

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

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

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

于 2009-07-22T18:15:04.380 回答
0

在这个实例中使用一些 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();

更多关于枚举的类似用法可以在这里这里找到

于 2009-07-22T19:43:58.413 回答
0

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

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

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

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

于 2009-07-22T16:13:47.543 回答