Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Die Delphi-IDE (https://www.delphipraxis.net/62-die-delphi-ide/)
-   -   Interface-Unterstützung (https://www.delphipraxis.net/193733-interface-unterstuetzung.html)

stahli 2. Sep 2017 11:01

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:
IMyIntf1 = Interfaces
  property MyProp1: string read write;
  procedure Do1(P: string);
end;

IMyIntf2 = Interfaces
  property MyProp2: string read;
  procedure Do2(P: string);
end;
UnitMyClass.pas

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:
procedure Do1(P: string);
im Interface ändere in
Delphi-Quellcode:
procedure Do1(const aValue: string);
sollte das auch in die Klassendeklarationen übertragbar sein, die dieses Interface verwenden (im Interface- und Implementationsteil).
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...

Aviator 2. Sep 2017 12:23

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.

jaenicke 2. Sep 2017 13:08

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.

stahli 2. Sep 2017 13:46

AW: Interface-Unterstützung
 
Oh, man beachte die zwei letzten Beiträge:
http://www.delphipraxis.net/130219-i...eexplorer.html

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?

himitsu 2. Sep 2017 14:25

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:
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;
Interface-Methoden legt nicht ver Klassenvervollständigung (Strg+Shift+C) an, sondern das übernimmt "teilweise" die Code-Vervolständigung (Strg+Leertaste),
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.

stahli 2. Sep 2017 14:52

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. :-(

Uwe Raabe 2. Sep 2017 15:23

AW: Interface-Unterstützung
 
Zitat:

Zitat von stahli (Beitrag 1380180)
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?

Die beiden ergänzen sich zwar, aber ich denke für dich ist MMX die bessere Wahl, da dieser direkt in der IDE arbeitet. Für MM müsstest du immer mit zwei separaten Programmen arbeiten: MM für das Klassen-Design und die IDE für das visuelle Design und das Compilieren. Auch was den Source-Editor betrifft, ist MM der IDE deutlich unterlegen.

himitsu 2. Sep 2017 16:58

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:

stahli 2. Sep 2017 17:25

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.

Uwe Raabe 2. Sep 2017 17:33

AW: Interface-Unterstützung
 
Zitat:

Zitat von himitsu (Beitrag 1380184)
Nein, wenn read/write nicht auf ein Feld, sondern ausschließlich auf Setter/Getter verweisen, dann wird niemald automatisch ein Feld angelegt,

MMX bietet eine (vom Benutzer erweiterbare) Reihe von möglichen Implementationen für die Getter und Setter bei der Implementierung eines Properties an. Darunter auch das Lesen und Schreiben eines Feldes, daß in diesem Zusammenhang gleich mit angelegt, initialisiert und im Falle einer Instanz bei Bedarf auch wieder freigegeben wird.

Vielleicht sollte ich wirklich mal ein paar Filmchen über MMX machen. Manch einer weiß offenbar gar nicht was er verpasst :-D

stahli 2. Sep 2017 17:35

AW: Interface-Unterstützung
 
Ich abonnier deinen Kanaaal!!!


EDIT: Huch, ich hatte das alles groß geschrieben aber die DP hat das offenbar zensiert...

Stevie 2. Sep 2017 19:15

AW: Interface-Unterstützung
 
Siehe Marcos Kommentare.

https://quality.embarcadero.com/browse/RSP-13306
https://quality.embarcadero.com/browse/RSP-16316

jaenicke 2. Sep 2017 21:58

AW: Interface-Unterstützung
 
Zitat:

Zitat von stahli (Beitrag 1380187)
Ein automatisches Feld sollte die Codevervollständigung m.E. anlegen und im Getter/Setter verwenden, wenn Getter und Setter erzeugt werden.

Ohne Code Explorer habe ich das immer so gemacht:
  • Ich schreibe
    Delphi-Quellcode:
    property BlaBlub: Integer;
  • Ich drücke Strg + Shift + C
  • Nun habe ich:
    Delphi-Quellcode:
    property BlaBlub: Integer read FBlaBlub write SetBlaBlub;
  • Nun ersetze ich F durch Get und Set durch F und drücke wieder Strg + Shift + C
  • Nun habe ich privates Feld, Getter und Setter
  • Nun muss ich nur noch das F nach write wieder in Set ändern
Am meisten lohnt sich das natürlich, wenn man mehrere Properties gleichzeitig anlegt und gleich mehrfach "read F" durch "read Get" ersetzen kann usw.

Aber schöner ist natürlich Code Explorer zu benutzen.

Zitat:

Zitat von Stevie (Beitrag 1380194)

Die Idee gefällt mir zwar überhaupt nicht, aber mal schauen was daraus wird...

himitsu 2. Sep 2017 22:24

AW: Interface-Unterstützung
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von jaenicke (Beitrag 1380202)
Ohne Code Explorer habe ich das immer so gemacht:
  • Ich schreibe
    Delphi-Quellcode:
    property BlaBlub: Integer;
  • Ich drücke Strg + Shift + C
  • Nun habe ich:
    Delphi-Quellcode:
    property BlaBlub: Integer read FBlaBlub write SetBlaBlub;
  • Nun ersetze ich F durch Get und Set durch F und drücke wieder Strg + Shift + C
  • Nun habe ich privates Feld, Getter und Setter
  • Nun muss ich nur noch das F nach write wieder in Set ändern

Strg+Leertaste, "prop", Typ auswählen, Enter
"Name", Tab, "Typname", Enter
fertig

stahli 2. Sep 2017 22:54

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:
property X: Interger read write;
steht.
Entweder konnten
Delphi-Quellcode:
function _get_X: Integer;
und
Delphi-Quellcode:
procedure _set_X(const aValue: Integer);
physisch im Code eingebaut werden oder der Compiler tut einfach so, als würden die dort stehen...
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...

stahli 2. Sep 2017 23:42

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.

himitsu 3. Sep 2017 00:18

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:
type
  TMyClass = class
  published
    property Name: string read FName write SetName;
  end;
das hier
Delphi-Quellcode:
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;
oder
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;

Uwe Raabe 3. Sep 2017 08:08

AW: Interface-Unterstützung
 
Zitat:

Zitat von stahli (Beitrag 1380206)
MMX bietet aber nicht die Möglichkeit, die Klasse durch fehlende Interface-Members zu ergänzen - oder?

Das Interface per Drag-Drop oder Copy-Paste auf die implementierende Klasse ziehen.

stahli 3. Sep 2017 08:22

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...

jaenicke 3. Sep 2017 11:53

AW: Interface-Unterstützung
 
Zitat:

Zitat von stahli (Beitrag 1380205)
So könnte der Compiler einfach normale Getter und Setter erzeugen bzw. voraussetzen, wenn im Interface
Delphi-Quellcode:
property X: Interger read write;
steht.
Entweder konnten
Delphi-Quellcode:
function _get_X: Integer;
und
Delphi-Quellcode:
procedure _set_X(const aValue: Integer);
physisch im Code eingebaut werden oder der Compiler tut einfach so, als würden die dort stehen...

Die Logik, dass der Setter SetXyz und der Getter GetXyz heißt, gibt es in Delphi ja schon. Da braucht man nicht mit den unsäglichen Unterstrichen anfangen. In anderen Sprachen sind die üblich, in Delphi entsprechend des Styleguides kein guter Programmierstil. Und das finde ich auch gut so.

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:

Zitat von stahli (Beitrag 1380205)
Ich will einfach weniger schreiben müssen und weniger Redundanzen haben.

Redundanz, klar, das ist ein Argument, aber das mit dem weniger schreiben wollen finde ich als Entwickler unangebracht. Denn gute Bezeichner usw. sollten ohnehin nicht ganz kurz sein usw., so dass man ohnehin nicht schreibfaul sein sollte, wenn man guten Code schreiben möchte.

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...

Rollo62 3. Sep 2017 12:50

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

himitsu 3. Sep 2017 12:59

AW: Interface-Unterstützung
 
Das Problem bei Automatisch/Implizit:
Zitat:

Delphi-Quellcode:
property X: Interger read write;

Ist "write" der Name des Getters?

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;
...

stahli 3. Sep 2017 13:11

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:
// MyIntf.pas
...
IMyintf = interface
  prop X:Integer;
  prop Y:Integer ro; // read only
  prop Z:Integer wo; // write only
end;
soll es die notwendigen Getter und Setter definieren und aus

Delphi-Quellcode:
// MyClass.pas
...
TMyintf = class(TBaseClass, IMyIntf)
end;
soll es die erforderlichen Propertys und Methoden entsprechend der eingebunden Interfaces erstellen und alles (incl. des Implemantationsteils) vernünftig strukturieren.


@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.

Wosi 3. Sep 2017 20:22

AW: Interface-Unterstützung
 
Bevor du dich an einem Parser versuchst, solltest du lieber auf DelphiAST (https://github.com/RomanYankovsky/DelphiAST) zurückgreifen.

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: https://twitter.com/OmniPascal/statu...53751267790849

stahli 3. Sep 2017 21:29

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:
...
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
...
wird zu:
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.

stahli 4. Sep 2017 23:21

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:
BaseProp: integer
in
Delphi-Quellcode:
BaseProp: Boolean
ändern kann und dadurch Getter und Setter im Interface angepasst werden.

Die Klassen werden dann unabhängig von der Ausgangssituation an die aktuelle Interface-Deklaration angepasst.

stahli 5. Nov 2017 22:12

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?

stahli 7. Nov 2017 11:55

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.

TiGü 7. Nov 2017 12:26

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.

stahli 7. Nov 2017 12:47

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.

TiGü 7. Nov 2017 15:41

AW: Interface-Unterstützung
 
Zitat:

Zitat von stahli (Beitrag 1385483)
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.

Kannst du dazu ein Beispiel aus der Praxis liefern?
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.

stahli 7. Nov 2017 16:23

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...

Fritzew 7. Nov 2017 16:31

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.....

Uwe Raabe 7. Nov 2017 16:32

AW: Interface-Unterstützung
 
Zitat:

Zitat von Fritzew (Beitrag 1385522)
Wahrscheinlich bin ich zu doof.....

Dann wären wir schon zwei :stupid:

Fritzew 7. Nov 2017 16:39

AW: Interface-Unterstützung
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1385523)
Dann wären wir schon zwei :stupid:

Irgendwie beruhigt mich das :lol:

stahli 7. Nov 2017 17:01

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.

Lemmy 7. Nov 2017 17:24

AW: Interface-Unterstützung
 
Zitat:

Zitat von Fritzew (Beitrag 1385522)
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.....

Ein STRG + C für Interfaces, das bei

Delphi-Quellcode:
property Foo: Integer;
die entsprechenden Methoden anlegt - ist ja wirklich nicht die Welt.

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....

Uwe Raabe 7. Nov 2017 19:11

AW: Interface-Unterstützung
 
Zitat:

Zitat von stahli (Beitrag 1385525)
Nach meiner bisherigen Einschätzung wird das trotzdem noch eine Schreib- oder Kopierorgie.

Das sehe ich zwar anders, aber deine Anforderungen sind schon sehr speziell (so speziell zumindest, daß ich sie zumindest bisher noch nicht vermisst hätte) und da ist natürlich ein speziell darauf abgestimmtes Tool nicht zu toppen.

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:
  • die Klasse anlegen (MMX-Dialog, Code-Template oder von Hand)
  • die Properties in den Klassen anlegen (der MMX-Dialog erlaubt bereits unterschiedliche, auch selbst definierte Implementierungen)
  • die Properties in MMX markieren
  • über (MMX-)Refactorings - Extract Interface das Interface erzeugen
  • den Interfacenamen dabei gleich in die Zwischenablage kopieren
  • und nach einem Komma in die Klassendeklaration kopieren
  • das Interface per MMX-Cut/Paste in die andere Unit verlagern
  • falls noch nicht geschehen die andere Unit per Ctrl-U (MMX) oder Alt-F11 (IDE) usen

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.

TiGü 8. Nov 2017 07:47

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!".

Stevie 8. Nov 2017 10:54

AW: Interface-Unterstützung
 
Zitat:

Zitat von TiGü (Beitrag 1385572)
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!".

Es soll Programmiersprachen geben, wo man gar keine Semikolons machen muss, weil der Compiler schlau genug ist, zu erkennen, wo Anweisungen enden.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:32 Uhr.
Seite 1 von 2  1 2      

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