Ich habe durch Effektive Java von Joshua Bloch gelesen. Ich entwickle auch in PHP und wollte die builder pattern outlined in item 2 implementieren, aber PHP hat keine inneren Klassen. Gibt es eine Möglichkeit, dieses Muster in PHP zu erreichen und den Konstruktor für das Produkt privat zu halten?PHP Builder Muster ohne innere Klassen
Antwort
Seit PHP does not support inner classes muss eine öffentliche Methode für die Produktklasse vorhanden sein, die eine Instanz davon erstellt. Betrachten Sie die folgenden PHP-Klassen:
<?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>';
?>
Beachten Sie, dass in dem obigen Beispiel der Konstruktor von NutritionalFacts
öffentlich ist. Aufgrund der Einschränkungen der Sprache ist es jedoch nicht schlecht, einen öffentlichen Konstruktor zu haben. Da man den Konstruktor mit einer NutritionalFactsBuilder
aufrufen muss, gibt es nur eine begrenzte Anzahl von Möglichkeiten, NutritionalFacts
zu instanziieren. Vergleichen wir sie:
// 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();
Funktion zu nutzen, in vollem Umfang Chaining „NutritionalFacts
Instantiierung # 2“ ist die bevorzugte Verwendung.
"
Update: In PHP 5.4.0 gibt es jetzt support for the syntax in "NutritionalFacts
Instantiierung # 3" zeigt eine andere Nuance der PHP-Syntax;
one cannot chain a method on a newly instantiated object.
NutritionalFacts
Instanziierung # 3." Ich habe es aber noch nicht getestet.
Making the Constructor Privat
Sie könnten der Konstruktor privat machen, aber ich würde es nicht empfehlen. Wenn der Konstruktor privat gemacht würde, wäre eine öffentliche, statische Factory-Methode erforderlich, wie im folgenden Code-Snippet. Wenn wir uns den folgenden Code ansehen, können wir den Konstruktor auch öffentlich machen, anstatt eine Indirektion einzuführen, nur um den Konstruktor privat zu machen.
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();
}
}
Das ist perfekt. Danke, du hast meine Frage vollständig beantwortet. – Jackson
Auch, gerade bemerkt, dass Sie eine Methode auf einem neu instanziierten Objekt in PHP 5.4 verketten können: http://docs.php.net/manual/en/migration54.new-features.php – Jackson
@JacksonOwens Das ist genial. Fügen Sie das, was Sie gerade gesagt haben, als Antwort auf http://stackoverflow.com/questions/2188629 hinzu. Ich denke, das wäre für andere nützlich. – creemama
In der Beschreibung der Gang of Four des Builder-Musters finden Sie keine Anforderung für eine innere Klasse. Das Schlüsselmerkmal ist die Aggregatbeziehung zwischen der Director- und der Builder-Schnittstelle, die einen "Blueprint" zum Zusammenstellen einer Reihe von Produktimplementierungen bereitstellt.
Sie können viele Beispiele für die Muster PHP Builder finden Sie hier:
http://www.php5dp.com/category/design-patterns/builder/
Cheers, Bill
Unveränderlichkeit ist gut und auf jeden Fall etwas zu streben, gilt dies als es PHP tut zu jeder anderen Sprache, egal was. Unveränderlichkeit gibt Ihnen Gewissheit, dass Sie nicht befürchten müssen, dass die Instanz plötzlich mutiert, ohne dass Sie es wissen.
Es gibt eine einfache Möglichkeit, das Builder-Muster zu implementieren, um unveränderliche Objekte auch ohne innere Klassen zu erstellen (obwohl jetzt mit PHP 7 verfügbar).
Der erste wichtige Baustein ist eine gemeinsame Basisklasse für die eigentliche unveränderliche Klasse und den Builder. Dies ermöglicht ihnen, auf die anderen Eigenschaften zuzugreifen. Etwas, das auch als Freundesklassen oder durch erweiterte Zugriffsmodifizierer in anderen Sprachen lösbar ist, etwas, das PHP nicht hat.Beachten Sie, dass die Klon-Fähigkeit eingeschränkt ist. Es macht keinen Sinn, unveränderliche Objekte zu klonen, sondern später mehr über den Modifikator protected
.
abstract class NutritionalFactData {
protected $sodium = 0;
protected $fat = 0;
protected $carbo = 0;
protected function __clone() {}
}
Die unveränderliche Klasse ist geradlinig mit dummen Beispielgettern und einem privaten Konstruktor. Beachten Sie den Modifizierer final
für die Klasse selbst und dass die Builder-Klasse überhaupt nicht bekannt ist.
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() {}
}
Jetzt die eigentliche Builder-Implementierung. Beachten Sie, wie wir direkt auf einer Instanz der unveränderlichen Klasse arbeiten und dass wir sie einfach klonen, wenn die Build-Methode aufgerufen wird. Dies stellt sicher, dass spätere Aufrufe an die Setter des Builders die zuvor erstellten Instanzen nicht ändern und sicherstellen, dass kein Empfänger einer solchen Instanz sich selbst um das Klonen kümmern muss.
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;
}
}
Für Vollständigkeit ein Anwendungsbeispiel:
var_dump(
(new NutritionalFactBuilder)
->setSodium(21)
->setFat(42)
->build()
);
Ich denke, es ist offensichtlich, dass wir jetzt so viele Builder Implementierungen umsetzen können, wie wir wollen. Für dieses Beispiel wird es nicht wirklich benötigt, aber wir können uns andere Konstrukte vorstellen, bei denen viel mehr Eigenschaften beteiligt sind. Wie das Auto-Beispiel, das auf dem (sehr schlechten) Erbauermusterartikel von Wikipedia gegeben wird. Wir möchten vielleicht vorkonfigurierte Builder für bekannte Fahrzeugkategorien haben.
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. Wie verwende ich innere Klassen in PHP?
- 2. Dozer Mapping innere Klassen
- 3. Builder-Muster mit Vererbung
- 4. Weniger ausführliche Builder-Muster?
- 5. Builder-Muster vs. Konfigurationsobjekt
- 6. Builder-Muster, aber Builder in Objekten Konstruktor
- 7. Innere Klassen Vererbung in Kotlin
- 8. Warum verwenden wir innere Klassen?
- 9. Innere Klassen: Android vs Java
- 10. Java innere Klassen Methode Zugriff
- 11. anonyme innere Klassen Innerhalb Methoden
- 12. C++ Typname und innere Klassen
- 13. Java innere Klassen in C#
- 14. Innere Klassen als Quelldaten-Repositories
- 15. anonyme innere Klassen für Tasten
- 16. Statische innere Klassen in scala
- 17. create Query Builder symfony innere Verbindung
- 18. Builder-Muster mit verschachtelten Objekten
- 19. Aktion Listener und innere Klassen java
- 20. Java innere Klassen Dateinamen zu lang
- 21. Best Practice für innere/anonyme Klassen
- 22. Generische typisierte innere Klassen in Java
- 23. Generische Art von Enum & Builder-Muster
- 24. Wie Generika in Builder-Muster verwenden
- 25. Ersetzt das Builder-Muster das Werksmuster?
- 26. Builder-Muster, Vorlagen und verschachtelte Klasse
- 27. "dynamisches" Schlüsselwort mit Builder-Muster verbirgt Erweiterungsmethode
- 28. Java Generics Typ DSL mit Builder Muster
- 29. Builder-Muster: Welche Variante ist bevorzugt?
- 30. Java Builder-Muster mit generischen Typgrenzen
Während die Frage eine Lösung hat, ist der Wert einer solchen Implementierung stark reduziert. PHP ist nicht um Unveränderlichkeit herum aufgebaut, so dass der einzige Vorteil die verketteten Methoden wären. Die Verwendung des JavaBeans-Musters ist die akzeptierte Lösung in PHP. – danidacar