Nachdem mein Projekt wieder mal ne Weile auf Eis lag und mich immer wieder dazu bringen wollte, es mal wieder anzugehen, bin ich nun zu dem Schluss gekommen, es noch mal komplett neu zu designen. Gerade die Problematik, dass alle meine Teilmanager (Camera, Window, Renderer etc.) als globale Variablen deklariert sind, ist etwas, das mir jedes Mal aufs Neue ins Auge sticht und von mal zu mal mehr missfällt.
Allerdings war seinerzeit der Grund dafür, dass ich anders einen zirkulären Unitaufruf nicht vermeiden konnte. UnitA soll in UnitB eingebaut sein, aber gleichzeitig auch UnitB selbst kennen können, sodass man darauf aufwärts zugreifen kann.
Etwa so:
Code:
Unit foo:
Type foo =class;
Myboo: Boo;
….
Code:
Unit boo:
Type boo =class
Private
Parent: foo;
Ich müsste in beiden Units in der ersten Usesklausel die jeweils andere vermerken und zack „Zirkulaerer Unitaufruf“. Ich habe schon des Öfteren gehört, dass sich dies durch „intelligentes Klassendesign“ vermeiden ließ. Schließlich komme ich aber zu dem Schluss, meine Klassen effektiv nicht besser designen zu können, da sich kaum noch etwas konsolidieren ließe, was letztlich ohnehin keinen Vorteil brächte.
Ich möchte auch nicht auf Teufel-komm-raus meinen Code malträtieren und meine Klassen umbiegen, und in ihrer Funktion einschränken, nur damit dieser Fehler beseitigt wird.
Ich würde gern mal wissen, ob hier einer noch einen ganz schlauen Kniff parat hat, um das zu umgehen. Ansonsten muss ich wohl echt mal über einen Umstief auf Java oder C++ nachdenken…
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Afaik kann man das wirklich nur durch intelligentes Klassendesign machen. Eine Andere Möglichkeit wäre z.B.:
Code:
unit Base;
type
TBaseClass =class
..
end;
Code:
unit Foo;
...
uses
Base;
type
TFoo =class(TBaseClass)
MyBoo : TBaseClass;
end;
implementation
uses
Boo;
procedure TFoo.Foo;
begin
TBoo(MyBoo).DoBoo;
end;
Code:
unit Boo;
...
uses
Base;
type
TBoo =class(TBaseClass)
MyFoo : TBaseClass;
end;
implementation
uses
Foo;
procedure TBoo.Boo;
begin
TFoo(MyFoo).DoFoo;
end;
TBaseClass kann auch TObject sein, habs nur etwas allgemeiner gemacht. Es ist zwar wirklich etwas umständlich, aber ich glaube, dass beim Wechsel auf C++ auch keinen Vorteil bringen dürfte (in dieser hinsicht): wenn die eine Unit A abhängig von einer anderen Unit B ist, muss die andere Unit B erstmal kompiliert werden. Wenn aber die andere Unit B wiederum von A abhängt, muss ja zuerst A kompiliert werden. Wenn A kompiliert werden muss und A abhängig von B ist, muss zuerst B kompiliert werden .... usw ....
hi, ich glaube bei c++ gibt es namespaces und ich glaube da ist es so, dass alle Objekte im gleichen namespace auch aufeinander zugreifen können, egal ob sie sich in einer anderen unit befinden.
1. Bin ich mir da nicht sicher
2. Kann auch sein, dass es das erst bei C# gibt.
Die alternative mit staendigen typecasts hatte ich auch schon, aber das ist wirklich etwas aufwendig. wobei sich drueber nachdenken liesse, wenn man einfach den aufruf solche methoden möglichst minimiert. nur wenn man exzessiv von unit a auf unit b zugreift und umgekehrt, sind typecasts keine wirkliche alternative
Registriert: Di Okt 03, 2006 14:07 Beiträge: 1277 Wohnort: Wien
In Delphi ist eine Unit ein Namespace.
Ich habe mein OpenGl-Hauptprogramm so aufgebaut:
Unterste Basis, auf die alle zugreifen können: Unit BasicDefs (eigene Variablendefinitionen etc.)
Eine Stufe höher sind die Routinen mit der 3D-Mathematik angesiedelt: Unit Math3D
Ich habe zwar keine Physikroutinen, aber die wären jetzt auf dem Level über den Math3D-Routinen genau richtig
Wieder eine Stufe höher sitzen die 3D-Basisobjekte TSystem3D,TCamera,TMeshPoint,TMeshFace und noch einige andere: Unit Basis3D
Dann kommt endlich das 3DObjekt, das daher alle vorangegangenen Strukturen kennt: Unit Object3D
Und schlussendlich als Krönung der OpenGLSpace, der fürs Rendern sorgt: Unit OGLSpace3D (wenn der nicht auf alle anderen Zugriff hat, dann gehts nicht. Er hält z.B. die 3D-Objekte in einer Liste. Das aktuelle Objekt hält er immer für einen Lesezugriff bereit; weiters hat er eine Camera, die er seinen 3D-Objekten manchmal "borgt", das geht nur, weil sowohl das 3DObjekt als auch der OGLSpace auf die Unit Basis3D zugreifen können).
Und ganz zuletzt obendrauf - sozusagen als Wichtigtuer - kommt das TForm mit der GUI und kann zwar auf alle zugreifen, zieht es aber meist vor, nur dem OGLSpace und dem Object3D (jeweils immer dem aktuellen) Anweisungen zu geben. Zuweilen braucht es natürlich auch die BasicDefs.
Das ist nur eine der vielen Möglichkeiten.
Das einzige, was hilft: Papier und Bleistift und eine ruhige Stunde zum Diagramm zeichnen, denn das brauchst Du. Und darin musst Du die Abhängigkeiten der Objekte einzeichnen. Und jetzt zitiere ich Lossy Ex, der mal gesagt hat: "Wenn das UML-Diagramm aussieht wie Picassos Braut, weiß man, dass man etwas falsch gemacht hat".
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Shaddow: Ja das Delphi da solche Beschränkungen hat ist manchmal wirklich etwas ungütig. Ich hatte auch schon verschiedene Fälle an denen ich in solch einer Situation war. Das Einziehen einer abstrakten Basisklasse wirkt dem Problem zwar entgegen aber dann muss man wissen was man tut. Die einfach so einzubauen oder an der falschen Stelle einzubauen bringt keine Pluspunkte. Denn dann kann man sich schnell den Code zerwurschteln oder aber es läuft auf so etwas wie stumpfes casten hinaus. Aber nur das sollte sicher kein Grund sein um die Sprache wechseln zu wollen.
Wir hatten darüber ja schon mal im icq gesprochen. Und ich glaube in deinem Fall musst du etwas detailierter werden, denn wenn du nur von verschiedenen Objekten sprichst, dann könnte man schnell ein falschen Bild von deinem Problem bekommen. Evtl wäre es ganz praktisch, wenn du dir mal so etwas wie ArgoUML schnappst und, auch schon alleine mal für dich selber, die Objekte so aufbringst wie sie gerade sind. Da erkennt man meistens schon wo sich Schwachstellen befinden. Für uns wäre das zum Verständniss auch nicht doof. Auch evtl mit originaldatei von ArguUML dann kann der ein oder andere evtl sogar gleich Verschläge einbauen. Und ich meine nur die Klassen und wichtige Beziehungen zwischen ihnen also bloß nicht zu detailiert!
Aber wenn ich das noch richtig im Hinterkopf habe ist dein Problem, dass dein Hauptmanager jeden Untermanager kennt aber die Untermanager wieder immer auf Dinge des Hauptmanager zugreifen wollen/müssen. Was mitunter ja auch durchaus berechtigt ist. Aber bei OOP sollte es so laufen, dass die Objekte nicht zu viel von ihrer Umgebung wissen sollten. Ungeachtet dessen ob Pascal so etwas kann oder nicht. Du solltest dir auch genau überlegen was für Funktionalitäten die einzelnen Untermanager haben und ob sie diese wirklich benötigen! Bzw was eigentlich hinter der Funktionalität stecht. Manchmal muss man sich, zu Gunsten von klar getrennten Schnitstellen, selber ein paar Schranken auferlegen. So ein bisschen wie in einem großen Unternehmen. Der kleine Azubi wird nicht zum Vorstand gehen und dem sagen wie er seine Sachen zu machen hat. Das macht der Azubi sicher keine 32 Mal.
PS: Vorsicht Phrasendrescher. "Man geht so lange davon aus, dass etwas perfekt ist bis man etwas noch Perfekteres gefunden hat."
Zitat:
Und jetzt zitiere ich Lossy Ex, der mal gesagt hat: "Wenn das UML-Diagramm aussieht wie Picassos Braut, weiß man, dass man etwas falsch gemacht hat".
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
In Java macht man dies üblicherweise mit Interfaces. Die Interfaces liegen dann alle in einem Package auf das alle anderen Packages zugriff haben. Diese anderen Packages implementieren ein oder zwei Interfaces und nutzen andere Interfaces.
Beim erstellen der Anwendung gibt es einen zentralen Manager welcher die Factorys der einzelnen Packages nutzt um die Klassen zu erzeugen, und hangt die dann an die richtigen stellen ein.
Hoffe das war nicht zu wirr.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
Ja Java mach ich derzeit in der Uni, deswegen war das auch in der engeren Auswahl der alternativsprachen.
Mein Hauptproblem zur Zeit ist, dass mein Turbodelphi mein Projekt partout nicht als UML darstellen will. Ich meine, dass es evtl scheisse aussieht, wenn ich es darstellen lasse, das sei mal dahin gestellt, aber die umwandlung des Codes in UML sollte dennoch doch prinzipiell machbar sein. Hab da schonmal ne Weile mit meinem Bruder gesucht, aber wir haben nicht rausgefunden, warums nich ging.
Ich werd vllt einfach mal Eclipse nehmen und den Programmaufbau da im UMLEditor nachmodellieren oder ich mals halt echt auf ein Blatt papier ^^
ich halt euch da auf dem laufenden
bis dahin will ich hier aber keine diskussion / keinen kreativen Austausch unterbinden, also falls noch wer ideen hat, immer her damit
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
In c/c++ tritt das Problem in anderer Form auf.
Da man die Texte direkt kopiert(includiert), geht zirkulares includieren auch ned aber man kann durch einfaches typen oder klassen dummyhaft nochmal definieren(in der anderen includefile) dem Problem herr werden.
A kann B includieren aber B kann dann nicht mehr A includieren, da A schon im Code drin ist aber der Compiler zu blöd ist die definitionen zu nutzen, muss man die minimalistisch nochmal definieren(typedef int ID; class blupp;).
Object Pascal hat auch das Zirkuläre Problem, kann aber wie in c/c++ gelöst werden also in Unit A uses B; und in unit B type ACls=class; und unter implementation dann uses A;
Das ist natürlich ne total blöde sache aber noch blöder ist es, wenn man auf sowas überhaupt stösst.
Denn dann hat man eigentlich nicht gut designed.
Ideal ist es eigentlich mit Interfaces/abstrakten Klassen zu beginnen und dann darauf die eigenen Klassen zu bauen.
Unittechnisch wäre dann eine Unit mit der Abstrakten klasse/Interface zu schreiben und eine Unit mit der Implementation( Blupp=class(B);).
Es kann sich lohnen, unter Delphi/FP mal Interfaces anzugucken(und modularisierung), die sind schöner als abstrakte Klassen(man kann mehere Interfaces implementieren).
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Do Dez 05, 2002 10:35 Beiträge: 4234 Wohnort: Dortmund
Ob man nun Interfaces oder abstrakte Klassen benutzt ist sicher nicht so ausschlaggebend. Klar Interfaces haben in jedem Fall verschiedene Vorteile. Aber dahinter steckt durchaus auch mehr arbeit. Denn wenn man so etwas macht, dann darf man auch nicht nur 1 oder 2 Klassen umstellen weil die Probleme machen. Sondern dann muss man vermutlich weitläufiger umstellen.
Aber ich denke das wird an dem eigentlichen Problem derzeit nichts verändern. Und zwar, dass die Codes zu stark voneinander abhängen und nicht so klar voneinander getrennt sind wie sie sein sollten. (Sofern ich den Code noch richtig in Erinnerung habe). Nichts für ungut Shaddow. Aber ich denke das ist das Erste wo man ansetzen sollte. Du solltest dafür sorgen, dass Manage A und Manager B sich nicht gegenseitig kennen müssen. Sondern die sollten sich nur um ihr eigenes Zeug kümmern. Und dann sollte sich das Problem mit den Zirkulären Referenzen auch schon erledigt haben.
Wenn deine Klassen so eng gekoppelt sind, kannst du sie ja auch in eine unit stecken. Wenn sie so unabhägig sind, dass eine unit unpassend erscheint, und sie sich trotzdem so gut kennen müssen, ist an deinem Design etwas seltsam.
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Schau dir dazu einfach mal an, welche Funktionen von ManagerA den Manager B brauchen und umgekehrt. Eventuell erkennst du dann, welche Teile der Manager neu organisiert werden müssen.
Im Tutorial zur Softwareentwicklung stand die Regel: "Eine Klasse muss genau einen Zweck erfüllen, und nur diesen Zweck." Oder so.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
In c/c++ tritt das Problem in anderer Form auf. Da man die Texte direkt kopiert(includiert), geht zirkulares includieren auch ned aber man kann durch einfaches typen oder klassen dummyhaft nochmal definieren(in der anderen includefile) dem Problem herr werden.
Öhm das ist aber eher NAJA erklärt .... .
Zuerst mal hab ich in C++ h- und cpp-Dateien. Inkludieren tue ich nur die h-Dateien, in den cpp steht die Implementation.
Ohne jetzt das Problem zu kennen:
Shaddow will anscheinend eine bidirektionale Beziehung. Sowas ist normal und hat grundsätzlich nichts mit schlechtem Design zu tun, sonst würde es sie wohl kaum in der UML geben.
Und in C++ macht man das folgender Maßen: man erstellt keinen Dummy sonder eine Forward-Deklartion um so dem Compiler einen Typ bekannt zu machen, welcher später definiert wird. Nun kann ich nach dieser Deklaration Variablen von diesem Typ in Zeiger und Referenz-Form anlegen. Das macht man in der h-Datei.
In der cpp-Datei inkludiere ich nun normal die Headerdatei und kann den Zeiger benutzen. Fertsch!
_________________ __________
"C++ is the best language for garbage collection principally because it creates less garbage." Bjarne Stroustrup
UNd genau das geht in Delphi nich.
Ich kann zwar innerhalb einer Datei etwas folgendermaßen deklarieren:
Code:
type
foo =class;
boo =class
private
test: foo;
end;
foo =class
private
...
end;
aber ich kann nicht foo deklarieren und die ausformulierte deklaration in eine andere datei packen
dann sagt der böse compiler naemlich etwas als
Cannot convert foo to foo
Mitglieder in diesem Forum: 0 Mitglieder und 10 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.