PHP中的工厂设计模式是什么?

116

这让我感到困惑,用最简单的语言来说它是做什么的?请假设你正在向你的母亲或者一个几乎不懂技术的人解释。


131
我妈妈肯定不会理解的… - Bruno Reis
6
@JasonDavis,我不停地回答你的问题...我开始觉得自己像个跟踪狂。 - Tyler Carter
你订购一辆汽车,它组装好后送到你的门口,而组装工作是在某个工厂里完成的。在编程中,当你实例化一个工厂时,它应该提供功能,而不需要实例化内部具体功能。 - devasghar
11个回答

198

一个工厂创建一个对象。因此,如果您想构建

 class A{
    public $classb;
    public $classc;
    public function __construct($classb, $classc)
    {
         $this->classb = $classb;
         $this->classc = $classc;
    }
  }

您不希望每次创建对象时都要依赖于执行以下代码。
$obj = new ClassA(new ClassB, new Class C);

这就是工厂所发挥的作用。我们定义一个工厂来为我们处理:

class Factory{
    public function build()
    {
        $classc = $this->buildC();
        $classb = $this->buildB();
        return $this->buildA($classb, $classc);

    }

    public function buildA($classb, $classc)
    {
        return new ClassA($classb, $classc);
    }

    public function buildB()
    {
        return new ClassB;
    }

    public function buildC()
    {
        return new ClassC;
    }
}

现在我们要做的就是:
$factory = new Factory;
$obj     = $factory->build();

真正的优势在于你想要改变类时。假如我们想传入一个不同的ClassC:
class Factory_New extends Factory{
    public function buildC(){
        return new ClassD;
    }
}

或者创建一个新的ClassB:

class Factory_New2 extends Factory{
    public function buildB(){
        return new ClassE;
    }
}

现在我们可以使用继承来轻松修改类的创建方式,以放置不同的类集合。
一个很好的例子可能是这个用户类:
class User{
    public $data;
    public function __construct($data)
    {
        $this->data = $data;
    }
}

在这个类中,$data 是我们用来存储数据的类。如果我们使用 Session 来存储数据,那么工厂会像这样:
class Factory{
    public function build()
    {
        $data = $this->buildData();
        return $this->buildUser($data);
    }

    public function buildData()
    {
        return SessionObject();
    }

    public function buildUser($data)
    {
        return User($data);
    }
}

假设我们想把所有数据都存储在数据库中,更改起来非常简单:

class Factory_New extends Factory{
    public function buildData()
    {
        return DatabaseObject();
    }
}

工厂模式是一种设计模式,我们用它来控制如何组合对象,使用正确的工厂模式允许我们创建需要的定制化对象。


4
打这么多字,现在我得把它放到我的维基上。 - Tyler Carter
2
不错,很有帮助。向你致敬,伙计。 - Stephane Gosselin
1
你的代码 $obj = $factory->build(); 相对于 $obj = new whateverClass(); 有什么不同/好处?另外,在另一个依赖于 classA 数据的类(比如 classZ)中,你会在哪里使用工厂方法?实际上你仍然是在一个类(classA)中实例化另一个类(classZ),这意味着没有测试。例如,工厂似乎只是通过方法执行 new 而不是直接使用 new 的一堆代码。 - James
这对我来说很困惑。我已经阅读了一个小时关于工厂和构建器的内容,这似乎更像是一个构建器,而不是工厂。 - akinuri

49

工厂设计模式非常适合处理多个资源并实现高级抽象。

让我们将其分成不同的部分。

假设您必须实现抽象化,而类的用户不需要关心您在类定义中实现了什么。

他/她只需担心类方法的使用。

例如,您的项目有两个数据库。

class MySQLConn {

        public function __construct() {
                echo "MySQL Database Connection" . PHP_EOL;
        }

        public function select() {
                echo "Your mysql select query execute here" . PHP_EOL;
        }

}

class OracleConn {

        public function __construct() {
                echo "Oracle Database Connection" . PHP_EOL;
        }

        public function select() {
                echo "Your oracle select query execute here" . PHP_EOL;
        }

}

您的工厂类将负责创建数据库连接对象。

class DBFactory {

        public static function getConn($dbtype) {

                switch($dbtype) {
                        case "MySQL":
                                $dbobj = new MySQLConn();
                                break;
                        case "Oracle":
                                $dbobj = new OracleConn();
                                break;
                        default:
                                $dbobj = new MySQLConn();
                                break;
                }

                return $dbobj;
        }

}

