Beiträge aus der Kategorie "Programmierereien"

Daily Activity

Mein Ziel: Eine garantierte Activity pro Tag, nämlich wenn das Smartphone angeschaltet wird und die Activity zum Einschaltdatum noch nicht aufgerufen wurde. Solche Dinge sollte man natürlich sehr bewusst programmieren, um keinen Benutzer zu nerven. In diesem Fall bin ich aber der Benutzer.
In der Referenz des Android SDK finde ich ein Systemereignis, welches in Frage kommt, um von einem Broadcast Receiver empfangen zu werden: ACTION_SCREEN_ON. Während ich mittels der AndroidManifest.xml für viele Ereignisse ganz zentral Broadcast Receiver während der Installation einer Anwendung im Betriebssystem registrieren kann, ist dies aber ausgerechnet für dieses Ereignis nicht möglich. Ich habe einige Codebeispiele gesehen, in denen Broadcast Receiver in der OnCreate von Activities registriert wurden. Doch möchte ich den Receiver von der aufgerufenen Activity entkoppeln. Nach weiterer Suche entdecke ich das Ereignis ACTION_USER_PRESENT, welches auch ein mittels der AndroidManifest.xml registrierter Broadcast Receiver empfangen kann.

Wird das Ereignis mit nachfolgendem ActivityLauncher empfangen, wird das Datum des heutigen Tages mit dem eingelesenen Datum aus einer Datei verglichen. Unterscheiden sich die Angaben, wird das aktuelle Datum in die Datei geschrieben und die Activity wird aufgerufen.

public class ActivityLauncher extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        if (! (intent.getAction().equals(Intent.ACTION_USER_PRESENT)))
          return;

        String date = GetDateFromFile(context);

        Date dateNow = new Date ();
        SimpleDateFormat dateformatYYYYMMDD = new SimpleDateFormat("yyyyMMdd");
        StringBuilder nowYYYYMMDD = new StringBuilder( dateformatYYYYMMDD.format( dateNow ) );
        String now = nowYYYYMMDD.toString();

        if (date.compareTo(now) != 0)
        {
          WriteCurrentDateToFile(context, now);

            Intent startActivity = new Intent();
            startActivity.setClass(context, MyActivity.class);
            startActivity.setAction(MyActivity.class.getName());
            startActivity.setFlags(
                    Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            context.startActivity(startActivity);
        }
    }
}
 

Hallo, Shared SQLite DB

Nachdem ich nun die fundamentalen Dinge über die Android-Entwicklung gelesen habe, mache ich mir jetzt die Hände schmutzig. Ich habe die ersten Beispiele und Tutorials aus den einschlägigen Quellen, nämlich developer.android.com, Hello, Android von Ed Burnette und Professional Android Development von Reto Meier ausprobiert, und komme zu dem Ergebnis, dass ich meine Hände auch schmutzig lassen kann. Schließlich geht es hier um Best Practices und die Beispiele haben vorbildlichen Character.
Wie man schon am Hello World-Tutorial erkennen kann, trennt man das Layout seiner Oberfläche am Besten in Form von XML vom Inhalt ab. Ähnlich geht man in der Webentwicklung vor, wo man HTML von CSS trennt. In der Android-Entwicklung hat das den Vorteil, dass man verschiedene Layouts für verschiedene Bildschirme und Geräte entwickeln kann und dabei vom Android SDK auch unterstützt wird.
Auch der Lokalisierung wird von Anfang an Rechnung getragen, indem Textstrings der Oberfläche nur per ID aus dem Layout referenziert werden. Für jede Sprache stellt Google eine Dateierweiterung bereit, so dass die Textstrings aus der richtigen Datei eingelesen werden.

