![]() |
Interface-Unterstützung
Ich arbeite viel mit Interfaces. Mich nervt, dass die IDE (m.E.) das kaum unterstützt.
So muss man redundant und wiederholt ständig das gleiche schreiben und man findet die Methoden nicht vernünftig in der Unit, da diese beliebig einsortiert werden. Also folgendes Beispiel mit anschließender Erklärung... UnitMyIntf.pas
Delphi-Quellcode:
UnitMyClass.pas
IMyIntf1 = Interfaces
property MyProp1: string read write; procedure Do1(P: string); end; IMyIntf2 = Interfaces property MyProp2: string read; procedure Do2(P: string); end;
Delphi-Quellcode:
TMyClass = class(TInterfacedObject, IMyIntf1, IMyIntf2)
protected { IMyIntf1 } fMyProp1: string; function _get_MyProp1: string; procedure _set_MyProp1(Value: string); protected { IMyIntf2 } fMyProp2: string; function _get_MyProp2: string; public constructor Create; virtual; destructor Destroy; override; public { IMyIntf1 } property MyProp1: string read _get_MyProp1 write _set_MyProp1; procedure Do1(P: string); public { IMyIntf2 } property MyProp2: string read _get_MyProp2; procedure Do2(P: string); end; ... { TMyClass } constructor TMyClass.Create; begin end; destructor TMyClass.Destroy; begin inherited; end; { TMyClass / IMyIntf1 } function TMyClass._get_MyProp1: string; begin Result := fMyProp1; end; procedure TMyClass._set_MyProp1(Value: string); begin fMyProp1 := Value; end; procedure TMyClass.Do1(P: string); begin end; { TMyClass / IMyIntf2 } function TMyClass._get_MyProp2: string; begin Result := fMyProp2; end; procedure TMyClass.Do2(P: string); begin end; Ich hätte gern folgende Aspekte: 1) Im Interface möchte ich die Getter und Setter nicht komplett definieren müssen. Dies würde die Sache vereinfachen und die Übersichtlichkeit erhöhen. Der Compiler könnte ja eigentlich - wenn dort "read" und/oder "write" steht, einfach "gedanklich" Standardgetter und -setter voraussetzen. 2) In den Klassen hätte ich gern die Abschnitte, die Interfaces zugewiesen sind, entsprechend geordnet und beschrieben. Im Implementationsteil sollte das entsprechend gehandhabt werden. Zunächst sollten Constructor und Destructor kommen, dann die normalen Methoden und dann die Interface-Methoden mit entsprechender Gliederung und Sortierung. 3) Sofern in den Interfaces Getter und Setter definiert sind und diese in den Klassen erstmalig erzeugt werden, sollten auch gleich private Felder "fMyProp: MyType;" eingerichtet und in den Gettern und Settern benutzt werden. Da ist ja bestimmt zu 95% der Normalfall und mich nervt, dass man das jedesmal neu von Hand machen muss (zumal Getter und Setter ja wild in der Unit verteilt sind). 4) Refactoring von Interfaces zu Klassen sollte möglich sein. Wenn ich z.B.
Delphi-Quellcode:
im Interface ändere in
procedure Do1(P: string);
Delphi-Quellcode:
sollte das auch in die Klassendeklarationen übertragbar sein, die dieses Interface verwenden (im Interface- und Implementationsteil).
procedure Do1(const aValue: string);
Die Methoden können dann zwar ggf. nicht kompiliert werden, aber ich brauch nur noch die logischen Dinge anpassen und bin sicher, dass die Änderungen im Interface (z.B. Propertynamen) auch in den Klassen angepasst wird. Ich wüsste mal gern Eure Meinung dazu. Haltet Ihr die Überlegungen für sinnvoll/wichtig? Gibt es dazu vielleicht schon Möglichkeiten, die ich bisher übersehen habe (Ich würde mich in den A... beißen und davon ein Foto hochladen :stupid:)). Hättet Ihr (die Ihr mit Interfaces arbeitet) Interesse an so einer Lösung? Auf Emba setze ich diesbezüglich leider nicht. Wenn diese Dinge demnächst richtig gut unterstützt würden, dann würde ich sogar nochmal ein Update in Erwägung ziehen. Aber da gibt es ja sicher andere Prioritäten. Aber ich habe eine Idee zu einem Tool, das den Interface-Quelltext analysiert, die fehlenden Getter und Setter (wie oben im Beispiel) ergänzt und dann die verwendenden Klassen zerlegt, ordnet, ergänzt, anhand der Interface-Vorgaben korrigiert und entsprechend dem obigen Beispiel neu zusammenbaut. So könnten viele, immer gleiche Arbeitsschritte deutlich reduziert werden. (Angefangen habe ich damit schon.) Meiner Meinung nach kann das extrem viel Arbeit ersparen und gleichzeitig die Übersichtlichkeit deutlich erhöhen. Was meint Ihr dazu? Habt Ihr Interesse an solch einem Tool? Ist das überflüssig? Gruß Stahli PS: Die externe Umsetzung halte ich für mich für machbar (so dass die Units einfach von der Exe geladen und aufbereitet werden). Damit das richtig komfortabel wäre müsste man das durch einen IDE-Experten realisieren, der auch scannen kann, welche Units unter "uses" stehen und diese nach der Interface-Deklaration durchsuchen. Dazu habe ich noch keine Vorstellung, wie das geht... |
AW: Interface-Unterstützung
Also ich arbeite in letzter Zeit fast ausschließlich mit Interfaces. Alle Punkte die du angesprochen hast, habe ich jetzt nicht direkt verstanden, bin aber auch nur relativ schnell über deinen Post hinweg geflogen.
Zu der Einsortierung in den Quelltext an einer bestimmten Position kann ich sagen, dass ich da früher darauf geachtet habe. Aber heute verwende ich ausschließlich die Methodenliste des CnPacks. Einfach STRG + D, den Anfang des Methodennamens eingeben und Enter. Da interessiert mich im Prinzip nicht mehr wo die Methode wirklich steht. Ich nutze eben sehr viele Tastenkombinationen die mir das Leben in der IDE deutlich vereinfachen und somit auch gewisse Dinge nicht benötigt werden. Das automatische Anlegen des privaten Feldes und das Ausfüllen der Getter/Setter wäre sicherlich eine schöne Sache. Aber oft mache ich im Getter und Setter noch diverse Abfragen um unnötige Zuweisungen zu vermeiden. Von daher wäre das für mich nur bedingt hilfreich. Die Properties in der Klasse brauchst du gar nicht mehr anzulegen. Es reicht, einfach nur die Getter und Setter zu erzeugen. Die Properties werden durch das Interface zur Verfügung gestellt. Setzt natürlich voraus, dass du ausschließlich mit Interfaces im Zusammenhang mit der Klasse arbeitest. Das mal dazu wie ich damit umgehe. Manchmal würde ich mir allerdings wünschen, dass ein STRG + Klick bzw. ein STRG + G zum Springen zur Methode nicht zur Interface-Deklaration sondern zur Klasse die an dieser Stelle instanziiert wird zu springen. Visual Studio macht so etwas. Das schaut sich an, welche Klasse der Interface Variablen zugewiesen wurde und springt dann direkt zur Klasse und nicht zum Interface. |
AW: Interface-Unterstützung
Ich kann dir nur Modelmaker Code Explorer empfehlen. Damit kann man viel zu den Punkten umsetzen, die du angesprochen hast.
Die Sortierung der Methoden geht damit zwar, aber da achte ich gar nicht drauf. Die ist mir ziemlich egal. Zu Punkt 3... Dafür gibt es ja die Klassenvervollständigung. Die legt das automatisch an. Und die Deklarationen aus einen Interface kann man auch automatisch übernehmen, auch ohne Addon. |
AW: Interface-Unterstützung
Oh, man beachte die zwei letzten Beiträge:
![]() Ich hatte MM und MMX völlig verdrängt. Werde mir jetzt mal die Demos laden. Scheint ja ganz gut auf meine Wünsche zu passen. Gute, aktuelle Video-Tutorials (mit sprachlichen Erläuterungen) wären sicher trotzdem sinnvoll (und würden den Verkauf unterstützen) ... @jaenicke Trotzdem mal die Frage: Delphi legt Klassenvervollständigung und Methodenübernahme von Interfaces legt ja aber keine privaten Felder an - oder gibt es das doch irgendwie? |
AW: Interface-Unterstützung
Liste der Anhänge anzeigen (Anzahl: 1)
Interfaces haben eigentlich keine Property, da gibt es nur Methoden, auch wenn Delphi hier die Property mit anbietet.
Aber daher fehlt dafür dann auch die automatische Codeverfolständigung. Obwohl man sich natürlich fragen könnte, warum für Interfaces nicht einfach die Vervolständigung für Klassen verwendet wird/kopiert wurde. (abzüglich der Methodenimplementation und der Felder) Private: Private Felder werden angelegt, für Property. Außer in der Starter, da fehlt eh Vieles.
Delphi-Quellcode:
Interface-Methoden legt nicht ver Klassenvervollständigung (Strg+Shift+C) an, sondern das übernimmt "teilweise" die Code-Vervolständigung (Strg+Leertaste),
type
TMyClass = class property Name1: string read GetName1 write SetName1; // Template prop property Name2: string read FName2 write FName2; // Template propf property Name3: string read GetName3 write SetName3; // Template propgs property Name4: string read GetName4; // Template propro property Name5: string read FName5; // Template proprof property Name6: string write FName6; end; TMyClass = class private FName2: string; FName6: string; FName5: string; function GetName1: string; function GetName3: string; function GetName4: string; procedure SetName1(const Value: string); procedure SetName3(const Value: string); published property Name1: string read GetName1 write SetName1; // Template prop property Name2: string read FName2 write FName2; // Template propf property Name3: string read GetName3 write SetName3; // Template propgs property Name4: string read GetName4; // Template propro property Name5: string read FName5; // Template proprof property Name6: string write FName6; // !!! hier legt Delphi mir manchmal auch noch den Getter an, egal ob ich will oder nicht !!! end; aber du mußt da selber auswählen was du willst. Wenigstens mit Multiselect, auch wenn es schwachsinn ist, denn von Interfaces müssen sowieso immer alle Methoden implementiert werden, dann könnte Delphi da auch von Selber gleich alles anlegen. Die Codevervollständigung legt "eigentlich" alles alphaetisch an. Und ja, mir wäre die Reihenfolge der Implementation auch lieber. Und obwohl das alphabetisch rein kommt, dreht es schnell durch, wenn einmal in der Implementation die Reihenfolge der vorher bestehenden Methoden nicht stimmt, oder wenn man überladene Methoden hat, dann wird schnell mal was zu früh eingefügt. |
AW: Interface-Unterstützung
@himitsu
Das Interface erzwingt ja Getter und/oder Setter, also akzeptiert kein privates Feld. Daher wäre wünschenswert (gewesen), dass in Deinen Beispielen von der Codevervollständigung ein privates Feld fName1 angelegt und in Getter und Setter gelesen bzw. zugewiesen wird. @all Der ModelMaker Code Explorer scheint wirklich schon sehr gut das zu sein, was ich mir wünschen würde. Da stellt sich die Frage: Lohnt es sich vielleicht direkt, gleich Nägel mit Köpfen zu machen und auch den ModelMaker zu nutzen? Wenn der MM genutzt wird, bringt dann der MMX noch etwas? Ich kann die Zusammenhänge immer noch nicht so genau einordnen. :-( |
AW: Interface-Unterstützung
Zitat:
|
AW: Interface-Unterstützung
Nein, wenn read/write nicht auf ein Feld, sondern ausschließlich auf Setter/Getter verweisen, dann wird niemald automatisch ein Feld angelegt,
denn was wäre, wenn der Entwickler kein Feld braucht und dann immer wieder Eines angelegt würde? Das wäre wie ein Kühlschrank, der immer gleich automatisch etwas bestellt, wenn es alle wird. Dabei hab ich das nur aufgegessen, weil ich keine Lust hatte es wegzuwerfen, es aber nie wieder haben will, weil es nicht so gut schmeckt, aber immer wenn es endlich weg ist, wird sofort Nachschub geliefert. :wall: |
AW: Interface-Unterstützung
Ein automatisches Feld sollte die Codevervollständigung m.E. anlegen und im Getter/Setter verwenden, wenn Getter und Setter erzeugt werden.
Braucht der Entwickler das dann nicht, ist das schnell gelöscht und im Getter/Setter entfernt. Wird später nochmal die Codevervollständigung benutzt, darf das Feld natürlich nicht nochmal ein privates Feld erzeugen (und der Kühlschrank würde Dir kein Bier nachbestellen ;-) ). In das Getter/Setter-Dilemma wird man ja nur gezwungen, weil die Interface-Regeln das so erfordern. Ein privates Feld kann man dort ja nicht angeben bzw. dies offen lassen. |
AW: Interface-Unterstützung
Zitat:
Vielleicht sollte ich wirklich mal ein paar Filmchen über MMX machen. Manch einer weiß offenbar gar nicht was er verpasst :-D |
AW: Interface-Unterstützung
Ich abonnier deinen Kanaaal!!!
EDIT: Huch, ich hatte das alles groß geschrieben aber die DP hat das offenbar zensiert... |
AW: Interface-Unterstützung
Siehe Marcos Kommentare.
![]() ![]() |
AW: Interface-Unterstützung
Zitat:
Aber schöner ist natürlich Code Explorer zu benutzen. Zitat:
|
AW: Interface-Unterstützung
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
"Name", Tab, "Typname", Enter fertig |
AW: Interface-Unterstützung
@himitsu
Da hast Du aber kein privates Feld erzeugt. Getter und Setter liegen irgendwo in der Unit. Du musst also das private Feld definieren, die Methoden suchen und dort immer den selben Quatsch reinschreiben. Das halte ich für völlig unnötig und hier sollte die IDE einem einfach etwas Arbeit abnehmen. @jaenicke Was gefällt Dir an der Idee aus Stevies Link nicht? Es würde doch einfach mehr Freiheiten ermöglichen. Ich fände das schon gut. So weit wollte ich mit meiner Idee aber eigentlich gar nicht gehen, bzw. hatte ich ein anderes Ziel. Ich will einfach weniger schreiben müssen und weniger Redundanzen haben. So könnte der Compiler einfach normale Getter und Setter erzeugen bzw. voraussetzen, wenn im Interface
Delphi-Quellcode:
steht.
property X: Interger read write;
Entweder konnten
Delphi-Quellcode:
und
function _get_X: Integer;
Delphi-Quellcode:
physisch im Code eingebaut werden oder der Compiler tut einfach so, als würden die dort stehen...
procedure _set_X(const aValue: Integer);
Der Programmierer könnte auch unverändert Getter und Setter vollständig selbst deklarieren, so dass dann alles beim alten bliebe. Da müsste also an den Interfaces selbst (im Hintergrund) gar nichts geändert werden. Standards müsste man halt nicht ständig wiederholt komplett ausschreiben. Gleiches dann in den Klassen und das Programmiererleben wäre sehr viel einfacher... |
AW: Interface-Unterstützung
@MMX-Cracks
MMX bietet aber nicht die Möglichkeit, die Klasse durch fehlende Interface-Members zu ergänzen - oder? Ein Property oder Methode zu erzeugen ist schon ok, aber noch nicht ganz mein gewünschtes Feature. |
AW: Interface-Unterstützung
Die vordeffinierten Templates sind nicht immer das Beste, aber man kann damit sehr viel mehr machen, auch inkl. dem Anlegen von Feldern und der Zuweisung im Setter, samt Prüfung auf Änderung uvm.
PS: Strg+Shift+C macht aus dem
Delphi-Quellcode:
das hier
type
TMyClass = class published property Name: string read FName write SetName; end;
Delphi-Quellcode:
oder
type
TMyClass = class private FName: string; procedure SetName(const Value: string); published property Name: string read FName write SetName; end; { TMyClass } procedure TMyClass.SetName(const Value: string); begin FName := Value; end;
Delphi-Quellcode:
// property Name: string read GetName write FName;
type TMyClass = class private FName: string; function GetName: string; published property Name: string read GetName write FName; end; { TMyClass } function TMyClass.GetName: string; begin Result := FName; end;
Delphi-Quellcode:
// property Name: string read GetName write SetName;
type TMyClass = class private function GetName: string; procedure SetName(const Value: string); published property Name: string read GetName write SetName; end; { TMyClass } function TMyClass.GetName: string; begin end; procedure TMyClass.SetName(const Value: string); begin end; |
AW: Interface-Unterstützung
Zitat:
|
AW: Interface-Unterstützung
Ah, ok, danke!
Das Erzeugen von Propertys im Interface und Übernahme in der Klasse gefällt mir aber auch noch nicht richtig. Ich versuche mal mein Tool weiter. Mal sehen, wie es wird... |
AW: Interface-Unterstützung
Zitat:
Ich finde was die Idee angeht einfach, dass man schon explizit schreiben müssen sollte was man meint. Solch eine Automatik führt nur zu Missverständnissen und Bugs. Ich merke immer wieder mal, dass ich in anderen Sprachen mehrfach hinschauen muss was genau eigentlich gemeint ist, während es in Delphi einfach so klar ist. Ich finde, dass man diese Einfachheit durchaus beibehalten sollte. Zitat:
Und insbesondere bei Interfaces und Klassen gibt es ja nicht mehr zu schreiben, sondern das wäre im Gegenteil mit der angedachten neuen Lösung der Fall. Aktuell schreibe ich die Deklaration einmal und kopiere diese dann herüber in Interface oder Klasse, je nachdem. Der Schreibaufwand wäre also höher, wenn man in Klasse und Interface unterschiedlich aussehende Deklarationen benutzen würde. Denn die müsste man dann ja wirklich manuell doppelt schreiben... |
AW: Interface-Unterstützung
Hallo Stahli,
geht es dir bei dem Tool nur um das Modelling, oder auch das Debugging ? Mich stört nämlich ziemlich das man nicht immer sauber in Interfaces reindebuggen kann, also weil es wohl die aktuele Implementation nicht finden kann. Da wäre mir ein Tool oder eine Lösung auch willkommen. Rollo |
AW: Interface-Unterstützung
Das Problem bei Automatisch/Implizit:
Zitat:
Und wie sieht die implizite Implementation aus? Gut, den Namen "SetX" hätten wir schonmal, aber was ist mit der Signatur?
Delphi-Quellcode:
procedure SetX(aValue: Integer);
procedure SetX(const aValue: Integer); procedure SetX(aValue: Integer); stdcall; procedure SetX(const aValue: Integer); stdcall; ... |
AW: Interface-Unterstützung
@Rollo62
Das Debugging würde meine Möglichkeiten übersteigen. Das wird wohl nur Emba können. Mein Tool soll lediglich verkürzte Schreibweisen korrigieren. Aus
Delphi-Quellcode:
soll es die notwendigen Getter und Setter definieren und aus
// MyIntf.pas
... IMyintf = interface prop X:Integer; prop Y:Integer ro; // read only prop Z:Integer wo; // write only end;
Delphi-Quellcode:
soll es die erforderlichen Propertys und Methoden entsprechend der eingebunden Interfaces erstellen und alles (incl. des Implemantationsteils) vernünftig strukturieren.
// MyClass.pas
... TMyintf = class(TBaseClass, IMyIntf) end; @himitsu Statt bzw. zusätzlich zu "ro" und wo" könnte man ja noch "c" für "const" und "s" für "stdcall" vorsehen. Wenn das Prinzip funktioniert, dann ist man da sicher flexibel. Ich bastle gerade einen Parser(Light). Dann werden die gefunden Teile strukturiert gesammelt und später neu zusammengebaut. Je nach Aufwand kann das sicher auch mit komplexeren Anforderungen umgehen. Als externes Tool kann ich das sicher realisieren. Schwieriger wird wohl, das in die IDE einzubinden. Es wäre so etwas wie ein Quelltextformatierer. Er würde die Unit zerlegen und neu zusammenbauen. Welche Interfaces eine Klasse verwendet, weiß ich schon. Das Tool müsste aber jetzt noch die Units ermitteln können, welche diese Interfaces deklarieren. Dazu müsste wohl die Funktion genutzt werden, die durch Ctrl+Click in die Unit springt. Damit kenne ich mich nicht aus. Dann müssten evt. die Interface-Units überarbeitet werden (s.o.), die Interface-Deklaration übernommen werden und dann kann die Klasse optimiert werden. Vorerst reduziere ich das ganze halt mal auf ein externes Tool. |
AW: Interface-Unterstützung
Bevor du dich an einem Parser versuchst, solltest du lieber auf DelphiAST (
![]() Wenn es dir um das Schreiben von Code geht kannst du auch Visual Studio Code + OmniPascal nehmen. Die automatische Implementierung von Interfaces, sowie Snippets und multi Cursor Unterstützung helfen bei der Schreibarbeit: ![]() |
AW: Interface-Unterstützung
Danke für den Tipp.
Ich habe allerdings einen eigenen Parser aus einem anderen Projekt und kann den hier verwenden. Ein Teil der Interface-Aufbereitung geht schon:
Delphi-Quellcode:
wird zu:
...
type IsoNameObj = interface; iMyBaseIntf = Interface prop BaseProp:integer; end; IMyIntf = interface(IMyBaseIntf) procedure DoA; prop A:string c s; prop B:string ro; prop C:string wo c s; prop D:string sg; prop E:string ss c; function GetA:Integer; end; IMyIntf2 = interface(IMyBaseIntf) prop X:string c s; prop Y:string ro; function GetX:Integer; end; IMyIntf3 = interface procedure DoZ; prop Z:string c; function GetZ:Integer; end; TsoNotifyEvent = procedure(const Sender: IInterface) of object; IsoSystem = interface; IsoProject = interface; IsoPropertyDef = interface; IsoMethodeDef = interface; IsoClass = interface ...
Delphi-Quellcode:
...
type IsoNameObj = interface; iMyBaseIntf = Interface function _get_BaseProp: integer; procedure _set_BaseProp(aValue: integer); property BaseProp: integer read _get_BaseProp write _set_BaseProp; end; IMyIntf = interface(IMyBaseIntf) procedure DoA; function _get_A: string; stdcall; procedure _set_A(const aValue: string); stdcall; property A: string read _get_A write _set_A; function _get_B: string; property B: string read _get_B; procedure _set_C(const aValue: string); stdcall; property C: string write _set_C; function _get_D: string; stdcall; procedure _set_D(aValue: string); property D: string read _get_D write _set_D; function _get_E: string; procedure _set_E(const aValue: string); stdcall; property E: string read _get_E write _set_E; function GetA:Integer; end; IMyIntf2 = interface(IMyBaseIntf) function _get_X: string; stdcall; procedure _set_X(const aValue: string); stdcall; property X: string read _get_X write _set_X; function _get_Y: string; property Y: string read _get_Y; function GetX:Integer; end; IMyIntf3 = interface procedure DoZ; function _get_Z: string; procedure _set_Z(const aValue: string); property Z: string read _get_Z write _set_Z; function GetZ:Integer; end; TsoNotifyEvent = procedure(const Sender: IInterface) of object; IsoSystem = interface; IsoProject = interface; IsoPropertyDef = interface; IsoMethodeDef = interface; IsoClass = interface; ... Der Parser erkennt Interfacedeklarationen und kann auch Forward-Deklarationen unterscheiden. Nur die richtigen Interfaces scant und optimiert er. Die generierten Zeilen habe ich hier mal eingerückt. Jetzt will ich noch regeln, dass Typänderungen von Propertys automatisch in vorhanden Gettern und Settern angepasst werden. Namensänderungen von Membern können dann nicht automatisch bearbeitet werden (das wäre höchstens möglich für Getter und Setter von Propertys, aber das wäre den Aufwand wohl nicht wert). Die Interface-Member werden als Objekte gespeichert und an die Klassen übergeben. Dort wird dann noch geprüft, ob die dortigen Member korrekt sind. Sonst wird die Klasse angepasst und ggf. Propertys und Methoden erzeugt. Anschließend wird die Unit sortiert. |
AW: Interface-Unterstützung
Alle Sonderfälle kann man sicher nicht abdecken, aber jetzt kann man ein altes Property einfach durch ein neues überschreiben...
alt:
Delphi-Quellcode:
iMyBaseIntf = Interface // mehrfache Methoden hier nur zum Test
function _get_BaseProp: string; procedure _set_BaseProp(aTestS: string); prop BaseProp:integer; // überschreibt evtl. vorhandene Property sowie Getter und Setter function _get_BaseProp: Boolean; procedure _set_BaseProp(aTestB: Boolean); property BaseProp: Boolean read _get_BaseProp write _set_BaseProp; end; neu:
Delphi-Quellcode:
iMyBaseIntf = Interface
function _get_BaseProp: integer; procedure _set_BaseProp(aValue: integer); property BaseProp: integer read _get_BaseProp write _set_BaseProp; end; Der nächste Schritt soll sein, dass man einfach
Delphi-Quellcode:
in
BaseProp: integer
Delphi-Quellcode:
ändern kann und dadurch Getter und Setter im Interface angepasst werden.
BaseProp: Boolean
Die Klassen werden dann unabhängig von der Ausgangssituation an die aktuelle Interface-Deklaration angepasst. |
AW: Interface-Unterstützung
Liste der Anhänge anzeigen (Anzahl: 2)
Anbei mal eine erste Testversion, die schon mal zwei Units (_unit1 und _unit2) ordnet und sortiert.
In den zwei Memos links sind die Originale und rechts die Ergebnisse dargestellt. Die beigefügten Units sind verstümmelte Units von mir. Unit1 enthält einige Klassen und Unit2 für später die zugehörigen Interfaces (diese könnten aber dann auch direkt in der Unit1 deklariert sein). Die Interfaces werden aktuell allerdings noch nicht berücksichtigt. Die Unit1 wird aktuell vernünftig umsortiert. Einige Dinge funktionieren noch nicht. Direkt fallen mir ein: - class Variablen, Funktionen etc - Embedded functions/procedures - strict private etc - overloads - Klassen- und Interfacedeklarationen im Implementationsteil Eine besondere Schwierigkeit beim Umsortieren sind Zeilenumbrüche und Kommentare mitten im Code. Es kann schon sein, dass Eure Code-Styles aktuell nicht korrekt unterstützt werden. Als nächstes will ich erst mal die oben schon erläuterte "prop"-Interpretation umsetzen. Für die Interfaces hatte ich das ja schon mal realisiert, muss das aber nochmal auf den neuen Parser anpassen. Dann können z.B. durch "prop MyProp:Integer rf wf;" ein Property mit privatem Feld und durch "prop MyProp:Integer;" ein Property mit Getter und Setter (+privatem Feld) erzeugt werden. Wenn der Property-Typ geändert wird, wird schlägt das automatisch auf die Getter, Setter und privaten Felder durch. Außerdem sollen neue Deklarationen von Methoden im Implementationsteil entsprechende neuen Methoden erzeugen. Änderungen von Parametern sollen in den Implementationsteil übernommen werden. Umbenennungen von Parametern könnte das Tool erkennen und im Methodenbody nachführen (das wäre das einzige wirkliche "Refactoring" durch das Tool). Zum Schluss sollen dann alle Deklarationen in Interfaces automatisch in den Klassen realisert/angepasst werden, die dieses Interface verwenden. Wenn z.B. eine Klasse nicht kompiliert und man das Tool startet, würden die notwendigen Deklarationen aus dem Interface in einer Standardform implementiert und können bei Bedarf noch überarbeitet werden. Im Grunde wäre das Tool eine neue Klassenvervollständigung + Interface-Berücksichtigung + Unit-Neuordnung. Die Frage ist, wie weit man das treiben will und soll. Man könnte auch einen kompletten Code-Formatierer daraus bauen (wobei mir eigentlich der eingebaute reicht). Vielleicht mit Ausrichtung der Punkte bei FluentInterfaces? Habt Ihr dazu Bedarf/Meinungen? In jedem Fall will ich das Tool später mal in die IDE integrieren. Dazu wäre es cool, wenn ich die Interfaces-Units aus der Klassendeklaration finden könnte (also die Unit, die durch Ctrl+Click auf IMyIntf geöffnet wird). Noch braucht es etwas Zeit bis dahin. Aber kennt sich damit jemand aus? |
AW: Interface-Unterstützung
Gibt´s da wirklich kein Interesse?
Wenn ich das so realisieren kann würde das m.E. eine große Schwäche der Delphi-IDE ausbessern und auch effektiver sein als der ModelMaker Code Explorer. |
AW: Interface-Unterstützung
Was mir bei deinen Überlegungen fehlt, ist folgender Sachverhalt:
Die Klassen, die das zu refaktorierende Interface implementieren, müssen nicht zwangsläufig im gerade aktuell geladenen Projekt zu finden sein. Ggf. sind die in einen anderen Projekt zu finden, welches auch dieses Interface verwendet. Oder sind in einer BPL oder DLL. Oder sind gar keine Delphi-Klassen, sondern in irgendeiner anderen Sprache in einen anderen Modul implementiert. Falls man wirklich mehr als drei Zeilen am Interface ändert, dann macht man Copy&Paste und tackert das in die Klasse rein und passt entsprechend an. Vielleicht ist mein Leidensdruck auch nicht so hoch, weil sich relativ schnell eine fertige Property- und Methodendefinition feststeht. |
AW: Interface-Unterstützung
Klassen in anderen Projekten werden natürlich nicht berücksichtigt.
Der Ablauf wäre folgender: 1) Interface überarbeiten z.B. "prop MyIntf: Integer" wodurch gleich auch Getter und Setter im Interface ergänzt werden. 2) Projekt kompilieren, und die Units, die nicht kompiliert werden können nacheinander durch das Tool optimieren lassen (z.B. Ctrl+Shift+O, wenn es als IDE-Experte implementiert ist). Dadurch werden in den Klassen die Property, Getter und Setter ergänzt. Darüber hinaus auch ein passendes privates Feld und Standardcode im Getter und Setter. 3) Die Methoden im Implementationsteil werden analog zur Reihenfolge in den Klassendeklarationen sortiert. Mein Leidensdruck ist dabei extrem hoch, da bei jedem neuen Property in den Interfaces dieser ganze Mist immer wieder genau gleich in allen benutzenden Klassen durchgeführt werden muss. |
AW: Interface-Unterstützung
Zitat:
Wenn ich bspw. ein Interface habe und drei verschiedene Klassen, die dieses Property implementieren müssen und das ggf. sogar auf gleicher Art und Weise, dann mach ich mir doch eine Basisklasse dazu und frühstücke das da ab. Oder wenn sich das nur in einer von drei Klassen verschieden verhalten sollte, dann mach ich das auch in die Basisklasse und und mache Setter und Getter virtual und überschreibe in den richtigen Klassen entsprechend. |
AW: Interface-Unterstützung
Ich arbeite quasi nur noch mit Interfaces und meine Klassen implementieren jeweils sehr unterschiedliche (je nach erforderten Funktionalitäten).
Eine Interfaceänderung wirkt sich somit auf viele unterschiedliche Klassen aus. Der maßgebliche Grund für meinen Leidensdruck ist allerdings, dass ich nicht verstehe, dass man als Hightec-Softwareschmiede über die Jahre so etwas nicht mal vernünftig unterstützt. Wenn ich eine Kundenverwaltung schreibe, achte ich doch auch darauf, dass der Anwender die Kundendaten nicht 3 mal eingeben muss und das Programm Fehlermeldungen wirft, wenn die 3 Datenbestände nicht zusammenpassen. Ok, die Pascal-Struktur neu zu entwickeln wäre überzogen und ist nicht notwendig, aber zumindest sollte die IDE einem die Redundanzen etwas abnehmen und die Units vernünftig sortieren. Mich regt das wirklich auf und hier könnte doch eigentlich relativ leicht Abhilfe geschaffen werden. Mir fällt das natürlich schwerer als einer Hightec-Softwareschmiede, aber ich versuche das mal... |
AW: Interface-Unterstützung
Irgendwie habe ich es noch nicht verstanden,
was fehlt Dir im Modelmaker Code Explorer? Ich habe bis jetzt Deinen Flow bei Änderungen nicht verstanden. Wahrscheinlich bin ich zu doof..... |
AW: Interface-Unterstützung
Zitat:
|
AW: Interface-Unterstützung
Zitat:
|
AW: Interface-Unterstützung
Den MMX hatte ich getestet. Der war mir zu groß und umständlich.
Vor allem fand ich die Implementation von Interfaces nicht hilfreich. Kann natürlich mein Fehler gewesen sein. Kann mal jemand ein kurzes Video mit folgendem Inhalt erstellen? --> Unit mit 2 Interfaces - Intf1 mit zwei Propertys MyInteger und MyBoolean erzeugen (read+write) - Intf2 mit zwei Propertys MyString und MyByte erzeugen (read+write) --> Unit mit 3 Klassen - Class1(TInterfacedObject, Intf1) - Class2(TInterfacedObject, Intf2) - Class3(TInterfacedObject, Intf1, Intf2) Für die Getter und Setter muss natürlich ein privates Feld in den Klassen erstellt und verwendet werden. Es wäre mal interessant, wie weit MMX einen da unterstützt. Nach meiner bisherigen Einschätzung wird das trotzdem noch eine Schreib- oder Kopierorgie. Entweder überzeugt mich das Video dann und ich stoppe mein Tool oder ich baue es mal weiter aus und zeige später mal, warum ich diesen Ansatz besser finde. So ungefähr müsste man mit meinem Tool nur noch schreiben:
Delphi-Quellcode:
---> InterfaceUnit
Intf1 = Interface prop MyInteger:Integer; prop MyBoolean:Boolean; end; Intf2 = Interface prop MyString:String; prop MyByte:Byte; end; ---> KlassenUnit Class1 = class(TInterfacedObject, Intf1) end; Class2 = class(TInterfacedObject, Intf2) end; Class3 = class(TInterfacedObject, Intf1, Intf2) end; Alles andere würde automatisch ergänzt werden. Ebenso nachträgliche Änderungen in den Interfaces, wenn z.B. dort der Typ von einem Property geändert wird oder Parameter von var in const usw. |
AW: Interface-Unterstützung
Zitat:
Delphi-Quellcode:
die entsprechenden Methoden anlegt - ist ja wirklich nicht die Welt.
property Foo: Integer;
Und STRG + C ist einfach schneller als im CodeExplorer, den ich auch installiert habe, den AddProperty Dialog aufzurufen und dort die Daten einzugeben - so ganz trivial ist der Dialog auch nicht :-) Und sollte es eine vergleichbare Möglichkeit im MM Codeexplorer geben die ich bisher nur noch nicht gefunden haben, dann Asche auf mein Haupt.... |
AW: Interface-Unterstützung
Zitat:
Nur mal als schnelle Idee, wie ich etwas vergleichbares mit MMX realisieren würde (für ein Video habe ich gerade keine Zeit). Ich würde halt ganz anders anfangen. Ausgehend von zwei leeren Units:
Bei geschickt gewählten Default-Einstellungen sind in den MMX-Dialogen kaum Eingaben nötig (Name von Klasse/Property/Interface, Typ des Properties geht in der Regel per Auto-Complete mit 1-2 Tasten). Das sind vergleichbar viele (eher weniger) Eingaben als in deinem Beispiel zu machen sind. Allerdings kann man in beiden Fällen mit Code-Templates eventuell noch was rausholen. Es gibt ein Copy/Paste (Name des Interfaces) und ein Cut/Paste (Interface in andere Unit) für jedes Interface. Eine automatische Synchronisierung der Parameter ist dort leider nicht vorgesehen. Allerdings genügt hier analog zum Beschriebenen ein simples Copy/Delete/Paste der betroffenen Properties bzw. Methoden um das wieder zu synchronisieren. Das ist natürlich nicht so schnell und komfortabel wie deine Speziallösung, aber es ist ja auch nicht gerade und ausschließlich dafür gedacht. (Was nicht heißt, daß es sowas in Zukunft nicht bekommen könnte) Aber natürlich bleibt dein Tool für dich immer die bevorzugte Wahl. Schließlich baust du es ja auch genau nach deinen Bedürfnissen. |
AW: Interface-Unterstützung
Meiner Meinung nach wäre es viel sinnvoller, wenn es ein Tool geben würde, dass automatisch vergessende Semikolons hinter die Zeilen tackert.
Das regt mich immer maßlos auf! Schnell was hingeschrieben, Strg + F9 und Bumm: "Nak nak nak, Sie haben ein Semikolon vergessen...ich als Compiler finde das voll doof...ich könnte zwar selber eins hinbasteln, aber ich will Sie als Programmierer noch ein bissel gängeln!". |
AW: Interface-Unterstützung
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:39 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz