PHP Patterns: Proxy Pattern – JSON Beispiel

Das Proxy-Pattern kontrolliert den Zugriff auf ein Object mit Hilfe eines Stellvertreters, der an Stelle des eigentlichen Objekts verwendet wird.

Hier wieder ein praktisches Beispiel aus der realen Welt:

Wir haben eine Produkt Klasse mit sehr vielen Eigenschaften und Methoden, die komplette Business-Logik ist abgebildet. Nun haben wir folgendes Szenario. In der Oberfläche möchte wir via AJAX eine Produktliste nachladen. Vom Server soll ein Array mit Produkt Objekten im JSON Format kommen.

Hierzu könnte man jetzt eine SQL Query schreiben, evtl. mit stdObject hantieren um sich ein CustomProdukt Object zusammen zu bauen und danach ein json_encode fahren.

Wir lösen das aber etwas eleganter.

Produkt Klasse (stark verkürzt)

class Product  implements Product_Interface {
 
	protected $title,
	protected $description;
	protected $price;
	protected $variants;
	// etc..
 
	public function __construct($title) {
		$this->setTitle($title);
	}
 
	public function setTitle($title) {
		if($this->title !== $title) {
			$this->title = $title;
		}
	}
	public function setDescription($description) {
		if($this->description !== $description) {
			$this->description = $description;
		}
	}
	public function setPrice($price) {
		if($this->price !== $price) {
			$this->price = $price;
		}
	}
	public function getTitle() {
		return $this->title;
	}
	public function getDescription() {
		return $this->description;
	}
	public function getPrice() {
		return $this->price;
	}
	public function getAccessories() {
		// Produkt Zubehör
	}
 
	 public function getVariants() {
		// Produkt Varianten
	}
}

Die Produkt Klasse implementiert alle Methoden aus der Product_Interface Schnittstelle. Encodieren wir jetzt eine Product Instanz in JSON bekommen wir einen leeren JSON strin zurück „{}“

$product = new Product("test");
$product->setPrice(3.99);
echo json_encode($product);

Das liegt daran das die Eigenschaften protected sind und somit beim json_encode nicht beachtet werden.
Wir Lösen das Problem indem wir eine art Proxy für das Product Object schaffen. Zuerst benötigen wir eine Abstrakte Product_Proxy Basisklasse die alle Methoden an die Product Klasse weiterreicht. Im Konstruktor übergeben wir eine Instanz vom Product_Interface. Dazu später mehr.

abstract class Product_Proxy implements Product_Interface {
 
	protected $product;
 
	public function __construct(Product_Interface $product) {
		$this->product = $product;
	}
	public function setTitle($title) {
		return $this->product->setTitle($title);
	}
	public function setDescription($description) {
		return $this->product->setDescription($description);
	}
	public function setPrice($description) {
		return $this->product->setPrice($description);
	}
	public function getTitle() {
		return $this->product->getTitle();
	}
	public function getDescription() {
		return $this->product->getDescription();
	}
	public function getPrice() {
		return $this->product->getPrice();
	}
	public function getAccessories() {
		return $this->product->getAccessories();
	}
 
	 public function getVariants() {
		return $this->product->getVariants();
	}
}

Aus dieser Basisklasse können nun weitere Proxies für JSON, SOAP, XML-RPC etc. entstehen.
Die konrete JSON_Proxy Implementierung sieht wie folgt aus:

class Product_Proxy_JSON extends Product_Proxy  {
 
	public $title;
	public $price;
	public $description;
 
	public function __construct(Product_Interface $product) {
		$this->title 			= $product->getTitle();
		$this->price			= $product->getPrice();
		$this->description		= $product->getDescription();
 
	}
}

Nun sind die benötigen Eigenschaften public und json_encode() kann endlich damit etwas anfangen. Der Einsatz sieht nun wie folgt aus.

$product = new Product('Test');
$product->setDescription('Das ist ein Test');
$product->setPrice(3.99);
 
$jsonProxy = new Product_Proxy_JSON($product);
echo json_encode($jsonProxy);

Wr haben es geschafft, ohne die Product Klasse zu verändern, ein JSON String zu bekommen.
important

Leider bleibt eine solche Anwendung nicht ohne Konsequenzen. Durch das Ummanteln von Objekten wird die Applikation zusätzlich verlangsamt und verbraucht mehr Speicher. Außerdem verliert man die konkreten Typen. Würde die Product_Proxy Klasse nicht die Product_Interface Schnittstelle implemntieren, dann könnten wir uns auf Product_Proxy_JSON nicht mehr verlassen. Darum, wenn ein Proxy eingesetzt wird, immer gegen eine Schnittstelle statt gegen konkrete Implementierung programmieren.

Leave a comment

Your comment