Nun stelle ich mir die Frage, wie ich Daten zwischen Activities teilen kann. Mein erster Gedanke war, die Daten in Form einer XML-Datei zu speichern und für die Anwendung in eine Objektstruktur einzulesen, so dass eine schneller Zugriff möglich ist. Ein bevorzugter Weg, Persistenz in Android zu ermöglichen, ist aber die Verwendung einer SQLite-Datenbank. Auf reigndesign habe ich einen Weg gefunden eine Datenbank von außen beizusteuern. Statt die DB von innen über das Android-SDK anlegen zu müssen, kann ich die Datenbank in das assets-Verzeichnis des Projekts legen.

Ich realisiere meinen von SQLiteOpenHelper abgeleiteten DBHelper als Singleton. Auch wenn es nichts kosten mag, in jeder Activity einen DBHelper zu initialisieren, muss ich mit einem Singleton nicht einmal über die Kosten von Objekterzeugungen nachdenken, denn ein paar Zeilen Code für die Erstellung eines Singleton kosten schon gar nichts. Ich spare mir auch die Mühe, in jeder Activity an jeder in Frage kommenden Stelle des Activity-Lebenszyklus die Datenbank öffnen und schließen zu müssen. Stattdessen stelle ich in meiner abgeleiteten Application-Klasse MyApplication eine Referenz auf eine DBHelper-Instanz bereit, über die ich von jeder Activity aus auf die Datenbank zugreifen kann.

Der Zugriff aus der Activity sieht dann für einen schreibenden Zugriff etwa so aus:


package org.example.hellosqlite;

import static android.provider.BaseColumns._ID;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.TextView;

public class HelloSQLite extends Activity
{
	private static final String TITLE = "title";
	public static final String TABLE_NAME = "books";
	private static String[] FROM = { _ID, TITLE, };

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		addBook("Hello, Android! 3e");
	}

	private DataBaseHelper DBH()
	{
		MyApplication application = ((MyApplication)getApplicationContext());
		return application.GetDataBaseHelper();
	}

	private void addBook(String string)
	{
		SQLiteDatabase sqlitedb = DBH().getWritableDatabase();
		ContentValues values = new ContentValues();
		values.put(TITLE, string);
		sqlitedb.insertOrThrow(TABLE_NAME, null, values);
	}
}
 

Reich mit Amazon Product Advertising API?

Unter der Amazon Product Advertising API wird dem Programmierer eine Untermenge der Funktionalität des Amazon-Shops zur Verfügung gestellt. Amazon-Produktdaten können nahtlos in die eigene Website integriert werden. Produkte können z.B. in einen Warenkorb gelegt werden und der Besucher kann sich umfangreich über die Produkte informieren. Die Bestellabwicklung übernimmt Amazon. Amazon stellt keine Reviews mehr zu Verfügung. Die müssen auf der Eigentümersite gelesen werden. Auf diese Veränderung weist Amazon deutlich hin. Als weitere Änderung muss auf eine bestimmte Anzahl an Anfragen über die API auch ein gewisser Prozentsatz an Bestellungen erfolgen. Ich sehe die Veränderungen als Amazons Reaktion darauf, dass die Produktdaten in der Vergangenheit fleißig von Suchmaschinenoptimierern (SEO) missbraucht wurden, um Inhalte für Websites zu generieren. Wofür braucht das Internet millionenfach reproduzierten Content, der doch ursprünglich einzig und alleine Amazon gehört? So weisen die License Agreements zur Benutzung dieser API denn auch darauf hin, dass die API nur dazu benutzt werden soll, um Besucher zu Amazon umzuleiten.

Hier studiere ich ein C#-Codebeispiel, welches ich in der Amazonschen Dokumentation gefunden habe. Es handelt von einer REST-Anfrage an den Amazon-Server. Die Antwort kommt als XML zurück. Um den Eigentümer der Anfrage eindeutig zu identifizieren bietet Amazon verschiedene Verfahren an. Das Beispiel setzt ein Verfahren um, bei dem Amazon und der Programmierer den gleichen geheimen Schlüssen haben. Dieser Schlüssel kann auf Knopfdruck neu genieriert werden.

Das Beispiel benutzt die Klasse HMACSHA256. Instanzen dieser Klasse können einen Hash-based Message Authentication Code (HMAC) mit dem SHA256-Algorithmus erzeugen.

