Autor Beitrag
MitschL
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 211

Win 98 SE, Win 2000
D5 Pers, D6 Pers und D7 Pro
BeitragVerfasst: Mo 24.01.05 13:42 
'Die Factory' oder 'Wie erweitere ich schnell/schön'

So manches mal stand ich vor dem Problem, daß ich eine unbekannte Anzahl an Varianten von einer Funktion hatte. Sie sollten eine Aufgabe auf unterschiedliche Art und Weise handhaben.

Um bei dem Namen zu bleiben, stelle ich mir vor, daß ich eine Auswahl an Arbeitern habe. Jeder tut etwas anderes. Mich interessiert nur das Ergebnis. Ich möchte mich an den Eingang der Halle stellen und rufen: 'Das Werkstück muß von einem Fräser bearbeitet werden, Zackzack!'

Als erstes nehme ich mal die Arbeiter her. Ich habe es mir einfach gemacht, indem ich diese Arbeiter in eine Unit packte.
ausblenden volle Höhe Delphi-Quelltext
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:
unit UArbeiter;

interface

const INGENIEUR     = 1;
const EINRICHTER    = 2;
const FRAESER       = 3;
const LAGERARBEITER = 4;
const LEHRLING      = 5;
const PUTZFRAU      = 6;

type TArbeiter = class( TObject )
  private
  //...
  public
    // muß überschreibbar sein
    // muß nur abstract sein, wenn es hier
    // kein Standard-Verhalten gibt
    procedure Arbeite(); virtualabstract;
end;

// Wichtig! Muß vom TArbeiter abgeleitet sein!
TIngenieur = class(TArbeiter)
  //...
  public
    // Hier kommt die Arbeit des Ingenieurs
    procedure Arbeite(); override;
end;

// dito
TEinrichter = class(TArbeiter)
  //...
  public
    procedure Arbeite(); override;
end;

// und genau so für TFraeser, TLagerarbeiter, 
// TLehrling und TPutzfrau
implementation
// die Arbeit eines jeden muß natürlich 
//nach wie vor auch hier rein

Damit habe ich die Möglichkeit, die angefallene Arbeit zu verrichten.

Als nächstes nehme ich mal die Wahl des richtigen Arbeiters her:
ausblenden Delphi-Quelltext
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:
unit UWerkhalle;

interface

uses UArbeiter;

type TWerkhalle = class( TObject)
  public
    class function Gib_Arbeiter( Typ: Integer ): TArbeiter;
end;
//...
implementation
//...
class function TWerkhalle.Gib_Arbeiter( Typ: Integer ): TArbeiter;
begin
  case Typ of:
    UArbeiter.INGENIEUR:     Result := TIngenieur.Create();
    UArbeiter.EINRICHTER:    Result := TEinrichter.Create();
    UArbeiter.FRAESER:       Result := TFraeser.Create();
    UArbeiter.LAGERARBEITER: Result := TLagerarbeiter.Create();
    UArbeiter.LEHRLING:      Result := TLehrling.Create();
    UArbeiter.PUTZFRAU:      Result := TPutzfrau.Create();
  else
    // nil oder Standard-Arbeiter möglich
    // also: Result := TArbeiter.Create(); //wenn nicht abstract
    Result := nil;
end;
(Ich habe die Wahl als Klassenmethode deklariert, weil ich sie separat haben wollte und gern auf globale Funktionen/Methoden verzichte.)

Das erscheint erstmal viel und vielleicht umständlich, bietet aber einen Vorteil. Der Aufruf ist komplett losgelöst von der hier gezeigten Funktion.

Nutzen könnte ich die Arbeiter nähmlich so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
unit Hauptprogramm;

interface

uses UArbeiter, UWerkhalle;
//...
implementation
//...
procedure THauptprogramm.Mach_Was( werkstueck: TWerkstueck );
var arbeiter: TArbeiter;
//...
begin
  //...
  if werkstueck.typ = UTyp.Dreck then
    arbeiter := TWerkhalle.Gib_Arbeiter( UArbeiter.Putzfrau )
  else
    arbeiter := TWerkhalle.Gib_Arbeiter( UArbeiter.FRAESER );

  arbeiter.Arbeite();
  //...
end;


Bei einer Erweiterung muß ich beachten, daß der neue Arbeiter ein Nachfahre des Arbeiters ist (Beamte sind keine Arbeiter :D ) und daß ich die Gib_Arbeiter-Methode um diesen Arbeiter erweitere. Danach kann ich diesen Arbeiter genauso benutzen, wie die anderen.

Ich möchte noch ein weiteres Beispiel hernehmen: Ein Konverter. Zwischen die Schritte: Lade_Daten und Schreibe_Daten steht: Konvertiere(). Ich kenne das Ausgangs- und Zielformat. Mir ist aber egal, wie er konvertiert. Ich rufe stets Konvertiere() auf, und das war's.
Oder wie wäre es mit einem Taschenrechner? Die Funktion Berechne() nimmt zwei Zahlen auf und entscheidet aufgrund des verbindenden Zeichens - ist somit auch schön erweiterbar.

Viel Spaß damit!

Anmerkungen möchte ich schon noch machen. So bin ich für jedwede Berichtigung dankbar, denn dieses Werk ist hier ungetestet reingeschrieben worden.

gegrüßt!

[edit] Habe zwei Tippfehler beseitigt. :?
[edit2] BenBE wies mich auf weitere fehlerhafte Details hin. Hab Dank dafür!

_________________
"Bloßes Ignorieren ist noch keine Toleranz." (Theodor Fontane)