This post originated from an RSS feed registered with .NET Buzz
by -.
Original Post: Builder Pattern in C#
Feed Title: Norbert Eder - Living .NET
Feed URL: http://feeds.feedburner.com/NorbertEder-Livingnet
Feed Description: Copyright (c)2005, 2006 by Norbert Eder
Eine Diskussion mit Thomas veranlasste mich, einige Beispiele für ein Builder Pattern im .NET Framework zu suchen, zu beschreiben und selbst ein kleines Beispiel aus der Praxis zu liefern.
Einführung
Grundlegend zählt das Builder Pattern zu den Object Creational Patterns - es werden durch dieses Entwurfmuster also Objekte erstellt. Sinn und Zweck des Builder Patterns ist es, den Objekt-Erstellungsprozess von der Repräsentation des Objektes zu entkoppeln. Dadurch ist es möglich, mit demselben Erstellungsprozess unterschiedliche Repräsentationen erstellen zu können.
Beispiel aus dem .NET Framework
Ein sehr gutes Beispiel aus dem .NET Framework ist durch den DbConnectionStringBuilder geben. Dieser stellt die Basisklasse für stark typisierte Ableitungen dar (SqlConnectionStringBuilder etc.). Sehen wir uns an dieser Stelle einen kurzen Sourcecode an:
Mit Hilfe des SqlConnectionStringBuilder ist es möglich, einfach einen syntaktisch korrekten ConnectionString zu erstellen, woraus in weiterer Folge ein Connection-Objekt gebildet werden kann.
Ein eigenes Beispiel
Nun sehen wir uns anhand eines eigenen Beispiels die Implementierung des Builder Patterns an. Grundlage bilden Adressdaten, die in einer XML-Datei vorliegen und importiert werden müssen. Den Part des Importierens ersparen wir uns. Die erstellten Objekte werden lediglich in eine Liste gelegt und anschließend per ToString ausgegeben.
Wie in der Abbildung zu sehen ist, besteht diese Implementierung des Builder-Patterns aus zwei wichtigen Teilen:
- AddressBuilder
- AddressParser
Der Parser selbst ist kein echter Bestandteil des ursprünglichen Builder-Patterns, wurde jedoch in diesem Beispiel zum Parsen der Adressdaten herangezogen und in das Pattern integriert, um die Zuständigkeiten der Objekte sauber zu trennen.
publicclass AddressParser : AParser
{
AddressBuilder _builder = null;
public AddressParser(IBuilder builder)
{
_builder = (AddressBuilder)builder;
}
publicoverridevoid Parse(XmlNode data)
{
if (data != null && _builder != null)
{
_builder.ResetData();
XmlNode xnStreet = data.SelectSingleNode("street");
XmlNode xnStreetNumber =
data.SelectSingleNode("streetnumber");
XmlNode xnZip = data.SelectSingleNode("zip");
XmlNode xnCity = data.SelectSingleNode("city");
XmlNode xnCountry = data.SelectSingleNode("country");
if (xnStreet != null)
_builder.Street = xnStreet.InnerText;
if (xnStreetNumber != null)
_builder.StreetNumber = xnStreetNumber.InnerText;
int zip = 0;
if (xnZip != null)
Int32.TryParse(xnZip.InnerText, out zip);
_builder.Zip = zip;
if (xnCity != null)
_builder.City = xnCity.InnerText;
if (xnCountry != null)
_builder.Country = xnCountry.InnerText;
}
}
}
Der AddressBuilder selbst besitzt nun die Aufgabe, das eigentliche Address-Objekt zu erstellen und zurück zu liefern.
publicclass AddressBuilder : ABuilder
{
privatestring _street = null;
privatestring _streetnumber = null;
privateint _zip = 0;
privatestring _city = null;
privatestring _country = null;
publicstring Street
{
get { returnthis._street; }
set { this._street = value; }
}
publicstring StreetNumber
{
get { returnthis._streetnumber; }
set { this._streetnumber = value; }
}
publicint Zip
{
get { returnthis._zip; }
set { this._zip = value; }
}
publicstring City
{
get { returnthis._city; }
set { this._city = value; }
}
publicstring Country
{
get { returnthis._country; }
set { this._country = value; }
}
publicvoid ResetData()
{
this._city = null;
this._country = null;
this._street = null;
this._streetnumber = null;
this._zip = 0;
}
publicoverride BaseObject Build()
{
Address address = new Address();
address.City = this._city;
address.Country = this._country;
address.Street = this._street;
address.StreetNumber = this._streetnumber;
address.Zip = this._zip;
return address;
}
}
Die Ausführung der gesamten Anwendung erfolgt durch nachfolgenden Sourcecode:
List<Address> addresses = new List<Address>();
XmlDocument doc = new XmlDocument();
doc.Load("addresslist.xml");
XmlNodeList xnlAddresses = doc.SelectNodes("/addresses/address");
if (xnlAddresses != null && xnlAddresses.Count > 0)
{
AddressBuilder builder = new AddressBuilder();
AddressParser parser = new AddressParser(builder);
foreach (XmlNode xnAddress in xnlAddresses)
{
parser.Parse(xnAddress);
Address address = (Address)builder.Build();
addresses.Add(address);
}
}
foreach (Address a in addresses)
{
Console.WriteLine(a.ToString());
Console.WriteLine("---------------------");
}
Console.Read();
Fazit
Über dieses Pattern können durch kleine Anpassungen unterschiedlichste Typen abgewickelt werden, was sich sehr schnell positiv auswirkt und zudem alle für die passende Aufgabe notwendigen Schritte trennt. Dadurch lassen sich diese entsprechend anpassen bzw. gänzlich ersetzen.