AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Zyklusproblem ( Circular reference ) [solved]
Thema durchsuchen
Ansicht
Themen-Optionen

Zyklusproblem ( Circular reference ) [solved]

Ein Thema von DGS · begonnen am 22. Apr 2007 · letzter Beitrag vom 24. Apr 2007
Antwort Antwort
Seite 1 von 2  1 2      
DGS

Registriert seit: 10. Jul 2004
65 Beiträge
 
Delphi 6 Enterprise
 
#1

Zyklusproblem ( Circular reference ) [solved]

  Alt 22. Apr 2007, 22:27
Moi.

Ich hatte das Wochenende vorgehabt Programm schön OO zu machen, was fehlgeschlagen ist.
Genauer gesagt, Circular Reference ist mein Problem.
Verschiebend er Unit in die implementation funktioniert nicht, da ich die Komponenten im interface verwende.

Folgendes Szenario (vereinfacht)
Unit1 ist meine Form1.
Inut2 enthält dann das eigentliche Programm (in wirklichkeit teilt die sich noch weiter auf )
Jedenfalls empfängt Unit1 die Ereignisse von der Form ( zB ButtonKlick ) und soll an meine Unit2 weiterleiten. Diese wertet diese aus ( eigentlich weiterleiten an weitere Klassen ) und reagiert halt drauf. In manchen Fällen brauch ich aber Zugriff auf die Form1.
Bei mir ist es so, dass die Unit2 eine Instanz der Unit2 erstellt. Ich wollte beim Create noch als Parameter die Form1 übergeben. Um diese aber in unit2 abspeichern zu können brauch ich da die Unit1 im Interface.

Generell heißt es, Crosslinks ( gegenseitige verlinkung ) sei ein ganz schlechter Programmierstill.
Was lässt sich da machen? Denn selbst wenn ich Pointer der wichtigsten Komponenten in record packe und die schicke, sozusagen eine Kopie der Form1 erstelle, hilft mir das nicht weiter.
Eine der Funktionen steuert die Sichtbarkeit der Form1 ( Form1.Hide und Form1.Show ). Ich wüsste nicht, wie ich das anstellen sollte.

Einzigster Geistesblitz kam mir gerade eben beim schreiben hier.
Ich hätte evtl die einzigste Chance, wenn ich in Unit1 die uses in implementationsabschnitt verschiebe. Jedenfalls erinnere ich mich da eine keine gekoppelten Funktionen im Interface, die nicht in implementation überleben würden.

Andere Alternative wäre es prozeduren auf function umzustellen und in der Form1 dann den Rückgabewert auszuwerten. Glaub, in dem Fall würde die abstraktion mich aber meinem Ziel nicht weiterbringen, den Quellcode übersichtlicher zu gestallten.
Vorschläge für sinnvolle Signatur bitter per PN
  Mit Zitat antworten Zitat
Benutzerbild von 3_of_8
3_of_8

Registriert seit: 22. Mär 2005
Ort: Dingolfing
4.129 Beiträge
 
Turbo Delphi für Win32
 
#2

Re: Zyklusproblem ( Circular reference )

  Alt 22. Apr 2007, 22:40
Ich löse das immer so: Wenn möglich, verschiebe ich die Uniteinbindung in den implementation-Abschnitt. Wenn das nicht geht, weil ich Typendefinitionen der anderen Unit brauche, schmeiße ich einfach alles in eine Unit.
Manuel Eberl
„The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.“
- Terry Pratchett
  Mit Zitat antworten Zitat
DGS

Registriert seit: 10. Jul 2004
65 Beiträge
 
Delphi 6 Enterprise
 
#3

Re: Zyklusproblem ( Circular reference )

  Alt 22. Apr 2007, 23:40
Danke...
Also ist die einzigste Lösung das ganze in soweit zu verändern, dass man in einer Unit den Uses Teil auch in den implementation Bereich verschieben kann?

Was die extra Unit betrifft, in die man alles reintut. Im Grunde ist das meine Unit2. Sie ist das Bindeglied zwischen der Form und den eigentlichen Funktionen.

