工厂方法模式的现实世界例子

40

我刚阅读了关于工厂方法的内容,我理解它提供了一种委托实例化给子类的方式。但我无法理解在实际场景中可能的用途。

有没有人能够给出一个典型的示例,展示工厂方法模式如何使用,以便我可以将其与我所阅读的内容联系起来。

如果提供一个最佳解决方案是工厂方法模式的问题描述,就足以使其清晰明了。

5个回答

54

实现工厂设计模式的类作为多个类之间的桥梁。以使用多个数据库服务器(如SQL Server和Oracle)为例。如果您正在使用SQL Server数据库作为后端开发应用程序,但将来需要将后端数据库更改为Oracle,则需要修改所有代码,如果您没有按照工厂设计模式编写代码。

在工厂设计模式中,您只需要做很少的工作即可实现此目标。实现工厂设计模式的类会代替您完成这些工作,减轻您的负担。切换数据库服务器不会对您造成任何麻烦。您只需要在配置文件中进行一些小的更改即可。


3
以上示例代码目前不可用。我找到了这个链接,很有用... http://www.primaryobjects.com/CMS/Article81.aspx - DevT
2
为什么不使用通用接口和依赖注入呢?例如 IDatabase。在 DI 可以很好地处理的情况下,工厂模式似乎有些过度设计了。 - Levi Fuller
@LeviFuller 我猜你可以这样做,但是至少在链接中的示例中,你无法将连接字符串添加到接口中。 - cah1r
@cah1r 你也可以使用解析器来解决具有相同接口的多个依赖项的问题,或者为不同的连接使用单独的接口。 - Levi Fuller

10

一个使用静态创建的PHP代码示例:

interface DbTable
{
    public function doSomething(): void;
}

class MySqlTable implements DbTable
{
    public function doSomething(): void
    { }
}

class OracleTable implements DbTable
{
    public function doSomething(): void
    { }
}

class TableFactory
{
    public static function createTable(string $type = null): DbTable
    {
        if ($type === 'oracle') {
            return new OracleTable();
        }
        return new MySqlTable(); // default is mysql
    }
}

// client
$oracleTable = TableFactory::createTable('oracle');
$oracleTable->doSomething();

为了使其更具动态性(减少后续修改):
interface DbTable
{
    public function doSomething(): void;
}

class MySqlTable implements DbTable
{
    public function doSomething(): void
    { }
}

class OracleTable implements DbTable
{
    public function doSomething(): void
    { }
}

class TableFactory
{
    public static function createTable(string $tableName = null): DbTable
    {
        $className = __NAMESPACE__ . $tableName . 'Table';
        if (class_exists($className)) {
            $table = new $className();
            if ($table instanceof DbTable) {
               return $table;
            }
        }
        throw new \Exception("Class $className doesn't exists or it's not implementing DbTable interface");
    }
}

$tbl = TableFactory::createTable('Oracle');
$tbl->doSomething();

2
这更像是简单工厂而不是工厂方法模式。据我理解,工厂方法模式提供多个具体工厂来创建从同一抽象产品继承的具体产品实例。如果我认为只需要一个工厂根据客户端传递的类型字符串(或枚举)来决定实例化哪个具体产品,那么甚至简单工厂也足够了。在这种情况下,我们为什么需要使用工厂方法模式呢? - ndh
@ndh 做了一些更改 :) - M Rostami

3

我正在开发的API:

WebGalleryFactory factory = WebGalleryFactory.newInstance (WebGalleryType.PICASA);
WebAlbum album = factory.createAlbum (title, description);

在这个例子中,我使用工厂方法来创建特定类型(例如PICASA)的抽象工厂。
这两种模式经常一起使用。

5
为什么不显式实例化PicasaWebGalleryFactory? - Rekin
3
为什么一定要是一个工厂呢?你不能创建一个WebGallery类和PicasaWebGallery子类吗? - paul

3
Zend_Db在其Zend_Db_Adapter类中使用它,以允许根据从配置对象传递的数据库设置创建不同类型的数据库对象。

其他工厂方法的例子包括:Zend_Cache::factory(),Zend_Memory::factory()。Zend_Log_Writer还具有用于初始化Zend_Log_Writer_Abstract的具体适配器的工厂方法。但需要注意的是:这些实现通常以工厂方法直接注入到产品中的形式存在(即没有单独的AbstractCreator/ConcreteCreator类)。 - Victor Farazdagi
刚刚重新检查了代码:ZF实际上使用的是所谓的简单工厂,这是一种惯用语而不是模式(如HF设计模式中所描述)。我还看到了一堆静态工厂方法,至少实现非常类似于Bloch在Effective Java中讨论的内容。我找不到GoF的工厂方法模式的确切实现。 - Victor Farazdagi

2

一个来自.NET 基类库(BCL)的例子是Control.CreateControlsInstance,它被很多其他(Windows Forms) Control类成员使用。

你可以重写这个受保护的方法,提供你自己的控件集合,例如当你正在实现一个自定义控件时。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接