![]() |
Verträge für Delphi / Design by Contract
Hallo,
gibt es eine Möglichkeit, in Delphi Verträge (Contracts) benutzen zu können? Ich würde gerne das Konzept ![]() Für Java gibt es verschiedene Plugins, gibts sowas auch für Delphi? Grüße Dominik |
AW: Verträge für Delphi / Design by Contract
Require und ensure sind nur in Oxygene (ehemals Delphi Prism / Prism) Teil der Sprache. Wenn du doch dafür interessiert und Object Pascal nutzen willst, würde ich dir raten in die Richtung zu gehen.
![]() In Delphi selbst gibt es dafür nichts, da musst du das selbst im Quelltext umsetzen ohne dafür eine Sprachunterstützung zu haben. Auch Addons kenne ich dafür nicht (kann mir aber auch schwer vorstellen was die dabei helfen sollten). |
AW: Verträge für Delphi / Design by Contract
Zitat:
Delphi hat in den letzten 12 Jahren leider viel an Boden verloren. Also Emba: baut das ein, Pre-/Postconditions und Invariants wären doch relativ einfach unzusetzen. |
AW: Verträge für Delphi / Design by Contract
Ich sehe allerdings nicht viele Vorteile gegenüber rein eigenem Code. Denn angeben muss man das so oder so und ob man die Bedingungen nun komplett prüft oder nur die Bedingungen hinschreibt... Eine Funktion wie Assert für diese Fälle kann man auch selbst schreiben, die das Ergebnis prüft.
Invariants lassen sich natürlich nicht ganz so einfach umsetzen, weil man deren Prüfung manuell in jede Methode einer Klasse packen müsste... |
AW: Verträge für Delphi / Design by Contract
Zitat:
Bei echtem Design by Contract prüft Dir schon der Compiler ob die Argumente passen. Wenn Du einen Check <> nil drin hast, und die Methode mit einem hart codierten nil in einem ganz seltenen Use-case aufrufst, dann fliegt Dir das mit einer selbstgebastelten Lösung erst beim Kunden um die Ohren wenn der richtig (bzw. falsch) rumklickt. Mit echten Code Contracts sagt Dir aber schon der Compiler, das der eine Aufruf nicht passt weil er den Contract verletzt. Der Mehrwert ist nicht zu unterschätzen. |
AW: Verträge für Delphi / Design by Contract
Dass der Compiler so etwas erkennt, dürfte in echtem Code allerdings gegen Null gehen. Denn um bei deinem Beispiel zu bleiben eine Funktion absichtlich mit nil aufrufen, wenn die Funktion das gar nicht unterstützt, das mag vielleicht mal passieren, fällt aber beim Test auf und ist leicht zu finden.
Viel wahrscheinlicher und vor allem schwerer zu finden sind aber doch Fehler, die durch zur Laufzeit veränderte Variablen entstehen. Und die kann der Compiler ohnehin nicht erkennen. |
AW: Verträge für Delphi / Design by Contract
Zitat:
Außerdem gehört das dann zum Verhalten des Objektes, und nicht zum Datentyp an sich. Der Vertrag vermischt sich mit der Implementierung. Des Weiteren gibt es ein Einsatzgebiet bei dem das nicht funktioniert: abstrakte Datentypen. Einem Interface kannst du Pre- und Postconditions nicht beibringen. Da Interfaces für mich immer wichtiger werden (man soll mit Interfaces arbeiten, nicht direkt mit Objekten) habe ich gehofft, dass es ein Plugin / Addon für Delphi gibt. In Java kenne ich eins, dass dies mit Hilfe von Annotations umsetzt. Da man in Delphi auch Annotations einsetzen kann dachte ich, ich frag mal hier :-) |
AW: Verträge für Delphi / Design by Contract
Pluginmäßig lässt sich das denke ich in Delphi nicht umsetzen. Annotationen in Java bieten ganz andere Möglichkeiten...
|
AW: Verträge für Delphi / Design by Contract
Zitat:
Genau das ist ja gerade die Mächtigkeit von Design by Contract. Wenn man das konsequent durchzieht werden die Contracts direkt gegeneinander validiert und es ist zur Compilezeit möglich sämtliche Fehler durch falsche Ein- und Ausgabeparameter auszufiltern. |
AW: Verträge für Delphi / Design by Contract
Zitat:
|
AW: Verträge für Delphi / Design by Contract
Was die Laufzeit Auswertung angeht, kann man das über AOP machen.
|
AW: Verträge für Delphi / Design by Contract
für ein Vorbedingungs- Nachbedingungs- Zwischenbedingungs Sonstiges
Kontroll-Konstrukt kann man schön methodenlokale Prozeduren/ funktionen nutzen( die nat. ggf andere Methoden aufrufen). Je nach Lage der methodenlokalen Variablen zur o.a. Prozedur/Fkt kann man dann auf diese Variablen und die Parameter wie gewünscht zugreifen oder aber aus dem Zugriff raushalten. Mal so auf die Schnelle:
Delphi-Quellcode:
type
TMyCheckSituation = (sVB, sSit1, sNB ); procedure TForm3.Mach(aInt: integer); var TestMe: integer; procedure Chk( const MySituation: TMySituation); begin case MySituation of sVB: if (aInt<8) and ( TestMe<0) then // Testfehler!: // irgendwie Fehlerbedingung händeln, z.B: except. raise Exception.Create('Loud Cry'); sSit1: ;// Check für Sit.1 sNB: ;// Nachbedingungsckeck; end; DoMyGeneralTest; end; var DontTestMe: integer; begin Chk(sVB); // Situationstest Vorbedingung DontTestMe := 1; MachWas; TestMe := DontTestMe; {$REGION 'InnerCheck SaubereMitte'} Chk(sSit1); // Situationstest Situation 1 {$ENDREGION} MachNochWas; Chk(sNB); // NachBedingung end; |
AW: Verträge für Delphi / Design by Contract
Nur so eine Idee, die mir gerade kam, aber vielleicht könnte man auch records dafür missbrauchen.
Z.B.:
Delphi-Quellcode:
Keine Ahnung, ob Delphi das Konstrukt überhaupt zulässt... ich hatte mit dem Implicit-Operator unter Delphi 2006 schon bei harmloseren Sachen Probleme, aber bei 2006 waren überladene record-Operatoren generell buggy.
type
EContractViolation = class(Exception) end; TObjectNotNil = record private FObj: TObject; public operator Implicit(const Obj: TObject): TObjectNotNil; operator Implicit(const Obj: TObjectNotNil): TObject; end; operator TObjectNotNil.Implicit(const Obj: TObject): TObjectNotNil; begin if not Assigned(Obj) then raise EContractViolation.Create("Reference must not be nil"); Result.FObj := Obj; end; operator TObjectNotNil.Implicit(const Obj: TObjectNotNil): TObject; begin Result := Obj.FObj; end; procedure DoSomethingWithObject(Obj: TObjectNotNil); begin Obj.{...} end; DoSomethingWithObject(nil); // => EContractViolation Und natürlich ist das so, wie es hier steht, viel zu aufgebläht... aber vielleicht könnte man ja irgendwie einen Preprocessor schreiben, der den Code vor der Kompilierung generiert, oder vielleicht kann man mit Generics noch was rausholen. Wahrscheinlich ist das alles nicht wirklich praktikabel, aber ich wollte die Idee gerade mal niederschreiben... |
AW: Verträge für Delphi / Design by Contract
Zitat:
Das funktioniert so aber auch schon mit Delphi 2006 würde ich behaupten, auch wenn ich das gerade nicht testen kann... Ich habe dort schon relativ viel mit Klassenoperatoren gemacht und hatte damit keine Probleme. |
AW: Verträge für Delphi / Design by Contract
Das geht auch mit einem generischen Record, so wie
![]() Kleiner Unterschied bei dieser Lösung allerdings ist, dass im Falle von nil die Exception vor dem Aufruf und nicht danach bzw innerhalb der aufgerufenen Methode geraised wird. |
AW: Verträge für Delphi / Design by Contract
Solche Lösung sind zwar gut gegen Nullpointer, biete aber keine statische Analysefähigkeit.
Das ist es, was die Design-by-Contract so wertvoll macht. Anfangs- und Endzustände sowie Invarianten checken kann man zwar manuell machen. Aber schon mit dem (erzwungenen) Vererbungen der "Contracts" wird ohne Compiler-Unterstützung kniffelig bis unmöglich. Es ist wie mit der Typen: Wenn es nicht erzwungen wird, ist nicht so viel Wert. Das Argument mit dem Halteproblem kann ich nicht nachvollziehen. Abgesehen von dem begrenzten Speicher sollte es problemlos möglich sein, eine Turingmaschine zu realisieren. Schließlich bieten die Contracts keine Aussage über die Laufzeit. |
AW: Verträge für Delphi / Design by Contract
Zitat:
|
AW: Verträge für Delphi / Design by Contract
Zitat:
Aber ich glaube, mir ist eben des Halteproblemargument aufgegangen: Es ist nicht aufgrund des Halteproblems nicht möglich, herauszubekommen, ob zwei Prozeduren (~> Turingmaschinen) das selbe Ergebnis liefern. Also müsst der Programmierer im Zweifelsfall vor dem Aufruf explizit auf die gleiche Bedingung prüfen, die in dem Contract spezifiziert ist. Das wäre unschön (keine Perfektion). Ich glaube aber, dass wirklich praktisch auftretende Problem ist, dass Contracts nur eine weitere Spezifikationen sind, die unvollständig oder schlicht gänzlich falsch sein können. |
AW: Verträge für Delphi / Design by Contract
Zitat:
|
AW: Verträge für Delphi / Design by Contract
Zitat:
Du hast eine Funktion A die gemäß Contract einen beliebigen Wert zwischen 1 und 10 zurückliefern kann. Du hast eine andere Funktion B, die einen Parameter hat der per Contract nur Werte zwischen 0 und 5 annehmen darf. Wenn Du nun eine Variable aus A füllst und in B reinwirfst, dann kann Dir der Compiler eine Warnung um die Ohren hauen das dieses Ding Deinen Contract verletzt. Du kannst nun entweder * die Fälle von 6-10 mit einer Sonderbehandlung bedienen und nur die Werte bis 5 in B reinwerfen * oder aber sicherstellen das A nur noch Werte zurückgibt die B auch annehmen kann * oder aber dafür sorgen das B auch Werte bis 10 verträgt. Machst Du nichts davon und ignorierst die Warnung einfach, DANN wirst Du möglicherweise zur Laufzeit Probleme bekommen. Es geht ja nicht darum, vollumfänglich zur Compilezeit sicherzustellen das da nie was falsches reingegeben wird. Es geht darum, das Dir der Compiler vorab ausreichend Hinweise gibt wo es eben potentiell zu Contractverletzungen und damit zu möglichen Fehlerquellen kommen kann. So kann man nämlich vorher mögliche Problemfälle analysieren, automatisch testen und muss nicht erst warten bis es draussen irgendwo wegen einer seltsamen Eingabe mal in einem Edge-Case zu einem Fehler kommt. |
AW: Verträge für Delphi / Design by Contract
[OT]... Danke! Jetzt habe ich endlich ungefähr verstanden, worüber Ihr die ganze Zeit redet. :thumb:[/OT]
|
AW: Verträge für Delphi / Design by Contract
Zitat:
Code:
Kann das dein konkreter Compile-Time Analyzer korrekt erkennen, oder liefert er, wovon ich in diesem Beispiel spontan ausgehen würde, ein false positive (im Sinne einer Warnung: Nachbedingung ist nicht sichergestellt). Dann will ich das nicht wirklich haben, weil wenn dann die statische Analyse zu einem Haufen Warnungen führt, die keine sein sollten, neigt man ja schnell wieder dazu, solche Warnungen gleich allgemein tendentiell eher zu ignorieren. Und selbst wenn es in diesem konkreten Beispiel funktioniert, gibt es doch genug (oder bessergesagt unendlich viele) Fälle, in denen es einfach nicht funktionieren kann.
function randInt(max: Int): Int = {
return Random.nextInt(max); } ensures: result <= max Zur Compilezeit hilft mir aber eine Analyse hauptsächlich, wenn sie entweder zu 100% ausschließen kann, dass ein Vertrag verletzt wird (denn dann kann ich den Vertrag eliminieren) oder aber erkennen kann, dass ein Vertrag zwangsläufig verletzt wird. Den Mehrwert von Erkenntnissen in der Welt dazischen zur Compilezeit wage ich zu bezweifeln. Denn wenn ich o.g. Beispiel dann so schreibe, dass der Vertrag immer erfüllt wird, dann führt das zu absolut sinnbefreitem Code. Apropos: die bisher genannten Beipsiele sprechen für mich eher gegen das verwendete Typsystem als für Verträge. Denn wenn es ein gutes Typsystem wäre, könnte ich dann nicht Dinge wie "kann oder kann nicht nil sein" oder aber "Zahl im Wertebereich X" auch direkt über die Typen ausdrücken ;) ? |
AW: Verträge für Delphi / Design by Contract
Die korrekte Funktion der Methode und die Voraussetzung das sie ihren Contract einhält würdest Du mit Unit-Tests abdecken.
Daher ist die Prüfung des 'Innenverhältnisses' des Vertrages nicht so relevant. Das 'Aussenverhältnis' ist das, was für eine solche Codeanalyse wirklich relevant ist. Wenn die Nachbedingung nicht passt weil Dein Random.NextInt(max) sich falsch verhält und Werte > max liefert, dann wird das klar erst zur Laufzeit auffallen. Aber in aller Regel wird sowas eben entsprechend gut getestet sein damit das nicht passiert. |
AW: Verträge für Delphi / Design by Contract
Zitat:
Wobei ich mich grade frage, wie das Eurekalog und vorallem AQTime machen? |
AW: Verträge für Delphi / Design by Contract
Zitat:
Und über die Tools-API ist glaube ich nun mittlerweile noch mehr möglich. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:02 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 by Thomas Breitkreuz