He estado leyendo a través de Java efectivo por Joshua Bloch. También desarrollo en PHP y quería implementar el builder pattern outlined in item 2, pero PHP no tiene clases internas. ¿Hay alguna forma de lograr este patrón en PHP, manteniendo el constructor para el producto privado?Patrón PHP Builder sin clases internas
Respuesta
Desde PHP does not support inner classes, debe haber un método público en la clase de producto que crea una instancia de la misma. Tenga en cuenta las siguientes clases PHP:
<?php
class NutritionalFactsBuilder {
private $sodium;
private $fat;
private $carbo;
/**
* It is preferred to call NutritionalFacts::createBuilder
* to calling this constructor directly.
*/
function __construct($s) {
$this->sodium = $s;
}
function fat($f) {
$this->fat = $f;
return $this;
}
function carbo($c) {
$this->carbo = $c;
return $this;
}
function getSodium() {
return $this->sodium;
}
function getFat() {
return $this->fat;
}
function getCarbo() {
return $this->carbo;
}
function build() {
return new NutritionalFacts($this);
}
}
class NutritionalFacts {
private $sodium;
private $fat;
private $carbo;
static function createBuilder($s) {
return new NutritionalFactsBuilder($s);
}
/**
* It is preferred to call NutritionalFacts::createBuilder
* to calling this constructor directly.
*/
function __construct(NutritionalFactsBuilder $b) {
$this->sodium = $b->getSodium();
$this->fat = $b->getFat();
$this->carbo = $b->getCarbo();
}
}
echo '<pre>';
var_dump(NutritionalFacts::createBuilder(10)->fat(23)->carbo(1)->build());
echo '</pre>';
?>
Tenga en cuenta que en el ejemplo anterior el constructor de NutritionalFacts
es público. Sin embargo, debido a las limitaciones del lenguaje, tener un constructor público no es para nada malo. Dado que uno debe llamar al constructor con un NutritionalFactsBuilder
, solo hay un número limitado de formas de crear instancias de NutritionalFacts
. Vamos a comparar ellos:
// NutritionalFacts Instantiation #0
$nfb = new NutritionalFactsBuilder(10);
$nfb = $nfb->fat(23)->carbo(1);
$nf0 = new NutritionalFacts($nfb);
// NutritionalFacts Instantiation #1
$nfb = new NutritionalFactsBuilder(10);
$nf1 = $nfb->fat(23)->carbo(1)->build();
// NutritionalFacts Instantiation #2
$nf2 = NutritionalFacts::createBuilder(10)->fat(23)->carbo(1)->build();
// NutritionalFacts Instantiation #3
// $nf3 = (new NutritionalFactsBuilder(10))->fat(23)->carbo(1)->build();
para aprovechar la función de encadenamiento en toda su extensión, "NutritionalFacts
instanciación # 2" es el uso preferido.
"
Actualización: En PHP 5.4.0, ahora hay support for the syntax en "NutritionalFacts
Instantiation # 3" muestra otro matiz de la sintaxis de PHP;
one cannot chain a method on a newly instantiated object.
NutritionalFacts
Instantiation # 3". Aunque no lo he probado todavía.
Hacer el constructor privado
Se podría hacer que el constructor privado, pero yo no lo recomendaría. Si el constructor se hizo privado, sería necesario un método de fábrica público, estático, como en el siguiente fragmento de código. Si miramos el código siguiente, también podríamos hacer que el constructor sea público en lugar de introducir un direccionamiento indirecto solo para hacer que el constructor sea privado.
class NutritionalFacts {
private $sodium;
private $fat;
private $carbo;
static function createBuilder($s) {
return new NutritionalFactsBuilder($s);
}
static function createNutritionalFacts($builder) {
return new NutritionalFacts($builder);
}
private function __construct($b) {
$this->sodium = $b->getSodium();
$this->fat = $b->getFat();
$this->carbo = $b->getCarbo();
}
}
Esto es perfecto. Gracias, respondiste completamente mi pregunta. – Jackson
Además, acaba de notar que puede encadenar un método en un objeto recién instanciado en PHP 5.4: http://docs.php.net/manual/en/migration54.new-features.php – Jackson
@JacksonOwens Eso es increíble. Agregue lo que acaba de decir como respuesta a http://stackoverflow.com/questions/2188629. Creo que eso sería útil para otros. – creemama
En la descripción de Gang of Four del patrón Builder, no encontrará ningún requisito para una clase interna. La característica clave es la relación total entre el Director y la interfaz del Constructor que proporciona un "anteproyecto" para armar una serie de implementaciones del Producto.
Usted puede encontrar gran cantidad de ejemplos del patrón de PHP Constructor aquí:
http://www.php5dp.com/category/design-patterns/builder/
Cheers, Bill
inmutabilidad es buena y sin duda algo que esforzarse, esto se aplica a PHP, ya que hace a cualquier otro idioma no importa qué. La inmutabilidad te da certeza de que no tienes que temer que la instancia de repente muta sin que lo sepas.
Dicho esto, hay una manera fácil de implementar el patrón del generador para construir objetos inmutables incluso sin clases internas (aunque está disponible ahora con PHP 7).
El primer bloque de construcción importante es una clase base común para la clase real inmutable y el constructor. Esto les permite acceder a las propiedades de los demás. Algo que también se conoce como clases de amigos o solucionables a través de modificadores de acceso extendido en otros idiomas, algo que PHP no tiene.Tenga en cuenta que la capacidad de clonación está restringida, no tiene sentido clonar objetos inmutables, pero más sobre el modificador protected
más adelante.
abstract class NutritionalFactData {
protected $sodium = 0;
protected $fat = 0;
protected $carbo = 0;
protected function __clone() {}
}
La clase inmutable es directa con getters de ejemplo estúpidos y un constructor privado. Tenga en cuenta el modificador final
para la clase en sí y que no tiene conocimiento de la clase de constructor.
final class NutritionalFacts extends NutritionalFactData {
public function getSodium() {
return $this->sodium;
}
public function getFat() {
return $this->fat;
}
public function getCarbo() {
return $this->carbo;
}
private function __construct() {}
}
Ahora la implementación real del constructor. Observe cómo operamos directamente en una instancia de la clase inmutable y que simplemente la clonamos cuando se llama al método de compilación. Esto garantiza que las llamadas posteriores a los instaladores del generador no alterarán las instancias que se crearon previamente y asegura que ningún receptor de dicha instancia tenga que encargarse de la clonación por sí mismo.
final class NutritionalFactBuilder extends NutritionalFactData {
private $nutritional_facts;
public function __construct() {
$this->nutritional_facts = new NutritionalFacts;
}
public function build() {
return clone $this->nutritional_facts;
}
public function setSodium($sodium) {
$this->nutritional_facts->sodium = $sodium;
return $this;
}
public function setFat($fat) {
$this->nutritional_facts->fat = $fat;
return $this;
}
public function setCarbo($carbo) {
$this->nutritional_facts->carbo = $carbo;
return $this;
}
}
Para completar un ejemplo de uso:
var_dump(
(new NutritionalFactBuilder)
->setSodium(21)
->setFat(42)
->build()
);
Creo que es obvio que ahora podemos aplicar como muchas implementaciones constructor como nos gusta. No es realmente necesario para este ejemplo, pero podemos pensar en otros constructos donde están involucradas muchas más propiedades. Como el ejemplo del coche dado en (el muy malo) artículo del patrón de construcción de Wikipedia. Es posible que deseemos tener constructores preconfigurados para categorías de automóviles conocidas.
abstract class CarParts {}
final class Car extends CarParts {}
abstract class CarBuilder extends CarParts {
abstract public function build(): Car;
}
final class CompactCarBuilder extends CarBuilder {}
final class SportsCarBuilder extends CarBuilder {}
final class RaceCarBuilder extends CarBuilder {}
- 1. ¿Cómo uso las clases internas en PHP?
- 2. ¿Dónde poner clases internas?
- 3. Clases internas en Java
- 4. clases internas públicas
- 5. protected/public Clases internas
- 6. Patrón Jackson + Builder?
- 7. Clases internas: Android vs Java
- 8. ¿Cómo importar clases internas sin dependencia de ruta en Scala?
- 9. Clases internas anónimas en C#
- 10. clases internas estáticas en Scala
- 11. ¿Las clases internas son livianas?
- 12. Usar clases internas en C#
- 13. Lanzar excepciones comprobadas de clases internas anónimas
- 14. Ejemplos del mundo real del patrón Builder
- 15. clases internas anónimas para los botones
- 16. Obteniendo todas las clases internas por reflexión
- 17. Estándares para usar clases internas para GUI?
- 18. ¿Cuáles son las clases internas finales?
- 19. Intellij auto import para clases internas
- 20. Clases internas anónimas vs nombradas? - ¿mejores prácticas?
- 21. Constructores en clases internas (implementando interfaces)
- 22. Refactor utilizar Builder o patrón constructor telescópica
- 23. Patrón de Java Builder y una jerarquía de objetos "profunda"
- 24. Exponer clases internas cuando se ofusca con ProGuard
- 25. Las clases internas estáticas necesitan importación para las anotaciones
- 26. ¿Las clases internas de Java representan un riesgo de seguridad?
- 27. ¿Por qué las clases internas no pueden declarar miembros estáticos?
- 28. Ejemplo de clases internas utilizadas como alternativa a las interfaces
- 29. Eclipse RCP: ¿Cómo acceder a las clases internas de complementos?
- 30. Ámbito privado del objeto Scala con clases internas y métodos
Si bien la pregunta tiene una solución, el valor de dicha implementación se reduce en gran medida. PHP no se basa en la inmutabilidad, por lo que la única ventaja serían los métodos encadenados. El uso del patrón JavaBeans es la solución aceptada en PHP. – danidacar