Zuerst wird eine Instanz von HMACSHA256 mit dem geheimen Schlüssel als Argument erzeugt:


string secretKey = "XblaY"
HMAC signer = new HMACSHA256(secretKey);

Danach wird der gesamte Query-String signiert:


string queryString = "Fertig formatierter String mit allen gewünschten Parametern, für Amazon präpariert";
byte[] sigBytes = signer.ComputeHash(queryString );
string signature = Convert.ToBase64String(sigBytes);

Diese Signatur wird dann als letzter Parameter der Anfrage angehängt.

Kann ich jetzt endlich durch die Benutzung der API reich werden? Meine Nachforschungen haben ergeben, dass weniger Programmieren mehr ist. Besucher einer Website wurden noch nie durch Programmierkunst beeindruckt, sondern durch handgemachten Inhalt. So habe ich denn auch unter denjenigen, die einen nennenswerten Verdienst durch das Partnerprogramm erzielten, nur solche gefunden, die vereinzelte Links auf Amazonprodukte gesetzt haben. Diese Links haben direkt etwas mit dem zu tun, was sie in mühsamer Arbeit gepostet haben. Hier wird es eine vollautomatische Generierung von Produktdaten schwer haben, zu Verdienst gemacht zu werden. Da besucht man doch lieber direkt den Amazon-Shop.

Also, keine Scham, das Partnerprogramm ohne API zu nutzen! Die einfachste Möglichkeit besteht darin, einen Link auf die Amazon-Homepage zu setzen. Damit Amazon nicht zu viele für Suchmaschinen sichtbare Links bekommt, können Links umgeleitet werden. In ASP.NET MVC habe ich das so gemacht:

    public class AmazonLinkController : Controller
    {
        //
        // GET: /AmazonLink/

        public void Index()
        {
            Response.Redirect("http://www.amazon.de/exec/obidos/redirect-home?tag=meinTag-21&site=home");
        }

    }

die Action Index meines Controllers ist eine void-Methode und leitet zu Amazon um.

 

URL-Rewriting mit ASP.NET MVC

Möchte man ASP.NET MVC einsetzen, um Internetanwendungen zu programmieren, kann man guten Inhalt direkt mit Suchmaschinenoptimierung verknüpfen. Öffentlich zugänglicher Inhalt kann auch Google möglichst gut zugänglich gemacht werden. Die Google-Roboter, die Websites abgrasen und Webseiten in den Index aufnehmen, scheuen vor URL mit Parametern zurück (z.B. www.wetter.de?PLZ=53127) und nehmen statisch erscheinende URL in den Index auf (z.B. www.wetter/53127.html). Das ist ein triftiger Grund, URL statisch aussehen zu lassen. Zusätzlich sehen statische URL freundlicher aus, und können besser gemerkt werden.

Als Erstes definiere ich eine Zuordnung zwischen einem Konfigurationsabschnittshandler und einem Konfigurationsabschnitt. Aus diesem Grund füge ich dem configSections-Knoten in Web.config folgende sectionGroup hinzu:

<configSections>
  <sectionGroup name="myModuleConfig">
    <section name="rewriteModule" type="MyAssembly.RewriteModuleSectionHandler, MyAssembly"/>
  </sectionGroup>
</configSections>

Dementsprechend muss ich dann im configuration-Knoten den Konfigurationsabschnitt definieren:

<myModuleConfig>
  <rewriteModule>
    <rewriteOn>true</rewriteOn>
    <rewriteRules>
      <rule source="Keyword/(.+).aspx" destination="Keyword?keyword=$1"/>
    </rewriteRules>
  </rewriteModule>
 </myModuleConfig>

In diesem Abschnitt definiere ich, ob das URL-Rewriting stattfinden soll und ich definiere Rewrite-Rules, so wie man das unter PHP in einer .htaccess-Datei tun würde.

Nun muss ich den Konfigurationsabschnittshandler RewriteModuleSectionHandler denn auch in Code gießen:

