php.de

Zurück   php.de > Webentwicklung > Software-Design

Software-Design Diskussionen auf Profi-Niveau: PHP Lösungen auf konzeptioneller Ebene

Antwort
 
LinkBack Themen-Optionen Thema bewerten
Alt 09.03.2010, 16:28  
moderatives Dielektrikum
 
Benutzerbild von nikosch
 
Registriert seit: 21.05.2008
Beiträge: 34.241
PHP-Kenntnisse:
Fortgeschritten
nikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz sein
Standard

Zitat:
Das setzt dann ja aber voraus, dass Contact auch noch Observeable implementiert ...
Selbstredend
Zitat:
Macht man das in der Praxis überhaupt, also eine Observeable Observer-Klasse zu erstellen?
Es gibt nicht das Pattern. Im konkreten Anwendungsfall einer Personalhierarchie würde das schon Sinn machen. Und technisch möglich ist das ja (man muss nur auf Endlosschleifen achten).

Zitat:
Das mit dem automatische Zuweisen der Contacts in der Factory hatte ich mir zwar anders vorgestellt, aber die Idee hat sich auch mittlerweile als unbrauchbar in Luft aufgelöst
Ja, das ist verhältnismäßig unpraktisch über eine Factroymethode. Es ginge natürlich auch so:

PHP-Code:
$pc    = new Contact ('Personalchef');
$va    = new Contact ('Vorarbeiter');
$boss = new Contact ('Boss');

$BaseWorkerFactory = new WorkerFactory;
$BaseWorkerFactory->setObservers (array ($va));

$PrioBaseWorkerFactory = new WorkerFactory;
$PrioBaseWorkerFactory->setObservers (array ($va $pc));


$worker1 $BaseWorkerFactory->create ('1' 'Horst Horsten');  
$worker2 $PrioBaseWorkerFactory->create ('2' 'Heinz Kunze');  

// usw. 
__________________
--
One pixel is still too big. Please make it smaller. ASAP.

Initiative Mittelstand.
Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers.
--
nikosch ist offline   Mit Zitat antworten
Sponsor Mitteilung
PHP Code Flüsterer

Registriert seit: 21.08.2005
Beiträge: 4682
PHP-Kenntnisse:
Fortgeschritten

Alt 09.03.2010, 16:37  
Neuer Benutzer
 
Registriert seit: 09.03.2010
Beiträge: 23
PHP-Kenntnisse:
Anfänger
Kardey befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Zitat von nikosch Beitrag anzeigen
... (man muss nur auf Endlosschleifen achten).
Daran hatte ich schon gedacht, was ja durchaus, wenn man nicht aufpasst, bei einer komplexeren Hierarchie passieren könnte.

Zitat:
Zitat von nikosch Beitrag anzeigen
Es ginge natürlich auch so: ....
So etwas hatte ich mir im Prinzip auch vorgestellt, nur waren bei mir die Contacts mehr oder weniger fest in der Fabrik verankert und die Contacts waren auch eher nach dem Motto da "Hauptsache jeder Worker hat ein Contact, aber die Contacts an sich interessieren mich nicht.", aber die Idee hat sich dann doch schnell als unpraktisch erwiesen.
__________________
Kardey
Kardey ist offline   Mit Zitat antworten
Alt 09.03.2010, 18:25  
Neuer Benutzer
 
Registriert seit: 09.03.2010
Beiträge: 23
PHP-Kenntnisse:
Anfänger
Kardey befindet sich auf einem aufstrebenden Ast
Standard

Hoffentlich ist dieser Beitrag nicht zuuuu lange geworden .

Hallo,

ich habe das Beispiel mit den Arbeitern, dem Ansprechpartner und dem Chef mal etwas ausgebaut, um das Zusammenspiel der Observers aktiv zu sehen.
Die beiden Interfaces bleiben unverändert (mit Ausnahme verbesserter Methodennamen):
PHP-Code:
interface Observer {
    
    public function 
update(Observeable $observedObject);
    
}

