Reinhards Vorschläge sind erstmal grundsätzlich korrekt, bis auf Kleinigkeiten:
Zitat von
Reinhard Kern:
2. Funktionen ... werden ... ausgelagert in eigene Units, ... Diese Units werden in der Main-
Unit als uses meist im Interface deklariert, da ..
Wenn Du die
Unit im
Interface-Abschnitz der Hauptunit gar nicht verwendest, dann solltest Du die
Unit auch erst im Uses-Abschnitt der
Implementation angeben. So vermeidet man von vorneherein
fast immer eine zirkuläre Referenz.
Delphi-Quellcode:
Unit A;
Interface
Uses UnitB;
// deklariert TTypeB, die aber im Interfaceabschnitt gar nicht verwendet wird ...
Implementation
Procedure Foo;
Var
b : TTypeB;
// ... sondern erst hier
...
Ist nicht korrekt, aber
Delphi-Quellcode:
Unit A;
Interface
Implementation
Uses UnitB;
// Hier gehörts hin.
Procedure Foo;
Var
b : TTypeB;
...
ist perfekt.
Grundsätzlich gilt;
Deklarationsreferenzen (
Uses,
Var) sollten so nah wie möglich an der ersten Verwendung verschoben werden. C# und Java z.B. gehen ja bezüglich der Variablen so weit, das man die erst unmittelbar in dem Codeblock deklariert, in dem sie verwendet wird; Delphi leider nicht.
Wenn nun einmal eine
Unit A im Interface-Abschnitt etwas aus
Unit B benötigt, und umgekehrt, dann kann man das in eine dritte
Unit C auslagern.
Das ist, was Reinhard im Punkt 3. ansprichst.
Hier mal ein Beispiel;
Delphi-Quellcode:
Unit UnitA;
Interface
Uses UnitB;
Type
TTypeA =
Class
fB : TTypeB
End;
....
Unit UnitB;
Interface
Uses UnitA;
Type
TTypeB =
Class
fA : TTypeA;
End;
Eine zirkuläre Referenz, die sich durch o.g. Regeln nicht auflösen lässt.
Hier kannst Du z.B. die Deklaration von TTypeA und TTypeB in eine separate
Unit auslagern (Reinhards Vorschlag). Wenn nun aber die Units eigentlich nur diese eine Klasse deklarieren und implementieren, würde das natürlich den Konflikt auflösen, wäre aber u.U. bezüglich der Übersichtlichkeit kontraproduktiv.
Hier würde ich erstmal nachdenken, was diese zirkuläre Referenz bedeutet: Wenn Du keinen Designfehler gemacht hast, bedeutet das, das wir es hier mit zwei voneinander abhängigen Klassen zu tun haben, die dann aber auch wirklich ein eine
Unit gehören: Die Übersichtlichkeit würde eigentlich erhöht, da 'zusammenwächst, was zusammengehört'.
Wem das dann doch zu unübersichtlich wird, der deklariert z.B. abstrakte Vorfahren von A und B in einer Hilfsunit:
Delphi-Quellcode:
Unit ABTypes;
Interface
Type
TAbstractTypeA =
Class
<Abstrakte Deklaration der Klasse>
End;
TAbstractTypeB =
Class
<Abstrakte Deklaration der Klasse>
End;
End.
Unit A greift nun auf ABTypes zu und definiert '
fB' als 'TAbstractTypeB'. Zirkuläre Referenz aufglöst, Übersichtlichkeit und Dateistruktur erhalten.
Delphi-Quellcode:
Unit UnitA;
Interface
Uses ABTypes;
Type
TTypeA =
Class
fB : TAbstractTypeB
End;
Hier gibt es keine Vorschrift: Einzig und allein das 'Design' sowie das Datenstrukturlayout zählen.