public class RewriteModuleSectionHandler : IConfigurationSectionHandler
{

    private XmlNode _XmlSection;
    private string _RewriteBase;
    private bool _RewriteOn;

    public XmlNode XmlSection
    {
        get { return _XmlSection; }
    }

    public string RewriteBase
    {
        get { return _RewriteBase; }
    }

    public bool RewriteOn
    {
        get { return _RewriteOn; }
    }
    public object Create(object parent, object configContext, System.Xml.XmlNode section)
    {
        _RewriteBase = HttpContext.Current.Request.ApplicationPath + "/";

        try
        {
            _XmlSection = section;
            _RewriteOn = Convert.ToBoolean(section.SelectSingleNode("rewriteOn").InnerText);
        }
        catch (Exception ex)
        {
            throw (new Exception("Error while processing RewriteModule configuration section.", ex));
        }
        return this;
    }
}

Im Wesentlichen nimmt mein Handler bei Erzeugung seinen XML-Knoten in Empfang und dient mir dazu, den XML-Zugriff zu kapseln.

Zusätzlich definiere ich in der Web.config ein HTTP-Modul:

<system.web>
  <httpModules>
    <add name="RewriteModule" type="MyAssembly.RewriteModule, MyAssembly"/>
  </httpModules>
</system.web>

In C#-Code gegossen:

class RewriteModule : IHttpModule
{

    public void Dispose() { }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(RewriteModule_BeginRequest);
    }

    void RewriteModule_BeginRequest(object sender, EventArgs e)
    {
        RewriteModuleSectionHandler cfg = (RewriteModuleSectionHandler)ConfigurationManager.GetSection("myModuleConfig/rewriteModule");

        if (!cfg.RewriteOn) return;

        string path = HttpContext.Current.Request.Path;

        if (path.Length == 0) return;

        XmlNode rules = cfg.XmlSection.SelectSingleNode("rewriteRules");
        foreach (XmlNode xml in rules.SelectNodes("rule"))
        {
            try
            {
                string attrsource = "/" + xml.Attributes["source"].InnerText;

                Regex re = new Regex(attrsource, RegexOptions.IgnoreCase);

                Match match = re.Match(path);
                if (match.Success)
                {
                    path = re.Replace(path, xml.Attributes["destination"].InnerText);
                    // new path to rewrite to
                    string rew = cfg.RewriteBase + path;

                    // rewrite
                    HttpContext.Current.RewritePath(rew);

                    return;
                }
            }
            catch (Exception ex)
            {
                throw (new Exception("Incorrect rule.", ex));
            }
        }
        return;
    }

}

in der Init-Methode, die mir das IHttpModule-Interface vorschreibt, bekomme ich eine Referenz auf die Anwendung übergeben. Ich füge dem Anwendungsevent BeginRequest als EventHandler einen Delegaten hinzu, der mir schließlich meine URL-Rewritefunktion aufruft:
void RewriteModule_BeginRequest(object sender, EventArgs e)

Nachdem ich meinen RewriteModuleSectionHandler instanziiert habe (über RewriteModuleSectionHandler cfg = (RewriteModuleSectionHandler)ConfigurationManager.GetSection("myModuleConfig/rewriteModule") findet die Instanziierung nur einmal statt), gehe ich alle Rewrite-Regeln durch und prüfe mit Regex und Match, ob der reguläre Ausdruck der Anfrage-URL mit dem der Regel übereinstimmt. Mit Regex.Replace(…) wird die URL endlich umgeschrieben.

 

Design Patterns – Proxy

