Twitter und Facebook-Anbindung
X
Tweet Follow @twitterapi
!!! Anbindung an twitter und facebook öffnen !!!

Wenn Ihnen mein Online-Buch gefällt,
dann bedanken Sie sich doch mit einer kleinen Spende...

33.4 Builder

33.4 Builder

Die Erbauer ähneln sehr den abstrakten Fabriken, aber sie erzeugen keine Produktklasse, sondern eher ein Endresultat. Dies könnte z.B. eine Datei - oder Bildschirmausgabe sein. Zudem nennt man den Client hier oft Direktor oder führt den Direktor als Zwischenstück ein.

Wofür braucht man jetzt Builder? Sie sind vorwiegend für komplexe Strukturen bzw. Abläufe gedacht. Wärend eine Fabrik z.B. einen einfachen Schuh produziert, ist ein Builder eher damit beschäftigt ein Haus mit Garage, Balkon, Garten und Swimmingpool zu erzeugen, also durchaus ein Produkt mit mehreren Komponenten. Zudem kann es eine Rolle spielen, in welcher Reihenfolge die Komponenten erzeugt und miteinander verknüpft werden. Dabei wird der Außenwelt, also dem Direktor oder dem Client, nicht gezeigt, wie genau dieser Vorgang abäuft. Es wird also weniger das Produkt, als seine Erzeugung verschleiert bzw. Gekapselt.

Vereinfachte Struktur des Builder Pattern

Ein gutes Beispiel wäre z.B. ein Renderer. Er kümmert sich darum, das OpenGL oder DirectX spezifische Sachen vorbereitet werden, dann greift er auf alle Models zu, zeichnet sie entsprechend und führt dann ggf. spezielle Shader aus, damit die ganze Sache auch hübsch aussieht. Im Vorhergehenden Beispiel hatte jedes Produkt seine eigene Ausgabefunktion. Das Produkt wäre also die Bildschirmausgabe und nicht ein spezielles Model. Diese herangehensweise würde das vorhergehende Beispiel mit den abstrakten Fabriken, deutlich performanter machen.

In dem folgenden Beispiel werde ich dieses Entwurfsmuster, zum Speichern von Informationen, in eine Datei, benutzen. Mein Client wird also nicht nur für jede Dateiart eine eigene Methode benutzen, sondern zwei konkrete Erbauer, welcher das Speichern kapseln. Die Schnittstelle, also der abstrakte Builder, wird nur eine Methode zum Aufrufen der Speichernfunktion vorgeben. Die abgeleiteten speziellen Builder implementieren diese Methode und rufen von dort aus eigene private Methoden auf, welche dann Schritt für Schritt die notwendigen Aufgaben erledigen.

Schauen Sie sich zunächst die Schnittstelle an.

AbstractBuilder.h:

 1
 2
 3
 4
 5
					
// Schnittstelle für einen Dateierzeuger
class IAbstractFileBuilder {
	public:
		virtual void SaveToFile(const char* pcstrStream) = 0;
};
					

Wie Sie sehen, wird außer der Speichermethode, nichts weiter definiert. Als nächstes folgen die Definitionen der konkreten Builder.

PDFBuilder.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
					
#include "AbstractFileBuilder.h"

// Klasse zum Erzeugen von PDF Dateien
class CPDFBuilder : public IAbstractFileBuilder {
	public:
		CPDFBuilder(const char* pcstrFilePath);
		~CPDFBuilder();

		virtual void SaveToFile(const char* pcstrStream);

	private:
		char* m_pstrFilePath;

		void Open();
		void WriteMetaInformations();
		void WriteContent(const char* pcstrStream);
		void Close();
};
					

WordFileBuilder.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
					
#include "AbstractFileBuilder.h"

// Klasse zum Erzeugen von Word Dateien
class CWordFileBuilder : public IAbstractFileBuilder {
	public:
		CWordFileBuilder(const char* pcstrFilePath);
		~CwordFileBuilder();

		virtual void SaveToFile(const char* pcstrStream);

	private:
		char* m_pstrFilePath;

		void Open();
		void WriteHeader();
		void WriteContent(const char* pcstrStream);
		void WriteFooter();
		void Close();
};
					

Wie Sie sehen, unterscheiden sich beide Builder in der Anzahl der Methoden, aber es gibt auch Gemeinsamkeiten, wie den Dateiname und die Methoden zum Öffnen und Schließen. Jetzt könnte man meinen, dass man diese doch schon in der Basisklasse definieren kann. Hierauf muss ich mit einem klaren jain antworten.

Der Dateiname hat in einer Schnittstelle schon einmal nichts zu suchen. Der abstrakte Builder wäre dann zwar immer noch abstrakt, aber nicht mehr vollständig und dass sieht das Pattern nicht vor. Die Methoden wiederum könnten durchaus in die Basisklasse wandern, aber rein perspektivisch und semantisch, wäre das nicht korreckt. Im abstrakten Erbauer sollen nur die Methoden hinein, welche zum einen nichts über die Funktionsweise verraten (was das Öffnen und Schließen tun) und zweitens kann man garnicht davon ausgehen, dass man diese Funktionalitäten wirklich braucht. Man könnte sich genauso ein Builder erzeugen, welcher einfach auf die Konsole etwas ausgibt und diese Konsolenausgabe in eine Textdatei umleitet. Schon braucht man sich um das Öffnen und Schließen nicht mehr kümmern.

Als nächstes folgen die Implementierungen der Builder und wie Sie wieder feststellen werden, habe ich mich auf das wesentliche konzentriert und die eigentliche Arbeit durch simple Konsolenausgaben ersetzt.