Wenn du mit alles in eine unit meintest, ich solle es in die Unit1 packen, wo die Form wäre, dann wär ich da, wo ich gestartet bin: 1500 Zeilen in einer Unit. Finde, dass das keinesfalls ein schöner Still sein kann.
Vorschläge für sinnvolle Signatur bitter per PN
  Mit Zitat antworten Zitat
Reinhard Kern

Registriert seit: 22. Okt 2006
772 Beiträge
 
#4

Re: Zyklusproblem ( Circular reference )

  Alt 23. Apr 2007, 01:48
Zitat von DGS:
Moi.

Ich hatte das Wochenende vorgehabt Programm schön OO zu machen, was fehlgeschlagen ist.
Genauer gesagt, Circular Reference ist mein Problem.
Verschiebend er Unit in die implementation funktioniert nicht, da ich die Komponenten im interface verwende.
....
Hallo,

folgendes Unit-Modell vermeidet einige Fallstricke von vornherein:

1. es gibt i.A. eine Main-Unit mit der Hauptform der Anwendung.

2. Funktionen wie Einlesen einer Datei, Bearbeiten usw. werden zumindest ab einem bestimmten Umfang ausgelagert in eigene Units, besonders wenn sie eine eigene Form haben. Diese Units werden in der Main-Unit als uses meist im Interface deklariert, da sie von dieser ja benutzt werden (von wem sonst); manche brauchen auch Funktionen aus der Main-Unit, dann wird diese als uses im Implementationsteil der Sub-Unit deklariert.

3. Datentypen für das Programm werden oft an vielen Stellen benutzt und sind daher Ursache für Zirkularreferenzen; diese lagere ich von vornherein aus in eine "general"-unit, was dazu noch den Vorteil hat, dass alle solche Typdefinitionen an einer Stelle versammelt sind. Diese Unit ist überall in Interface als uses deklariert. Zweckmässigerweise werden hier auch generell benötigte Funktionen untergebracht wie etwa Umwandlungen zwischen den Typen oder eine allgemeine Status-Ausgabe; diese wird zwar auf die Main-Unit zugreifen müssen, aber im Implementation-Teil, macht also nix.

Datentypen können durchaus auch Klassen mit Methoden sein - eine Klasse, die wirklich nur einen Datentyp implementiert, hat keinen logischen Grund auf die MainForm zuzugreifen (zumindest nicht im Interface, und auch in Implementation ist es schlecht programmiert).

Mir ist bisher kein Fall unterlaufen, auch nicht bei mehr als 100 000 Zeilen Source, der sich damit nicht widerspruchsfrei aufbauen liess.

Gruss Reinhard
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#5

Re: Zyklusproblem ( Circular reference )

  Alt 23. Apr 2007, 07:27
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.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
DGS

Registriert seit: 10. Jul 2004
65 Beiträge
 
Delphi 6 Enterprise
 
#6

Re: Zyklusproblem ( Circular reference )

  Alt 23. Apr 2007, 14:01
wow, danke Reinhard Kern und alzaimar für die detailreichen tipps!

Aber zB bei Tipp2 von Reinhard komm ich nicht ganz klar.
Zitat von Reinhard Kern:
2. Funktionen wie Einlesen einer Datei [...] werden zumindest ab einem bestimmten Umfang ausgelagert in eigene Units[...]. Diese Units werden in der Main-Unit als uses meist im Interface deklariert, da sie von dieser ja benutzt werden (von wem sonst); manche brauchen auch Funktionen aus der Main-Unit, dann wird diese als uses im Implementationsteil der Sub-Unit deklariert.
Und zwar beim einlesen der Dateien (in meinem Fall eine INI ) brauch ich Zugriff auf die MainForm ( bzw Form1 ). Ich speichere zB gerne Fensterposition in der INI Datei ab.
Also muss ich aus der SubUnit irgendwie auf die Form1 zugreifen. Wenn ich aber beim erstellen der SubUnit als Parameter für Create noch Form1 übergebe, um drauf zugreifen zu können, muss ich in SubUnit die MainUnit im interface haben.

