![]() |
Re: Interfaces
Außerdem wird vom Compiler die Implementierung aller Interface-Member erzwungen, bei Ableitungen von abstrakten Klassen müssen nicht alle abstrakten Methoden überschrieben werden (dafür gibt es zur Laufzeit ggf. EAbstractError-Exceptions).
Das gilt jedenfalls unter Delphi.Win32, unter .NET muss z.B. jede Klasse, die einen abstrakten Member enthält, mit dem Modifier "abstract" deklariert werden. Solange die Klasse "abstract" ist, kann sie nicht instanziiert werden. |
Re: Interfaces
Moin,
so ich geb auch mal meinen senf dazu :) Schnitstellen (interfaces) sind nicht nur prima, um klassen irgendwie abstrakt zu definieren, sondern sind eine optimale entkopplungs-technick. Dh. man kann verschiedene systeme einer anwendung logisch trennen, sodass sie nur durch die schnittstelle verbunden sind - der name ist da eigentlich sehr sprechend. In delphi ist eigentlich das konzept der Events viel bekannter, was auch exessiv zur entkopplung eingesetzt wird, aber immer nur eine Information-liefern kann, da es nur eine methode definiert, was bei interfaces bekanntlich anders ist. Ich setzt interfaces sehr gerne als strategie ein, dh, ich übergebe einer klasse ein interface, worüber sie dann informationen beziehen oder aktionen ausführen kann, die sie eigentlich nix angehen und nicht in ihrem verantwortungsbereich liegen. Ein beispeil:
Delphi-Quellcode:
In dem beispiel soll ein text-template-processor einen text auseinandernehmen und immer wenn eine variable auftaucht, sie durch ihren wert ersetzen. Weil der template prozessor wiederverwendbar sein soll, darf er nicht darüber entscheiden mit welchem zeichen einen variable (im templat-text) definiert wird. So, dafür befragt er einfach die strategie...
type
ITemplateVariableStrategy = interface ['{FAE1E462-0E43-4B8E-A8D0-A594E0B25E67}'] function GetVariableValue(const VarName:string{; appearance:integer}):string; function GetVariableOpenDelimiter:char; function GetVariableCloseDelimiter:char; end; TAbstractTemplate = class(TObject) private FVariableStrategy:ITemplateVariableStrategy; FOpenDelimiter: char; FCloseDelimiter: char; protected ... public constructor Create(TemplateVariableStrategy:ITemplateVariableStrategy); reintroduce; function Process(const Text:String):string; end;
Delphi-Quellcode:
...und ist nun informiert. Nun findet er eine variable mit einem bestimmten namen und fragt wieder die strategie welchen text er denn nun einsetzen soll, weil er es selbst nicht entscheiden kann.
FOpenDelimiter := FVariableStrategy.GetVariableOpenDelimiter;
Es muss nur eine instanz geben die das entscheiden kann und die entsprechenden information liefert. Das kann die aufrufende instanz selbst sein, oder eine andere instanz die dafür geschaffen wurde und natürlich das interface implementiert:
Delphi-Quellcode:
Als beispiel. Sie wird dann mit den information gefüttert und liefert die werte direkt an den template-processor.
TVariableContainer = class(TInterfacedObject, ITemplateVariableStrategy)
private ... function GetVariable(const Name: string): TVariable; public constructor Create(OpenChar, CloseChar:char); overload; constructor Create(OpenChar, CloseChar:char; Variables:array of TVariable); overload; function GetVariableCloseDelimiter: Char; function GetVariableOpenDelimiter: Char; function GetVariableValue(const VarName: String): String; procedure AddVar(aVar:TVariable); procedure CreateVar(const aName, aValue:string); procedure DeleteVar(const VarName:string); ... end; So wird alles dynamisch gehalten und kann sehr leicht ausgewechselt werden. Mit der definition einer schnitstelle schliesst man gewissermassen einen vertrag mit unbekannt, dass bestimmte aufgaben von ihm zu übernehmen sind, wenn er den vertrag implementiert (er kann den vertrag ja immernoch brechen, indem er exceptions schmeisst) :) Das ist jetzt nur einen kleiner aspekt der schnittstellen-verwendung und sollte nur als anschauung dienen. PS: ich würd die referenzzählung nicht ausschalten! |
Re: Interfaces
Ja, das Interfaces zur Mehrfachvererbung benutzt werden können war mir klar, hab meine Frage vielleicht etwas unsauber implementiert. :wink: Ich meinte nämlich eigentlich darüber hinaus. (Was natürlich nicht den Nutzen von Mehrfachvererbung herunterspielen soll)
Was die Sprachunabhängigkeit angeht: So wie ich das bis jetzt mitgekriegt habe, ist es unmöglich, aus einer DLL eine Klasse zu exportieren (warum eigentlich?) Ist das mit Interfaces möglich? Denn sonst verlagert sich doch das Problem eigentlich nur. Um ein Plugin zu schreiben, müsste ich trotzdem die Architektur des Plugins kennen und vom Interface ableiten, das dementsprechend statisch eingebunden werden müsste--genau wie bei einer Vorfahrklasse. (Ich merk gerade, dass ich mich ein bisschen im Kreis drehe. Muss erstmal nachdenken, ob eine dynamische Vorfahrklasse/-interface überhaupt funktionieren könnte. Von wegen Methoden löschen/umbenennen/Refactoring etc :gruebel: ). @Khabarakh: Echt? Bei mir hat er da noch nie gemuckt, macht zwar immer Warnungen von wegen abstrakte Klasse mit Methode XYZ etc usw wird erzeugt, aber laufen tuts. Bloß die Methode(n) aufrufen sollte man halt nicht. @maximov: Das hört sich sehr interessant an, muss ich mir mal genauer anschauen (hab gleich aber einen Termin => später). Aber zunächst muss ich mal devil's advocate spielen und fragen: Anstatt der Methode Interfaces zu übergeben hätte man aber doch genauso gut auch Objekte übergeben können. Also wo ist der Unterschied? Anders ausgedrückt: Angenommen, ich würde versuchen, in deinem Beispiel sämtliche Interfaces durch Objekte zu ersetzen. Wo würde es haken? |
Re: Interfaces
Zitat:
Zitat:
![]() |
Re: Interfaces
Oh hab ich dich missverstanden.
Ja das Beispiel im Link ist mir bekannt, sowas ähnliches ist ja auch auf der BDN-Site zu sehen. Ich war auch dabei, für eine Anwendung Plugins zu implementieren. (Liegt aber momentan auf Eis). Was mich da gestört hat, war, dass ich die Vorfahrklasse statisch, über eine Unit, einbinden muss. Ändere ich die Vorfahrklasse, muss ich alle Plugins rekompilieren. |
Re: Interfaces
Hallo,
Zitat:
Gruß xaromz |
Re: Interfaces
Zitat:
Es ist sogar so, das der template-prozessor nichtmal wissen muss wie man einen text auseinander nimmt, da er ja nur nach schema-F -> zur nächsten variable geht -> namen der variable holen -> wert von strategie holen -> wert in das resultat einfügen -> und das so lange bis EOF tun muss! Für das tatsächliche verarbeiten der textdaten könnte man einen text-prozessor einsetzen, sodass folgendes ausreichend ist um ein hoch kompliziertes template zu füllen:
Delphi-Quellcode:
durch die einfachheit, klarheit und aufgabentrennung lassen sich unglaublich viele fehler quellen vermeiden, was dann end-viel zeit spart. Der textprocessor ist dann folgendermassen deklariert und wie er implementiert ist spielt ja eigentlich keine rolle:
TextProcessor := CreateTextProcessor(Text);
// process the template // while TextProcessor.ForwardToNextVariable do TextProcessor.InsertValue( VariableStrategy.GetVariableValue( TextProcessor.GetCurrentVariable)); Result := TextProcessor.GetResult;
Delphi-Quellcode:
Klar kommt man mit einem schnellschuss evtl. schneller zu einem ergebnis, aber ob das auch derart wiederverwendbar und dynamsich ist - ist fraglich ;) Hoffe das reicht als erklärung des unterschieds....zumindest sehe ich das so. ISt natürlich nicht in stein gemeisselt, denn das ist sicher noch eleganter und abgefahrener zu lösen.
ITextProcessor = interface
['{D7845C11-D65B-4780-9874-B9A1E4F656BA}'] function ForwardToNextVariable:boolean; function GetCurrentVariable:String; procedure InsertValue(const value:string); function GetResult:string; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:06 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