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


88

用最简单的话来说,这让我感到困惑。假装您几乎在向母亲或某人解释。


115
我的母亲无论如何也不会理解...
Bruno Reis 2010年

6
@JasonDavis我一直在回答您的问题...我开始觉得自己像个跟踪者。
泰勒·卡特

Answers:


175

工厂创建一个对象。所以,如果你想建立

 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();
    }
}

工厂是一种设计模式,我们可以用来控制如何将对象组合在一起,而使用正确的工厂模式可以使我们创建所需的定制对象。


3
打字很多。现在,我将不得不将其放在我的Wiki上。
泰勒·卡特

1
很好,乐于助人。向您致敬。
stefgosselin 2011年

1
您的代码$obj = $factory->build();$obj = new whateverClass();什么不同/好处?另外,在另一个依赖于classA数据的类(例如classZ)中,您将在classZ中的工厂方法中使用什么位置?从本质上讲,您仍在实例化类(classA)中的类(classZ),这意味着无需进行测试。例如工厂似乎是new通过方法而不是仅使用方法来加载的代码new
詹姆斯

19

就像现实生活中的工厂一样,它创建并返回东西。

想象这样的事情

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

或工厂方法

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

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


1
很好的例子,令我惊讶的是,这种模式的实现有多么多样化。静态调用时,我假设有人可以引用该实例,以便以后再使用同一实例吗?即$ joe = Joe :: Factory()-> say('hello');
stefgosselin 2011年

当然从5.6开始,还可以做(new Joe())-> say('hello');
Pancho

12

当您处理多种资源并想要实现高级抽象时,工厂设计模式非常好。

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

假设您必须实现抽象,并且类的用户不需要关心在类定义中实现的内容。

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

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

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;
        }

}

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

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

通常,“工厂”会产生一些东西:在面向对象编程的情况下,“工厂设计模式”会产生对象。

使用PHP,C#或任何其他面向对象的语言都没关系。


1

工厂设计模式(Factory Pattern)用于松耦合。就像工厂的含义一样,数据到工厂(产生数据)给最终用户。通过这种方式,工厂打破了数据源与数据过程之间的紧密联系。



0

这个答案与其他帖子有关,丹尼尔·怀特(Daniel White)曾说过使用工厂使用工厂模式创建MySQL连接。

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


0

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

$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类型对象(从Customer()类创建)。

不再需要,可以将“产品”或“客户”(现有类的确切名称)作为变量的值发送给动态实例化:

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

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

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;
      }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.