Das Proxy-Pattern legt eine Zugriffskontrollschicht über ein Objekt (Subjekt). Da Originalobjekt wie Proxyobjekt das selbe Interface Subject implementieren, kann das Proxyobjekt als Stellvertreter (Proxy) verwendet werden.
So alltäglich der Zugriff auf Objekte für den Programmierer ist, so mannigfaltig können die Zugriffskontrollen des Proxy sein. Der Proxy kann etwa den Zugriff auf ein entferntes Objekt kapseln und kann für den Client unsichtbar das Verbindungsmanagement übernehmen (Remote Proxy). Ohne es zu wissen, habe ich diese Art Proxy verwendet, um den Aktienwerte zurück gebenden Webservice zu testen. Anhand der WSDL generiert mir Visual Studio eine Servicereferenz, die als Proxy dient.
Das Proxy kann auch zu dem Zweck existieren, das gekapselte Objekt erst bei Bedarf zu instanziieren (Virtual Proxy). Im GoF-Buch gibt es z.B. ein Dokument-Objekt, welches eine Vielzahl von primitiven Objekten wie Texte, geomentrische Elemente und Bilder enthält. Würde jedes Bildobjekt sofort instanziiert werden, könnte das sehr lange dauern und wäre in vielen Fällen unnötig.
Ein Schutzproxy erlaubt nur autorisierte Zugriffe auf das Objekt. Hier kann der Objektzugang benutzer- oder gruppenspezifisch geregelt werden.
Im Unterschied zum Decorator fügt der Proxy dem Subjekt keine Funktionalität hinzu. Es wird lediglich der Zugriff gesteuert.

 

Design Patterns – State

Das Zustand-Muster erlaubt es einem Objekt zur Laufzeit zustandsabhängig sein Verhalten zu ändern. Im Zeitalter der Objektorientierung programmiert man nach diesem Muster Zustandsautomaten. Statt für jede Zustandswechselmethode eine umfangreiche if-else-Orgie zu veranstalten, wird jeder Zustand in eine Klasse gekapselt. Zustände können so wesentlich leichter hinzugefügt werden, ohne jedesmal die if-else-Anweisungen erneut verstehen zu müssen. Selbst bei einem sehr komplexen Zustandsautomaten bleibt die Wartung übersichtlich.

<?php
abstract class WindowState
{
	public function OpenWide($window)
	{
	    echo "Keine Zustandsänderung<br/>";
	}
	public function Close($window)
	{
	    echo "Keine Zustandsänderung<br/>";
	}
	public function Tilt($window)
	{
	    echo "Keine Zustandsänderung<br/>";
	}
}

class WindowClosedState extends WindowState
{
	public function OpenWide(Window $window)
	{
	    echo "Fenster wurde geöffnet<br/>";
	    $window->currentState=new WindowWideOpenedState();
	}
	public function Tilt(Window $window)
	{
	    echo "Fenster wurde gekippt<br/>";
	    $window->currentState=new WindowTiltState();
	}
}

class WindowWideOpenedState extends WindowState
{
	public function Tilt(Window $window)
	{
	    echo "Fenster wurde gekippt<br/>";
	    $window->currentState=new WindowTiltState();
	}
	public function Close(Window $window)
	{
	    echo "Fenster wurde geschlossen<br/>";
	    $window->currentState=new WindowClosedState();
	}
}

class WindowTiltState extends WindowState
{
	public function Close(Window $window)
	{
	    echo "Fenster wurde geschlossen<br/>";
	    $window->currentState=new WindowClosedState();
	}
	public function Open(Window $window)
	{
	    echo "Fenster wurde geöffnet<br/>";
	    $window->currentState=new WindowOpenedState();
	}
}
class Window
{
	public $currentState;

	public function Window()
	{
		$this->currentState=new WindowClosedState();
	}

	public function OpenWide()
	{
		$this->currentState->OpenWide($this);
	}

	public function Close()
	{
		$this->currentState->Close($this);
	}

	public function Tilt()
	{
		$this->currentState->Tilt($this);
	}
}

$window = new Window();
$window->Close();

$window->OpenWide();
$window->Tilt();
$window->Tilt();
$window->OpenWide();
$window->Close();

?>
 

Design Patterns – Composite