interface 
Observeable {
    
    public function 
addObserver(Observer $observer);
    
    public function 
removeObserver(Observer $observer);
    
    public function 
notifyObservers();


Dann habe ich noch zwei Klassen erstellt: eine für die verschiedenen Jobs und eine, die die in der Firma vorhandenen Jobs sammelt (wobei diese Klassen nur der Verwendung in diesem Beispiel dienen sollen, also nicht unbedingt schön sein sollen/müssen ):
PHP-Code:
class Job {

    protected 
$worker null;
    protected 
$title '';
    protected 
$decription '';
    protected 
$done false;
    
    public function 
__construct($title$description '') {
        
$this->title $title;
        
$this->description $description;
    }
    
    public function 
assignWorker(Worker $worker) {
        
$this->worker $worker;
    }
    
    public function 
markAsDone() {
        
$this->done true;
    }
    
    public function 
getTitle() {
        return 
$this->title;
    }

}

class 
Jobs {
    
    protected static 
$jobs = array();

    public static function 
addJob(Job $job) {
        
self::$jobs[] = $job;
    }
    
    public static function 
hasJobs() {
        return 
count(self::$jobs) !== 0;
    }
    
    public static function 
getJob() {
        
$jobIndex array_rand(self::$jobs);
        
$job self::$jobs[$jobIndex];
        unset(
self::$jobs[$jobIndex]);
        return 
$job;
    }
    

Dann die Klasse Worker, die Observeable implementiert und noch ein paar "Worker-spezifische" Attribute und Methoden enthält:
PHP-Code:
class Worker implements Observeable 

    protected 
$observers = Array();
    
    protected 
$finishedJobs = array();
    protected 
$finishedJobsCount 0;
    public 
$currentJob null;
    protected 
$name '';
    protected 
$status 'sleeping';
    const 
JOBS_PER_DAY 6;
    
    public function 
__construct($name) {
        
$this->name $name;
        
$this->status 'awake';
    }
    
    public function 
addObserver(Observer $observer) {
        
$this->observers[] = $observer;
    }
    
    public function 
removeObserver(Observer $observer) {
        
$offset array_search($observer$this->observers);
        if(
$offset !== false) unset($this->observers[$offset]);
    }
    
    public function 
notifyObservers() {
        foreach(
$this->observers as $observer$observer->update($this);
    }

    public function 
getName() {
        return 
$this->name;
    }
    
    public function 
getStatus() {
        return 
$this->status;
    }
    
    public function 
startWorking() {
        echo 
$this->getName().': I start working!<br />';
        
$this->status 'working';
        while(
$this->finishedJobsCount self::JOBS_PER_DAY) {
            if(
is_null($this->currentJob)) {
                
$this->getJob();
            }
            
$this->finishJob();
        }
        
$this->status 'doneForToday';
        echo 
$this->getName().": I'm done for today.<br />";
        
$this->notifyObservers();
    }
    
    public function 
addFinishedJob(Job $job) {
        
$this->finishedJobs[] = $job;
    }
    
    public function 
getJob() {
        
$this->status 'needJob';
        
$this->notifyObservers();
    }

    public function 
finishJob() {
        
$this->status 'done';
        
$this->finishedJobsCount++;
        
$this->notifyObservers();
    }
    

Das "Grundprinzip" hierbei ist: Worker instanziieren, Observer zuweisen, arbeiten (-> Job bekommen, Job abarbeiten und direkt neuen Job bekommen, sollte das Arbeitspensum noch nicht erschöpft sein) und sich verabschieden.
Neben dem Worker gibt es noch die Contacts:
PHP-Code:
class Contact implements ObserverObserveable {
    
    protected 
$observers = Array();
    
    protected 
$name '';
    protected 
$status '';
    
    public function 
__construct($name) {
        
$this->name $name;
        
$this->status 'working';
    }
    
    public function 
getName() {
        return 
$this->name;
    }
    
    public function 
getStatus() {
        return 
$this->status;
    }
    
    public function 
update(Observeable $observedObject) {
        switch (
$observedObject->getStatus()) {
            case 
'done':
                
$observedObject->currentJob->markAsDone();
                
$observedObject->addFinishedJob($observedObject->currentJob);
                echo 
$observedObject->getName().': '.$observedObject->currentJob->getTitle().' finished!<br />';
            case 
'needJob':
                if(!
Jobs::hasJobs()) {
                    echo 
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'.$this->getName().': I need new Jobs!<br />';
                    
$this->status 'needJobs';
                    
$this->notifyObservers();
                    
$this->status 'working';
                }
                
$job Jobs::getJob();
                
$job->assignWorker($observedObject);
                
$observedObject->currentJob $job;
                break;
            case 
'doneForToday':
                
$this->sayGoodbye($observedObject->getName());
                break;
        }
    }
    
    protected function 
sayGoodbye($workerName) {
        echo 
$this->getName().': See you tomorrow, '.$workerName.'!';
    }
    
    public function 
addObserver(Observer $observer) {
        
$this->observers[] = $observer;
    }
    
    public function 
removeObserver(Observer $observer) {
        
$offset array_search($observer$this->observers);
        if(
$offset !== false) unset($this->observers[$offset]);
    }
    
    public function 
notifyObservers() {
        foreach(
$this->observers as $observer$observer->update($this);
    }


die die Worker beobachten, ihm einen ersten Job (-> 'needJob') und die weitere Jobs (-> 'done') zuweisen und sie verabschieden (-> 'doneForToday').

Zuletzt gibt es noch den Boss:
PHP-Code:
class Boss implements Observer {
    
    public function 
update(Observeable $observedObject) {
        switch (
$observedObject->getStatus()) {
            case 
'needJobs':
                echo 
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boss: Here are 10 new Jobs!<br />';
                
$this->getJobs();
                break;
        }
    }
    
    protected function 
getJobs() {
        for(
$i 1$i <= 10$i++) {
            
Jobs::addJob(new Job(mt_rand(5100).'. job'));
        }
    }


der neue Jobs ordert, wenn ihn ein Contact informiert, dass keine Jobs mehr da sind.

Mein Beispielablauf sieht wie folgt aus:
PHP-Code:
$job1 = new Job('1. job');
$job2 = new Job('2. job');
$job3 = new Job('3. job');
$job4 = new Job('4. job');

Jobs::addJob($job1);
Jobs::addJob($job2);
Jobs::addJob($job3);
Jobs::addJob($job4);

$boss = new Boss();

$contact = new Contact('Kardey\'s Ansprechpartner');
$contact->addObserver($boss);

$worker = new Worker('Kardey');
$worker->addObserver($contact);
$worker->startWorking(); 
und er liefert auch das, was er soll:
Code:
Kardey: I start working!
Kardey: 2. job finished!
Kardey: 1. job finished!
Kardey: 4. job finished!
Kardey: 3. job finished!
     Kardey's Ansprechpartner: I need new Jobs!
          Boss: Here are 10 new Jobs!
Kardey: 61. job finished!
Kardey: 56. job finished!
Kardey: I'm done for today.
Kardey's Vorarbeiter: See you tomorrow, Kardey!
Wäre das in diesem Zusammenhang auch ein (halbwegs) vernüntiges Beispiel, um das Observer-Pattern in der Praxis einzusetzen?

Bevor ich nochmal die zweite Frage aus meinem Ausgangsbeitrag aufnehmen möchte, habe ich noch eine "Detail-Frage":
Wenn der Worker einen Job abgearbeitet hat, fügt der Contact diesen Job ins $finishedJobs-Array des Workers ein (damit der Worker nachher eine Übersicht hat, welche Jobs er begearbeitet hat, oder warum auch immer ). Dafür benötige ich die Methode addFinishedJob(Job $job) des Workers. Wie kann ich hier aber verhindern, dass der Worker die Methode selbst aufruft, ohne den Job überhaupt ausgeführt zu haben? Es müsste also sicher gestellt sein, dass die Methode nur von Contacts aufgerufen werden kann.
__________________
Kardey
Kardey ist offline   Mit Zitat antworten
Alt 09.03.2010, 20:44  
moderatives Dielektrikum
 
Benutzerbild von nikosch
 
Registriert seit: 21.05.2008
Beiträge: 34.241
PHP-Kenntnisse:
Fortgeschritten
nikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz sein
Standard

Zitat:
halbwegs
Meine Kritik wäre, dass Observer-Pattern ja eigentlich eine Art Parallelität abbilden, die bei Dir nicht gegeben ist, da immer nur ein Worker seine Jobs per Schleife abarbeiten kann. Aus Sicht des Workers funktionierts, global macht das so wenig Sinn.
Zitat:
Wenn der Worker einen Job abgearbeitet hat, ...
Naja, es ist ganz einfach: Im wahren Leben machst Du Deine Liste oder Dein Chef seine. Wenn Chef das personenbezogen machen will, dann muss er selbst dafür sorgen (assoz. Array)
__________________
--
One pixel is still too big. Please make it smaller. ASAP.

Initiative Mittelstand.
Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers.
--
nikosch ist offline   Mit Zitat antworten
Alt 09.03.2010, 21:11  
Neuer Benutzer
 
Registriert seit: 09.03.2010
Beiträge: 23
PHP-Kenntnisse:
Anfänger
Kardey befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Zitat von nikosch Beitrag anzeigen
Meine Kritik wäre, dass Observer-Pattern ja eigentlich eine Art Parallelität abbilden, die bei Dir nicht gegeben ist, da immer nur ein Worker seine Jobs per Schleife abarbeiten kann. Aus Sicht des Workers funktionierts, global macht das so wenig Sinn.
Okay, trotzdem intensivem Nachdenken muss ich gestehen, dass ich nicht wirklich verstanden habe, was das bedeuten soll .
(Trotzdem mal meine Gedanken dazu: Meinst Du mit der "Parallelität", dass mehrere Workers "gleichzeitig" aktiv sind, oder das die Workers gleichzeitig mehrere andere Objekte "verändern", wie es beim Beispiel auf Wikipedia der Fall ist (Messergebnisse und ihre Darstellung in verschiedenen Diagrammen)?)

Könnte vielleicht jemand ein praktisches Beispiel "liefern", dass auch irgendwo implementiert ist, also auch in der Praxis angewandt wird?
__________________
Kardey
Kardey ist offline   Mit Zitat antworten
Alt 10.03.2010, 07:55  
moderatives Dielektrikum
 
Benutzerbild von nikosch
 
Registriert seit: 21.05.2008
Beiträge: 34.241
PHP-Kenntnisse:
Fortgeschritten
nikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz sein
Standard

Zitat:
dass mehrere Workers "gleichzeitig" aktiv sind
Das meine ich. Nun ist (in einem Request) Parallelität nicht wirklich abbildbar, deswegen hinkt irgendwie das ganze Beispiel.
Observer in PHP muss man deswegen vermutlich etwas anders bewerten, als in Echtzeitapplikationen. Sie dienen hier wohl mehr zur Entkoppelung von Objekten als zur Eventsteuerung.
__________________
--
One pixel is still too big. Please make it smaller. ASAP.

Initiative Mittelstand.
Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers.
--
nikosch ist offline   Mit Zitat antworten
Alt 14.03.2010, 16:59  
Neuer Benutzer
 
Registriert seit: 09.03.2010
Beiträge: 23
PHP-Kenntnisse:
Anfänger
Kardey befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Zitat von nikosch Beitrag anzeigen
Nun ist (in einem Request) Parallelität nicht wirklich abbildbar, deswegen hinkt irgendwie das ganze Beispiel.
Observer in PHP muss man deswegen vermutlich etwas anders bewerten, als in Echtzeitapplikationen. Sie dienen hier wohl mehr zur Entkoppelung von Objekten als zur Eventsteuerung.
Ja, stimmt auch wieder.
Aber meinst Du wirklich "Entkopplung", denn eigentlich werden die Objekte doch gerade durch das Pattern aneinander gekoppelt (Worker <-> Contact <-> Boss)?

Dürfte ich jetzt nochmals meine zweite Frage aus meinem ersten Beitrag aufgreifen, in wie weit das Pattern für Erweiterungen geeignet ist, man also beispielsweise durch ein PlugIn einen zusätzlichen Observer hinzufügt.

Hierzu habe ich mir aber in der Zwischenzeit mal Gedanken gemacht :
In den eigentlichen Programmablauf komme ich ja nicht rein, es sei denn ich würde die Observers eines Observeable-Objektes beispielweise in einer Datendank speichern, also vereinfach ausgedrückt so:
Code:
Observeable | Observer
Hier müsste ich dann natürlich bevor ich ein Observeable-Objekt instanziiere (bzw. bevor ich ihm seine Observer bekannt mache) die Observers aus der Datenbank laden. Hier könnte sich dann ein PlugIn relativ einfach einklinken, indem es einfach einen zusätzlichen Datenbankeintrag anlegt.

Wäre das eine realistische Vorgehensweise?
__________________
Kardey
Kardey ist offline   Mit Zitat antworten
Alt 14.03.2010, 17:49  
Erfahrener Benutzer
 
Registriert seit: 27.06.2009
Beiträge: 498
PHP-Kenntnisse:
Anfänger
cetalian sorgt für eine eindrucksvolle Atmosphärecetalian sorgt für eine eindrucksvolle Atmosphäre
Standard

Zitat:
Zitat von Kardey Beitrag anzeigen
Ja, stimmt auch wieder.
Aber meinst Du wirklich "Entkopplung", denn eigentlich werden die Objekte doch gerade durch das Pattern aneinander gekoppelt (Worker <-> Contact <-> Boss)?
Die Kopplung ist aber deutlich loser als ohne das Pattern. Sonst müsstest du z.b. direkt in den Code des Observables reinschreiben "sags dem und jenem und hier noch". So kann es dem Observable vollkommen gleichgültig sein wer es beobachtet, ich habe jetzt dein Worker/Contact/Boss Beispiel nicht nachvollzogen, aber der doppelte Pfeil ist dort wohl falsch. Sollte Worker observable sein und Boss der Observer dann passt "Worker <- Boss" besser.
cetalian ist offline   Mit Zitat antworten
Alt 14.03.2010, 18:35  
Neuer Benutzer
 
Registriert seit: 09.03.2010
Beiträge: 23
PHP-Kenntnisse:
Anfänger
Kardey befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Zitat von cetalian Beitrag anzeigen
Die Kopplung ist aber deutlich loser als ohne das Pattern. Sonst müsstest du z.b. direkt in den Code des Observables reinschreiben "sags dem und jenem und hier noch". So kann es dem Observable vollkommen gleichgültig sein wer es beobachtet
Stimmt auch wieder.

Zitat:
Zitat von cetalian Beitrag anzeigen
ich habe jetzt dein Worker/Contact/Boss Beispiel nicht nachvollzogen, aber der doppelte Pfeil ist dort wohl falsch. Sollte Worker observable sein und Boss der Observer dann passt "Worker <- Boss" besser.
Meine doppelten Pfeile sollten eher die Kopplung symbolisieren, wobei Deine Wahl der Pfeile bezogen auf das Observer/Observeable-Verhältnis natürlich besser passen.
__________________
Kardey
Kardey ist offline   Mit Zitat antworten
Alt 15.03.2010, 17:11  
moderatives Dielektrikum
 
Benutzerbild von nikosch
 
Registriert seit: 21.05.2008
Beiträge: 34.241
PHP-Kenntnisse:
Fortgeschritten
nikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz seinnikosch kann auf vieles stolz sein
Standard

Die Frage mit den Plugins verstehe ich nur halb. Grob skizziert:

- Grundlegend hindert Dich niemand daran, mehere Observer zu verwenden
- Willst Du einen bestehenden Observer verwenden, muss das observered Object eine Schnittstelle bedienen, die dem Observer bekannt ist.
__________________
--
One pixel is still too big. Please make it smaller. ASAP.

Initiative Mittelstand.
Die wichtigste Gestaltungsregel im Screendesign ist Pi mal Daumen des Arbeitgebers.
--
nikosch ist offline   Mit Zitat antworten
Antwort


Themen-Optionen
Thema bewerten
Thema bewerten:

Forumregeln
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are an
Gehe zu

Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
Design Pattern BlackScorp PHP Tipps 2009 7 06.01.2010 19:04
[Erledigt] Crockfords Functional Pattern #fail phpdummi JavaScript, Ajax und mehr 1 20.06.2009 21:38
Welches Pattern? Creator PHP-Fortgeschrittene 19 14.04.2009 21:46
[Erledigt] preg_match und die Pattern Spoiler PHP Tipps 2009 9 13.02.2009 08:29
Formulargenerierung | Composite Pattern moveax1 PHP-Fortgeschrittene 13 27.01.2009 09:44
preg_replace: Gleiche Platzhalter im Pattern Lavalampe PHP Tipps 2009 5 16.01.2009 21:42
Singleton Pattern Verstaendisproblem Luka PHP Tipps 2008 13 19.12.2008 19:00
PHP Command Pattern = Module? meGa Software-Design 4 14.12.2008 12:35
[Erledigt] Formularvalidierung, preg_match pattern Problem moveax1 PHP Tipps 2008 4 12.12.2008 16:48
Design Pattern Harry B. Software-Design 7 02.09.2008 20:49
Bild vor Link pattern TimmaY PHP Tipps 2007 5 21.03.2007 18:06
Events, Observer, Henne-Ei-Problem Basti PHP-Fortgeschrittene 3 09.05.2006 15:53
Folgendes Pattern PHP Tipps 2005 5 01.03.2005 12:41
Probleme mit Pattern bei Templateersetzung RudiS PHP-Fortgeschrittene 2 19.02.2005 22:09
preg_match --> pattern PHP Tipps 2004 4 27.08.2004 18:12

Besucher kamen über folgende Suchanfragen bei Google auf diese Seite
verhindern endlosschleifen observer, observer pattern anwendungsfälle, endlosschleifen observer verhindern, observer endlosschleifen verhindern, observer pattern endlosschleifen, observer pattern beispiel messergebnis, observer observable gleichzeitig, observer pattern php, observable und gleichzeitig observer, observer beispiel, beispiel zu observer pattern

Alle Zeitangaben in WEZ +1. Es ist jetzt 07:04 Uhr.




Powered by vBulletin® Version 3.7.2 (Deutsch)
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
Aprilia-Forum, Aquaristik-Forum, Liebeskummer-Forum, Zierfisch-Forum, Geizkragen-Forum