<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ebene7 &#187; Strategy Pattern</title>
	<atom:link href="http://blog.ebene7.com/schlagwort/strategy-pattern/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.ebene7.com</link>
	<description></description>
	<lastBuildDate>Tue, 04 Jun 2013 18:57:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Weniger Code-Redundanz durch Design Patterns</title>
		<link>https://blog.ebene7.com/2010/08/27/weniger-code-redundanz-durch-design-patterns/</link>
		<comments>https://blog.ebene7.com/2010/08/27/weniger-code-redundanz-durch-design-patterns/#comments</comments>
		<pubDate>Fri, 27 Aug 2010 05:00:27 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Qualitätssicherung]]></category>
		<category><![CDATA[CustomLibrary]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Entwurfsmuster]]></category>
		<category><![CDATA[Factory]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[Strategy Pattern]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=1287</guid>
		<description><![CDATA[Heute habe ich eine Artikel für euch, der mir besonders gefällt, weil er auf einfache Weise mehrere interessante Themen vereint. Und, wie sollte es auch anders sein, geht es wieder um unsere Models, um Entwurfsmuster und den Weltfrieden. *hust* Bevor &#8230; <a href="https://blog.ebene7.com/2010/08/27/weniger-code-redundanz-durch-design-patterns/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Heute habe ich eine Artikel für euch, der mir besonders gefällt, weil er auf einfache Weise mehrere interessante Themen vereint. Und, wie sollte es auch anders sein, geht es wieder um unsere Models, um Entwurfsmuster und den Weltfrieden. *hust*</p>
<p>Bevor ich aber nun der Miss World 2010 die Scherpe streitig mache, fange ich lieber mal an&#8230;</p>
<p><span id="more-1287"></span>Stellen wir uns mal vor, wir haben ein Meta-Model, welches verschiedene allgemeine Daten zu einem anderen Objekt speichern kann.</p>
<pre>&lt;?php
class MetaModel extends Custom_Model
{
  protected $_ownerId;
  protected $_creatorId;
  protected $_modifierId;
  protected $_createDate;
  protected $_modifyDate;

  // Weitere Felder und Getter/Setter
}</pre>
<p>Die ersten drei Felder sind jeweils UserIds und die anderen zwei Datumswerte, die wir über gewöhnliche Getter und Setter verändern können. Damit können wir schonmal mit den Daten arbeiten, jedoch leider nicht objektorientiert navigieren.</p>
<p>Mit Navigation meine ich einen einfachen und für Menschen leicht lesbaren Zugriff auf die Objekte. Im Prinzip nur eine einfache Methodenverkettung.</p>
<pre>&lt;?php
$ownerName = $object-&gt;getMeta()-&gt;getOwner()-&gt;getName();</pre>
<p>Anders herum will ich vielleicht auch die Möglichkeit haben ein Datum als String oder als Objekt zu setzen und will ganz sicher nicht in jedem einzelnen Setter die Unterscheidung implementieren.</p>
<p>Die Lösung ist denkbar einfach. Wir lagern den benötigten Code einfach aus! Wer sich etwas mit Design Patterns beschäftigt, der erkennt sicher das <a href="http://de.wikipedia.org/wiki/Strategy_Pattern" target="_blank">Strategy Pattern</a> wieder.</p>
<p>Als erstes definieren wir eine einfache Schnittstelle und bauen uns dannach einen &#8220;Accessor&#8221;.</p>
<pre>&lt;?php
interface Custom_Model_Access_Interface
{
  public function get($value);
  public function set($value);
}</pre>
<pre>&lt;?php
class Custom_Model_Access_User implements Custom_Model_Access_Interface
{
  /**
   * @param int $value
   * @return UserModel
  public function get($value)
  {
    // UserModel laden ($value==ID)
  }

  /**
   * @param mixed int|UserModel
   * @return int (UserId)
   */
  public function set($value)
  {
    // $value-&gt;getId() zurückgeben, wenn $value ein UserModel ist, sonst $value
  }
}</pre>
<p>Damit haben wir, wenn auch stark vereinfacht, die Logik ausgelagert und können sie nun in der Modelklasse nutzen. Für viele dieser Access-Objekte reicht eine Instanz aus, die wir am besten über eine einfache <a href="http://de.wikipedia.org/wiki/Factory_Pattern" target="_blank">Factory</a> erzeugen.</p>
<p>In unserem Model sieht das dann wie folgt aus.</p>
<pre>&lt;?php
class MetaModel extends Custom_Model
{
  // ...
  private $_accessUser;

  public function init()
  {
    $this-&gt;_accessUser = Custom_Model_Access_Factory::singleton('User');
  }

  public function getOwner()
  {
    return $this-&gt;_accessUser-&gt;get( $this-&gt;getOwnerId() );
  }

  public function setOwner($owner)
  {
    return $this-&gt;setOwnerId( $this-&gt;_accessUser-&gt;set($owner) );
  }
}</pre>
<p>Nach diesem Muster würden nun auch die Methoden für creator und modifier geschrieben werden. An dieser Stelle wäre auch ein einfacher Cache möglich, damit das Objekt nicht bei jedem Aufruf erneut instanziiert werden muss.</p>
<p>Das Access-Objekt für das Datum könnte unter Umständen etwas umfangreicher werden.</p>
<p>Der Getter sollte uns ein Datums-Objekt zurückgeben anhand des in der Datenbank (oder wo auch immer) gespeicherten Werten, egal in welchem Format dieser vorliegt.</p>
<p>Der Setter auf der andern Seite könnte z.B. sämtlichen gängigen Datums-Objekte und Stringformate kennen und diese in das übliche Y-m-d H:i:s-Format übersetzen.</p>
<p>Zusammenfassend lässt sich festhalten, dass sich diese Technik für alle möglichen Bereiche geeignet ist, wenn man Objekte zu den Ids haben möchte oder auch Formate umwandeln will oder muss (Datum, IP-Adressen etc.).</p>
<p>Wenn euch noch etwas sinnvolles zur Verwendung einfällt oder einfach noch Fragen oder Kritik offen sind, dann freue ich mich über Kommentare.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/08/27/weniger-code-redundanz-durch-design-patterns/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Implementieren einer einfachen Plugin-Schnittstelle</title>
		<link>https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/</link>
		<comments>https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/#comments</comments>
		<pubDate>Wed, 12 May 2010 05:00:26 +0000</pubDate>
		<dc:creator>Daniel</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Custom_Model]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Entwurfsmuster]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[Magic Methods]]></category>
		<category><![CDATA[Model]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[SPL]]></category>
		<category><![CDATA[Strategy Pattern]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=766</guid>
		<description><![CDATA[Heute geht es darum, wie wir uns eine einfache Plugin-Schnittstelle für unsere Objekte bauen können. Als Beispiel, wie sollte es auch anders sein, eignet sich unsere Custom_Model-Klasse aus meinen vergangenen Artikeln. Und schon wieder die Frage, was kann unsere Klasse &#8230; <a href="https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Heute geht es darum, wie wir uns eine einfache Plugin-Schnittstelle für unsere Objekte bauen können. Als Beispiel, wie sollte es auch anders sein, eignet sich unsere Custom_Model-Klasse aus meinen <a href="http://blog.ebene7.com/2010/04/29/es-ist-ein-model-und-es-sieht-gut-aus/" target="_self">vergangenen Artikeln</a>.</p>
<p><span id="more-766"></span>Und schon wieder die Frage, was kann unsere Klasse dann mehr? Oder warum machen wir das?</p>
<p>Oftmals haben wir Objekte, die eine gewisse Menge identischer Methoden benötigen, jedoch nicht aus einer Vererbungslinie stammen.</p>
<p>Durch die Plugins können wir dann quasi Funktionen an unsere Objekte dran kleben und nutzen. Diesen Code müssen wir nur einmal schreiben, haben auch nur eine Stelle im Code zu pflegen und können dadurch sicherstellen, dass alles gleich funktioniert.</p>
<p>So erweitern wir nun also zuerst wie gewohnt unser <code>Custom_Model</code> um ein paar neue Methoden und Eigenschaften.</p>
<pre>&lt;?php
class Custom_Model implements Custom_Model_Interface
{
  // ...
  protected $_plugins;

  public function registerPlugin() {}
  public function unregisterPlugin() {}
  // ...
}</pre>
<p>Und das Interface für die Plugins.</p>
<pre>&lt;?php
interface Custom_Model_Plugin_Interface {}</pre>
<p>Beim Speichern der Plugins kommt wieder die Klasse <a href="http://de3.php.net/manual/de/class.splobjectstorage.php" target="_blank">SplObjectStorage</a> zum Einsatz, welche wir ja schon bei der <a href="http://blog.ebene7.com/2010/05/07/es-ist-ein-model-und-es-sieht-gut-aus-der-observer/" target="_self">Implementierung des Obeservers</a> verwendet haben. Demnach ist es auch nicht überraschend, dass die Registrierung an die Abmeldung der Plugins sehr einfach wird.</p>
<pre>public function registerPlugin(Custom_Model_Plugin_Interface $plugin)
{
  $this-&gt;_plugins-&gt;attach($plugin);
  return $this;
}

public function unregisterPlugin(Custom_Model_Plugin_Interface $plugin)
{
  $this-&gt;_plugins-&gt;detach($plugin);
  return $this;
}</pre>
<p>Die Models müssen das Interface <code>Custom_Model_Plugin_Interface</code> implementieren. Dieses Interface ist lediglich ein Flag- oder Marker-Interface, d.h. es schreibt keine Methoden vor.</p>
<p>Jetzt noch eine kleine Änderung an der Methode <code>__call()</code> und wir können schon mit dem Beispiel experimentieren.</p>
<pre>public function __call($method, $args)
{
  // Magie für die Getter &amp; Setter

  foreach($this-&gt;_plugins as $plugin) {
    if(method_exists($plugin, $method)) {
      array_unshift($args, $this);
      return call_user_func_array(array($plugin, $method), $args);
    }
  }

  // Fehlerbehandlung
}</pre>
<p>Durch <code>__call()</code> können wir unsere Plugin-Methoden so nutzen, als gehörten sie direkt zum Objekt. Die Art wie die Plugins verwendet werden  hat Vor-, aber auch Nachteile.</p>
<p>Die Liste der Plugins wird der Reihe nach durchlaufen und die erstbeste passende Methode wird aufgerufen. Auch die Getter oder Setter könnten wir nicht mehr überschreiben. Wenn das alles jedoch gewollt ist, dann lässt sich das aber auch mit wenigen Handgriffen erweitern.</p>
<p>Nun aber mal was praktisches zum Anschauen: Angenommen wir haben eine Model-Klasse mit Abhängigkeit zu anderen Objekten.</p>
<pre>&lt;?php
class Book extends Custom_Model
{
  protected $_id;
  protected $_title;
  protected $_ownerId;
}</pre>
<p>Nun wollen wir nicht nur die ID des Besitzers erfahren, sondern auch das Objekt das bekommen können. Dafür spendieren wir ein Plugin.</p>
<pre>&lt;?php
class GetOwnerPlugin implements Custom_Model_Plugin_Interface
{
  public function getOwner($sender)
  {
    $id = $sender-&gt;getOwnerId();

    // lade User-Objekt $owner anhand der $id

    return $owner;
  }
}</pre>
<p>Auf die Fehlerbehandlung habe ich an der Stelle mal verzichtet, sollte jedoch unbedingt stattfinden.</p>
<pre>&lt;?php
$book = new Book();
$book-&gt;registerPlugin(new GetOwnerPlugin());
$book-&gt;setOwnerId(42);
$owner = $book-&gt;getOwner();</pre>
<p>Spätestens hier fällt sicher nicht nur mir auf, dass das Konzept noch Lücken hat. Theoretisch kann es sein, dass zu verschiedenen IDs ein User-Objekt nachgeladen werden soll (<code>getAuthor()</code>, <code>getModifier()</code>, <code>getLastVisitor()</code>&#8230;) und dann bräuchten wir sämtliche Methoden in unserem Plugin oder verschiedene Plugins.</p>
<p>Dann hätten wir aber auch wieder mehrfachen Code und nichts gewonnen. Daher werde ich die Pluginschnittstelle noch etwas aufbohren und das Ergebnis dann in einem meiner nächsten Artikel vorstellen.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.ebene7.com/2010/05/12/implementieren-einer-einfachen-plugin-schnittstelle/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