Oder hab ich da einen Denkfehler?

Gruss
Vorschläge für sinnvolle Signatur bitter per PN
  Mit Zitat antworten Zitat
Apollonius

Registriert seit: 16. Apr 2007
2.325 Beiträge
 
Turbo Delphi für Win32
 
#7

Re: Zyklusproblem ( Circular reference )

  Alt 23. Apr 2007, 15:29
Was willst du überhaupt mit create erstellen? Und die konkreten Parameter eines Konstruktors werden sowieso erst in der implementation benötigt (=nur da werden Konstruktoren aufgerufen), daher musst du die form1unit auch erst in der implementation einbinden.
Wer erweist der Welt einen Dienst und findet ein gutes Synonym für "Pointer"?
"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."
  Mit Zitat antworten Zitat
DGS

Registriert seit: 10. Jul 2004
65 Beiträge
 
Delphi 6 Enterprise
 
#8

Re: Zyklusproblem ( Circular reference )

  Alt 23. Apr 2007, 15:48
Hatte zwischwendurch viel JAVA programmiert ( zu viel wenn mich einer fragt ). Dachte den konstruktor so aufzubauen in der SubUnit:

Delphi-Quellcode:
unit SubUnit;

interface

uses
  Unit1;

type
  tHelp = class
  private
    fvater : tForm1;
  public
    constructor Create(vater : tForm1)
  end;

implementation

constructor tHelp.Create(vater : tForm1);
begin
  inherited Create;

  fVater := vater;
end;

end.
Dachte in der Form abzuspeichern, von wo die SubUnit aufgerufen wird, damit ich hier zB fVater.left := 100; benutzen kann.

*edit: [ CODE ] durch [ DELPHI ] ersetzt.
Vorschläge für sinnvolle Signatur bitter per PN
  Mit Zitat antworten Zitat
Apollonius

Registriert seit: 16. Apr 2007
2.325 Beiträge
 
Turbo Delphi für Win32
 
#9

Re: Zyklusproblem ( Circular reference )

  Alt 23. Apr 2007, 16:15
Das ist aber absolut kein Problem. Als Parametertyp gibst du TForm an, denn das hat ja schon die meisten Eigenschaften. Wenn du auf etwas in TForm1 Deklariertes zugreifen willst, musst du einfach (Vater as TForm1).button1 schreiben und natürlich deine Unit in die Implementation-uses einbinden.

Wenn du aber nur eine Instanz von TForm1 hast, kannst du dir den Parameter schenken, du greifst in der implementation auf form1 zu.
Hoffe geholfen zu haben
Apollonius
Wer erweist der Welt einen Dienst und findet ein gutes Synonym für "Pointer"?
"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."
  Mit Zitat antworten Zitat
DGS

Registriert seit: 10. Jul 2004
65 Beiträge
 
Delphi 6 Enterprise
 
#10

Re: Zyklusproblem ( Circular reference )

  Alt 23. Apr 2007, 16:19
Zitat von Apollonius:
Wenn du aber nur eine Instanz von TForm1 hast, kannst du dir den Parameter schenken, du greifst in der implementation auf form1 zu.
Hoffe geholfen zu haben
Apollonius
Ja, hab nur eine Instanz der Form.

Und glaub, ich weis jetzt, was du meintest. In der Unit1. wird ja eine globale Variable Form1 deklariert.
*auf Kopf klatsch*
Dann reicht ja Unit1 in implementation aus und greif auf diese Variable zu. Dann brauch ich die nichtmal an meine SubUnits zu übergeben. Die SubUnits holen sich einfach die Form. genial. Einfach, aber genial.

Danke, werds mal gleich testen. Hätt ich bloss letzte Woche gefragt, wär mein Wochenende gerettet.

Gruss
Vorschläge für sinnvolle Signatur bitter per PN
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:56 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz