¿Qué es un patrón de diseño de fábrica en PHP?

88

Esto me confunde, en los términos más simples, ¿qué hace? Imagina que le estás explicando a tu madre oa alguien casi por favor.

JasonDavis
fuente
115
Mi madre no lo entendería de todos modos ...
Bruno Reis
6
@JasonDavis Sigo respondiendo a tus preguntas ... Empiezo a sentirme como un acosador.
Tyler Carter

Respuestas:

175

Una fábrica crea un objeto. Entonces, si quisieras construir

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

No querrá depender de tener que hacer el siguiente código cada vez que cree el objeto

$obj = new ClassA(new ClassB, new Class C);

Ahí es donde entraría la fábrica. Definimos una fábrica que se encargue de eso por nosotros:

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

Ahora todo lo que tenemos que hacer es

$factory = new Factory;
$obj     = $factory->build();

La verdadera ventaja es cuando quieres cambiar de clase. Digamos que queríamos pasar en una ClassC diferente:

class Factory_New extends Factory{
    public function buildC(){
        return new ClassD;
    }
}

o una nueva ClassB:

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

Ahora podemos usar la herencia para modificar fácilmente cómo se crea la clase, para colocar un conjunto diferente de clases.

Un buen ejemplo podría ser esta clase de usuario:

class User{
    public $data;
    public function __construct($data)
    {
        $this->data = $data;
    }
}

En esta clase $dataestá la clase que usamos para almacenar nuestros datos. Ahora, para esta clase, digamos que usamos una sesión para almacenar nuestros datos. La fábrica se vería así:

class Factory{
    public function build()
    {
        $data = $this->buildData();
        return $this->buildUser($data);
    }

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

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

Ahora, digamos que queremos almacenar todos nuestros datos en la base de datos, es realmente simple cambiarlo:

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

Las fábricas son un patrón de diseño que usamos para controlar cómo juntamos los objetos, y el uso de patrones de fábrica correctos nos permite crear los objetos personalizados que necesitamos.

Tyler Carter
fuente
3
Eso fue mucho escribir. Ahora tendré que ponerlo en mi wiki en algún momento.
Tyler Carter
1
Agradable y útil. Me quito el sombrero ante ti amigo.
stefgosselin
1
¿Cuál es la diferencia / beneficio de su código $obj = $factory->build();más $obj = new whateverClass();? Además, en otra clase (digamos classZ) que depende de los datos de classA, ¿en qué parte de classZ usaría el método de fábrica? Básicamente, todavía está creando una instancia de una clase (classZ) dentro de una clase (classA), lo que significa que no hay pruebas. por ejemplo, la fábrica parece ser una carga de código para hacer a newtravés de un método en lugar de simplemente usar new.
James
19

Como una fábrica de la vida real, crea algo y lo devuelve.

Imagina algo como esto

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

o un método de fábrica

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

La implementación del método de fábrica creará una nueva instancia y la devolverá.

alex
fuente
1
Buen ejemplo, me sorprende lo variadas que son las implementaciones para este patrón. Cuando se llama estáticamente, supongo que se puede obtener una referencia a la instancia para reutilizar la misma instancia más adelante. es decir, $ joe = Joe :: Factory () -> say ('hola');
stefgosselin
ciertamente, como en 5.6, también se puede hacer (new Joe ()) -> say ('hola');
Pancho
12

El patrón de diseño de fábrica es muy bueno cuando se trata de varios recursos y desea implementar una abstracción de alto nivel.

Dividamos esto en una sección diferente.

Suponga que tiene que implementar la abstracción y que el usuario de su clase no necesita preocuparse por lo que ha implementado en la definición de la clase.

Él / ella solo necesita preocuparse por el uso de sus métodos de clase.

por ejemplo, tiene dos bases de datos para su proyecto

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

}

Su clase Factory se encargaría de la creación del objeto para la conexión a la base de datos.

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

}

El usuario solo necesita pasar el nombre del tipo de base de datos

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

Salida:

MySQL Database Connection
Your mysql select query execute here

En el futuro, es posible que tenga una base de datos diferente, entonces no necesita cambiar el código completo, solo necesita pasar el nuevo tipo de base de datos y el otro código se ejecutará sin realizar ningún cambio.

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

Salida:

Oracle Database Connection
Your oracle select query execute here

Espero que esto ayude.

Shailesh Sonare
fuente
1

En general, una "fábrica" ​​produce algo: en el caso de la programación orientada a objetos, un "patrón de diseño de fábrica" ​​produce objetos.

No importa si está en PHP, C # o cualquier otro lenguaje orientado a objetos.

Pindatjuh
fuente
1

El patrón de diseño de fábrica (patrón de fábrica) es para acoplamientos sueltos. Como el significado de fábrica, datos a una fábrica (datos de producción) al usuario final. De esta manera, la fábrica rompe el estrecho vínculo entre la fuente de datos y el proceso de datos.

N Zhang
fuente
0

Esta respuesta está en relación con otra publicación en la que Daniel White dijo que use factory para crear una conexión MySQL usando el patrón de fábrica.

Para la conexión MySQL, prefiero usar el patrón singleton, ya que desea usar la misma conexión para acceder a la base de datos, no crear otra.

Marcin - usuario2676388
fuente
0

El enfoque clásico para instanciar un objeto es:

$Object=new ClassName();

PHP tiene la capacidad de crear dinámicamente un objeto a partir del nombre de la variable utilizando la siguiente sintaxis:

$Object=new $classname;

donde la variable $ classname contiene el nombre de la clase que uno quiere instanciar.

Entonces, la factorización de objetos clásica se vería así:

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

y si llama a la función getInstance ('Producto'), esta fábrica creará y devolverá el objeto Producto. De lo contrario, si llama a la función getInstance ('Cliente'), esta fábrica creará y devolverá el objeto de tipo Cliente (creado a partir de la clase Cliente ()).

Ya no hay necesidad de eso, se puede enviar 'Producto' o 'Cliente' (nombres exactos de clases existentes) como un valor de variable para instanciación dinámica:

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

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

Para que conste, en palabras sencillas, una fábrica como @Pindatjuh dijo, devuelve un objeto.

Entonces, ¿cuál es la diferencia con un constructor? (que hace lo mismo)

  1. un constructor usa su propia instancia.
  2. Algo que quiero, algo más avanzado y no quiero inflar el objeto (o agregar dependencias).
  3. Se llama al constructor cuando se crea cada instancia. A veces no quieres eso.

    Por ejemplo, digamos que cada vez que creo un objeto de la clase Account, leo de la base de datos un archivo y lo uso como plantilla.

Usando el constructor:

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

Usando la fábrica:

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