PDFBuilder.cpp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
					
#include "PDFBuilder.h"



// Konstruktor - Initialisieren ///////////////////////////////////////////////
CPDFBuilder::CPDFBuilder(const char* pcstrFilePath) {
	m_pstrFilePath = new char[strlen(pcstrFilePath) + 1];
	strcpy(m_pstrFilePath, pcstrFilePath);
} // CPDFBuilder //////////////////////////////////////////////////////////////



// Destruktor - Aufräumen /////////////////////////////////////////////////////
CPDFBuilder::~CPDFBuilder() {
	delete [] m_pstrFilePath;
} // ~CPDFBuilder /////////////////////////////////////////////////////////////



// Übergebenen String in PDF speichern ////////////////////////////////////////
void CPDFBuilder::SaveToFile(const char* pcstrStream) {
	Open();
	WriteMetaInformations();
	WriteContent(pcstrStream);
	Close();
} // SaveToFile ///////////////////////////////////////////////////////////////



// PDF-Datei öffnen ///////////////////////////////////////////////////////////
void CPDFBuilder::Open() { 
	printf("Open PDF-File\n");
} // Open /////////////////////////////////////////////////////////////////////



// Header-Informationen speichern /////////////////////////////////////////////
void CPDFBuilder::WriteMetaInformations() {
	printf("Write Word-Header\n");
} // WriteMetaInformations ////////////////////////////////////////////////////



// Hauptteil schreiben ////////////////////////////////////////////////////////
void CPDFBuilder::WriteContent(const char* pcstrStream) {
	printf("Write PDF-Content: %s\n", pcstrStream);
} // WriteContent /////////////////////////////////////////////////////////////



// Word-Datei schließen ///////////////////////////////////////////////////////
void CPDFBuilder::Close() { 
	printf("Close PDF-File\n");
} // Close ////////////////////////////////////////////////////////////////////
					

WordFileBuilder.cpp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
					
#include "WordFileBuilder.h"



// Konstruktor - Initialisieren ///////////////////////////////////////////////
CWordFileBuilder::CWordFileBuilder(const char* pcstrFilePath) {
	m_pstrFilePath = new char[strlen(pcstrFilePath) + 1];
	strcpy(m_pstrFilePath, pcstrFilePath);
} // CWordFileBuilder /////////////////////////////////////////////////////////



// Destruktor - Aufräumen /////////////////////////////////////////////////////
CWordFileBuilder::~CWordFileBuilder() {
	delete [] m_pstrFilePath;
} // ~CWordFileBuilder ////////////////////////////////////////////////////////



// Übergebenen String in Word-Dokument speichern //////////////////////////////
void CWordFileBuilder::SaveToFile(const char* pcstrStream) {
	Open();
	WriteHeader();
	WriteContent(pcstrStream);
	WriteFooter();
	Close();
} // SaveToFile ///////////////////////////////////////////////////////////////



// Word-Datei öffnen //////////////////////////////////////////////////////////
void CWordFileBuilder::Open() { 
	printf("Open Word-File\n");
} // Open /////////////////////////////////////////////////////////////////////



// Header-Informationen speichern /////////////////////////////////////////////
void CWordFileBuilder::WriteHeader() {
	printf("Write Word-Header\n");
} // WriteHeader //////////////////////////////////////////////////////////////



// Hauptteil schreiben ////////////////////////////////////////////////////////
void CWordFileBuilder::WriteContent(const char* pcstrStream) {
	printf("Write Word-Content: %s\n", pcstrStream);
} // WriteContent /////////////////////////////////////////////////////////////



// Abschließende-Informationen speichern //////////////////////////////////////
void CWordFileBuilder::WriteFooter() {
	printf("Write Word-Footer\n");
} // WriteFooter //////////////////////////////////////////////////////////////



// Word-Datei schließen ///////////////////////////////////////////////////////
void CWordFileBuilder::Close() {
	printf("Close Word-File\n");
} // Close ////////////////////////////////////////////////////////////////////
					

Wie Sie sehen, erfüllen diese Klassen nicht das was sie versprechen, aber in diesem Kapitel geht es ja nicht darum, Worddokumente und PDF's zu erzeugt, sondern um Entwurfsmuster. Somit habe ich auch hier wieder alles weggelassen, was vom Fokus ablenken könnte.

Zu guter Letzt noch die Verwendung im Client, also im Direktor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
					
#include "PDFBuilder.h"
#include "WordFileBuilder.h"



// Hauptfunktion der Anwendung ////////////////////////////////////////////////
int main (int argc, char** argv) {
	CWordFileBuilder	oWordBuilder("C:\\Test.docx");
	CPDFBuilder		oPDFBuilder("C:\\Test.pdf");

	oWordBuilder.SaveToFile("Hallo Welt");
	printf("\n");
	oPDFBuilder.SaveToFile("Hallo Welt");

	return 0;
} // main /////////////////////////////////////////////////////////////////////
					

Ausgabe:

Open Word-File
Write Word-Header
Write Word-Content: Hallo Welt
Write Word-Footer
Close Word-File

Open PDF-File
Write Word-Header
Write PDF-Content: Hallo Welt
Close PDF-File
		

Wie Sie sehen, ruft der Direktor wirklich nur die Hauptfunktionalität auf und die speziellen Implementierungen sorgen für den Rest.

Dieses Entwurfsmuster wird allerdings weniger häufig genutzt, da die Methapher selten vorkommt, bzw. Anwendung findet und auch ein Austauschen nicht ganz so einfach ist bzw. das Kompilieren des gesamten Direktors mit sich zieht. Der vollständigkeit halber habe ich es aber mit aufgenommen.

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012