Bei einer Komposition (HAS-A-Beziehung von Container/Komponenten) mag es vorkommen, dass Komponente wie Container gleich behandelt werden sollen. Ein GUI-Element kann beispielsweise ein Container oder ein primitives Element wie ein Control sein. Beide Arten, Container wie Control haben aber viele gleiche Eigenschaften und Methoden, wie Draw(), Size(), Move(x,y), usw. Für eine flexible Verwendung leitet man Container und Komponente vom selben Interface ab. Sie verhalten sich damit nach außen gleich. Wenn nun z.B. Draw() auf dem Container aufgerufen wird, braucht dieser nur sich selbst zu zeichnen und danach auf seinen Komponenten Draw() aufzurufen, welche ihrerseits wieder beides sein können.

Hier ein Codeschnipsel in PHP:

<?php

interface IComponent
{
    public function GetSize();
    public function GetWeight();
}

class Component implements IComponent
{
	private $width;
	private $height;
	private $length;
	private $weight;

	public function __construct($width, $height, $length, $weight)
    {
        $this->width = $width;
        $this->height = $height;
        $this->length = $length;
        $this->weight = $weight;
    }

    public function GetSize()
    {
    	return $this->width * $this->height * $this->length;
    }

    public function GetWeight()
    {
    	return $this->weight;
    }
}

class Container extends Component implements IComponent
{
    private $components = array();
    public function __construct($width, $height, $length, $weight)
    {
        parent::__construct($width, $height, $length, $weight);
    }

    public function Add ($component)
    {
    	$this->components[] = $component;
    }

    public function GetSize()
    {
    	return parent::GetSize();
    }

    public function GetWeight()
    {
    	$weight = parent::GetWeight();
        foreach ($this->components as $info => $value )
        	$weight += $value->GetWeight();

        return $weight;
   	}
}

    $container1 = new Container(10,10,10,1000);
    $component2 = new Component(2,2,2,200);
    $container = new Container(100, 100, 100, 100000);
    $container->Add($container1);
    $container->Add($component2);

    $minicontainer = new Container (8,8,8,8);
	$minicomponent = new Component (2,2,2,2);
	$minicomponent2 = new Component (2,2,2,2);
	$minicontainer->Add($minicomponent);
	$minicontainer->Add($minicomponent2);
	$container1->Add($minicontainer);  

	echo "Total Weight: <br\>";
   	echo $container->GetWeight();

?>

Mein Beispiel stellt eine Schiffscontainerstruktur dar. Container wie Component sind beide vom Typ IComponent und bieten die Methoden GetSize() und GetWeight() an. Statt bei der Gewichtsberechnung den Typ zu unterscheiden brauche ich nur GetWeight() aufzurufen.

 

Design Patterns – Iterator

Mit Hilfe des Iterator Patterns kann sequenziell auf die Elemente eines zusammengesetzten Objekts (Aggregat) zugegriffen werden, ohne die Struktur der Elemente offen zu legen. Ein Iterator erlaubt diesen Zugriff.
Es wird verwendet, wenn mehrere Iteratoren gleichzeitig ermöglicht werden sollen, wenn ein Iterator polymorph verwendet werden soll oder wenn Datenstrukturen sich ändern aber man kein Clientcode ändern möchte. Außerdem kann ein Iteratorverhalten zur Laufzeit ausgetauscht werden. Das Muster beherzigt das Single Responsibility Principle. Eine Klasse soll nur eine Verantwortlichkeit haben.

Hier ein Codeschnipsel in PHP:

<?php

class Person
{
	private $name = "";
    public function __construct($n)
    {
        $this->name = $n;
    }

    public function Name()
    {
    	return $this->name;
    }
}

class NameFilterIterator extends FilterIterator
{
    private $nameFilter;

    public function __construct(Iterator $iterator , $filter )
    {
        parent::__construct($iterator);
        $this->nameFilter = $filter;
    }

    public function accept()
    {
        $person = $this->getInnerIterator()->current();
        if( strcasecmp($person->Name(),$this->nameFilter) == 0) {
            return false;
        }
        return true;
    }
}