用户只需传递数据库类型的名称

$dbconn1 = DBFactory::getConn("MySQL");
$dbconn1->select();

输出:

MySQL Database Connection
Your mysql select query execute here

未来你可能会有不同的数据库,但你不需要更改整个代码,只需传递新的数据库类型,其他代码将继续运行而不进行任何更改。

$dbconn2 = DBFactory::getConn("Oracle");
$dbconn2->select();

输出:

Oracle Database Connection
Your oracle select query execute here

希望这能有所帮助。


1
我认为这是最好的解决方案。 - keroles Monsef
我非常容易理解的好例子。谢谢您。 - schenker

21

就像一个真实的工厂,它会创建某些东西并返回它。

想象一下这样的场景:

$joe = new Joe();
$joe->say('hello');

或者一个工厂方法

Joe::Factory()->say('hello');

工厂方法的实现将创建一个新实例并返回它。


1
不错的例子,让我惊讶的是这种模式的实现方式如此多样化。当以静态方式调用时,我假设可以获取到实例的引用,以便稍后重复使用同一实例?例如 $joe = Joe::Factory()->say('hello'); - Stephane Gosselin
当然,在5.6中,你也可以这样做:(new Joe())->say('hello'); - Pancho

3
经典的实例化对象方法是:

$Object=new ClassName();

PHP具有使用以下语法从变量名动态创建对象的能力:

$Object=new $classname;

变量$classname包含要实例化的类名。

因此,经典的对象工厂看起来像:

function getInstance($classname)
{
  if($classname==='Customer')
  {
    $Object=new Customer();
  }
  elseif($classname==='Product')
  {
    $Object=new Product();
  }
  return $Object;
}

如果您调用getInstance('Product')函数,则该工厂将创建并返回Product对象。否则,如果您调用getInstance('Customer')函数,则该工厂将创建并返回客户类型对象(由Customer()类创建)。

现在不再需要这样做,可以将“Product”或“Customer”(现有类的确切名称)作为变量值发送以进行动态实例化:

$classname='Product';
$Object1=new $classname; //this will instantiate new Product()

$classname='Customer';
$Object2=new $classname; //this will instantiate new Customer()

1
通常,“工厂”生产某种东西:在面向对象编程的情况下,“工厂设计模式”会生成对象。
无论是使用PHP、C#还是任何其他面向对象语言都没有关系。

1

工厂设计模式(Factory Pattern)是为了实现松耦合。就像工厂的意义一样,数据被送到工厂(生产数据),最终用户使用。通过这种方式,工厂打破了数据源和数据处理之间的紧耦合。


0

0

这个回答是针对另一篇帖子的,其中Daniel White建议使用工厂模式来创建MySQL连接。

对于MySQL连接,我更倾向于使用单例模式,因为您想要使用相同的连接来访问数据库而不是创建另一个连接。


0

工厂是一种解决应用程序代码易于维护和可扩展性的解决方案。它解决了SOLID建议我们遵循的代码耦合问题。本质上,它是简化结构的构建/创建的一种方式。它可以是对象、函数或数组。

只要您将使用结构的代码与创建它们的代码分离,并返回对象/结构,您就正在使用工厂,其中可以是以下四个之一:

  • 工厂方法
  • 抽象工厂
  • 静态工厂
  • 简单工厂

主要区别在于工厂方法和抽象工厂。

一个从定义良好的元素列表生成对象/结构,另一个使用自己的制造策略生成对象的蓝图。

一个生成具体元素,另一个生成类型。


0

就记录而言,简单地说,像@Pindatjuh所说的工厂会返回一个对象。

那么,与构造函数有什么区别呢?(二者做的事情相同)

  1. 构造函数使用自己的实例。
  2. 我想要做一些更高级的事情,但我不想让对象变得臃肿(或添加依赖关系)。
  3. 每创建一个实例时都会调用构造函数。 有时您不希望如此。

    例如,假设每次我创建一个类Account的对象时,我都会从数据库中读取文件并将其用作模板。

使用构造函数:

class Account {
      var $user;
      var $pwd;
      var ...
      public __construct() {
         // here i read from the file
         // and many other stuff
      }
}

使用工厂:

class Account {
      var $user;
      var $pwd;
      var ...
}
class AccountFactory {
      public static Create() {
         $obj=new Account();
         // here we read the file and more stuff.
         return $obj;
      }

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