class ConcreteAggregateArray implements IteratorAggregate
{
    protected $_array = array();
	public function __construct()
    {
    	$this->_array[] = new Person("Hans");
    	$this->_array[] = new Person("Seppel");
        $this->_array[] = new Person("Dirk");
   	}

    public function getIterator()
    {
        return new ArrayIterator($this->_array);
    }
}

class ConcreteAggregateObjectStore implements IteratorAggregate
{
	protected $objectStore;

	public function __construct()
    {
    	$this->objectStore = new SplObjectStorage();
    	$this->objectStore->attach(new Person("Lilli"));
    	$this->objectStore->attach(new Person("Eva"));
        $this->objectStore->attach(new Person("Herta"));
    }

    public function getIterator()
    {
        return $this->objectStore;
    }
}

class ConcreteAggregateFilteredArray implements IteratorAggregate
{
    protected $_array = array();
	public function __construct()
    {
    	$this->_array[] = new Person("Albert");
    	$this->_array[] = new Person("Adelheid");
        $this->_array[] = new Person("Anton");
   	}

    public function getIterator()
    {
        return new NameFilterIterator(new ArrayIterator($this->_array), "Adelheid");
    }
}

function PrintPersons($iteratorAggregate)
{
    foreach ($iteratorAggregate->getIterator() as $info => $value )
    {
        echo $value->Name();
        echo "<br />";
    }
}

$aggregates = array(new ConcreteAggregateArray,
					new ConcreteAggregateObjectStore,
					new ConcreteAggregateFilteredArray);
foreach ($aggregates as $info => $value)
	PrintPersons($value)
?>

Die Aggregate ConcreteAggregateArray, ConcreteAggregateObjectStore und ConcreteAggregateObjectStore implementieren das IteratorAggregate-Interface und geben jeweils einen konkreten Iterator, welcher von Iterator abgeleitet ist, zurück. Dadurch kann das Aggregat polymorph verwendet werden. Das beweist die Funktion function PrintPersons($iteratorAggregate). Mittels foreach kann ungeachtet der konkreten Form über das Aggregat iteriert werden.

 

Design Patterns – Template Method

Die Schablonenmethode (Template Method) definiert das Skelett eines Algorithmus und delegiert einzelne Schritte an Unterklassen weiter. Dadurch können Unterklassen bestimmte Schritte eines Algorithmus bestimmen, ohne die Struktur des Algorithmus zu verändern. Hier wird das Designprinzip “Don’t call us, we call you” verwirklicht.
Die Schablonenmethode sollte angewendet werden, um invariante Anteile eines Algorithmus genau einmal zu definieren und variierende Anteile an Unterklassen zu übergeben. Die Templatemethode ist eine häufig verwendete Refaktorisierngsmethode. Gemeinsame Teile werden aus Basisklassen in eine Templatemethode herausgezogen. Dadurch wird sichergestellt, dass doppelter Code vermieden wird. Durch Verwendung einer Schablonenmethode werden Unterklassen kontrolliert erweitert. Einschubmethoden, die in der abstrakten Klasse häufig leer implementiert sind, können in Unterklassen überschrieben werden. Die Verwendung der Einschubmethoden ist allerdings in der abstrakten Klasse geregelt.

Hier als Beispiel ein PHP-Schnipsel

<?php

abstract class KindergartenTrip
{
	protected function __construct($count)
	{
  		$this->memberCount = $count;
  	}

  	abstract protected function DoAntemeridianTrip();
  	abstract protected function DoPostmeridianTrip();

	private $memberCount = 0;
	private $collectedMembersCount = 0;

	public final function doTrip()
  	{
  		if ($this->memberCount == 0)
  		return;

  		if ($this->collectedMembersCount != $this->memberCount)
  			return;

  		$this->TravelToDestination();
  		$this->DoAntemeridianTrip();
  		$this->DoPostmeridianTrip();
		$this->TravelHome();

  	}

  	public final function SetCollectedChildren($count)
  	{
		$this->collectedMembersCount = $count;
  	}

  	public function TravelToDestination()
  	{
  		echo "Normal Travel to Destination<BR>";
  	}
  	public function TravelHome()
  	{
  		echo "Normal Hometravel<BR>";
  	}

}
class SchwimmbadTrip extends KindergartenTrip
{
	public function __construct($count)
	{
		parent::__construct($count);
  	}

  	public function DoAntemeridianTrip()
  	{
  		echo "Go swimming<BR>";
  	}

  	public function DoPostmeridianTrip()
  	{
  		echo "Go swimming once again<BR>";
  	}

  	public function TravelHome()
  	{
  		echo "Few children were sick<BR>";
  	}
}

class SpecialFunTrip extends KindergartenTrip
{
	public function __construct($count)
	{
		parent::__construct($count);
  	}

  	public function DoAntemeridianTrip()
  	{
  		echo "Go to circus<BR>";
  	}

  	public function DoPostmeridianTrip()
  	{
  		echo "Go to cinema<BR>";
  	}

  	// Einschubmethode
  	public function TravelToDestination()
  	{
  		echo "Children very excited<BR>";
  	}

  	// Einschubmethode
  	public function TravelHome()
  	{
  		echo "Children very happy an exhausted<BR>";
  	}
}
?>

Die abstrakte Klasse ist KindergartenTrip und die Templatemethode doTrip(). doTrip() ist als public final definiert, da die Methode nicht mehr überschrieben werden soll. Für alle Trips gilt die Invariante, dass die Anzahl der eingesammelten Kinder gleich der Anzahl der angemeldeten Kinder sein muss. Die öffentliche Methode SetCollectedChildren($count) soll ebenfalls in abgeleiteten Klassen nicht geändert werden können. Die Templatemethode legt als Invariante fest, dass der Trip erst losgeht, wenn die Anzahl der aufgelesenen Kinder gleich der Anzahl der angemeldeten Kinder ist. Außerdem wird die Reihenfolge der Schritte des Tripalgorithmus festgelegt. TravelToDestination() und TravelHome() sind Einschubmethoden, die überschrieben werden können. Konkrete Klassen müssen die variablen Schritte DoAntemeridianTrip() und DoPostmeridianTrip() implementieren.

 

Design Patterns – Facade

Die Fassade (Facade) dient dazu, ein einheitliches Interface zu einer Menge von Interfaces eines Subsystems bereit zu stellen. Dadurch wird die Komplexität aus dem Subsystem herausgenommen und die Benutzung vereinfacht. Die Interfaces des Subystems können auch weiter seperat genutzt werden. Die Fassade ist das wohl häufigste Entwurfsmuster, mindestens was die Benutzung betrifft. Wie oft habe ich mich dabei ertappt, wie ich stundenlang nach dem einfachsten Weg suche. Hier gehts ums nackte Überleben, denn complexity kills.
Dennoch einige Beispiele:

  • Die MFC ist Microsofts Fassade um die komplizierte Windows-API, um die Erstellung von Windows-Anwendungen zu vereinfachen.
  • In Java sind viele manuelle Schritte nötig, nur um eine Messagebox mit den Low-Level-Klassen von Swing auf den Bildschirm zu werfen. Mit der Fassade JOptionPane.showMessageDialog(...) kann man sich viele Zeilen sparen und dies ist schnell erledigt.
  • Java Swing wiederum ist selbst eine Fassade, um nicht jede geometrische Figur einzeln zeichnen zu müssen.
  • Eine Computersprache selbst ist eine Fassade auf die nächst niedrigere Ebene. C#-Code ruft intern C-Code auf und dieser Assembler. Der Benutzer selbst hat die abstrakteste Fassade vor sich.

Die Beispiele sind wohl unendlich. Ob es zu den wirklichen Fortschritten gehört, eine Fassade etwa um eine STL-Collection zu bauen, die die Methoden Count() und GetAt(int) anbietet, nur weil man die die letzten 20 Jahre so programmiert hat, darüber lässt sich streiten.