Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Interfaces - Multiple Inheritance (https://www.delphipraxis.net/153524-interfaces-multiple-inheritance.html)

ThE_-_BliZZarD 6. Aug 2010 11:14

Delphi-Version: 2010

Interfaces - Multiple Inheritance
 
Hallo liebe Delphi-Gemeinde,

ich habe jetzt schon etliche Artikel und Forendiskussionen dazu abgesucht, aber leider keinen Hinweis gefunden. Ich versuche, ein Interface von 2 weiteren Interfaces erben zu lassen, etwa wie folgt:

Delphi-Quellcode:

IReadObject = Interface(IInterface)
   ['{26828CFB-864E-488E-914D-B4BEBD49F641}']
   function getMass: Double;
End;

IWriteObject = Interface(IInterface)
   ['{B9F10F92-1145-4108-8C93-386ABA334A88}']
   procedure setMass(AValue: Double);
End;

// Das funktioniert so leider nicht
// Fehler: [DCC Fehler] E2029 ')' erwartet, aber ',' gefunden (In der Zeile "IFullObject..", Cursor steht vor ", IWriteObject)"
IFullObject = Interface(IReadObject, IWriteObject)
   ['{19FCFAF2-1BF0-45BE-AABF-0C5138F7D7ED}']
   property Mass: Double read getMass write setMass;
end;
Wie bereits im Codeblock geschrieben tritt ein Fehler auf.

Funktioniert das mehrfache Erben bei Interfaces (so wie bei Klassen) nicht? Oder mache ich etwas falsch?

Mit freundlichen Grüßen,

BliZZarD

Uwe Raabe 6. Aug 2010 11:16

AW: Interfaces - Multiple Inheritance
 
Interfaces können genau wie Klassen immer nur einen Vorfahren haben. Klassen können aber mehrere Interfaces implementieren.

Uwe Raabe 6. Aug 2010 11:24

AW: Interfaces - Multiple Inheritance
 
Um es mal auf dein Beispiel zu beziehen:

Delphi-Quellcode:
IReadObject = Interface(IInterface)
   ['{26828CFB-864E-488E-914D-B4BEBD49F641}']
   function getMass: Double;
   property Mass: Double read getMass;
End;

IWriteObject = Interface(IInterface)
   ['{B9F10F92-1145-4108-8C93-386ABA334A88}']
   procedure setMass(AValue: Double);
   property Mass: Double write setMass;
End;

TFullObject = class(TInterfacedObject, IReadObject, IWriteObject)
private
  function getMass: Double;
  procedure setMass(AValue: Double);
end;
Du kannst jetzt auch noch dies machen:

Delphi-Quellcode:
IFullObject = Interface(IReadObject)
   ['{19FCFAF2-1BF0-45BE-AABF-0C5138F7D7ED}']
   procedure setMass(AValue: Double);
   property Mass: Double read getMass write setMass;
end;

TFullObject = class(TInterfacedObject, IReadObject, IWriteObject, IFullObject)
Die implementierende Klasse muss dann das Interface nur noch aufführen.

In diesem Fall wäre IWriteObject dann aber eigentlich überflüssig.

himitsu 6. Aug 2010 11:28

AW: Interfaces - Multiple Inheritance
 
Nein, IReadObject ist "überlüssig"/unnötig, da es schon in IFullObject enthalten ist (der Vorfahre).
Die Funktion von IWriteObject ist zwar auch in IFullObject enthalten, aber das Interface selber natürlich nicht.

Uwe Raabe 6. Aug 2010 11:42

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von himitsu (Beitrag 1039997)
Nein, IReadObject ist "überlüssig"/unnötig, da es schon in IFullObject enthalten ist (der Vorfahre).
Die Funktion von IWriteObject ist zwar auch in IFullObject enthalten, aber das Interface selber natürlich nicht.

Das sehe ich etwas anders! Lass das mal laufen:

Delphi-Quellcode:
type
  IFather = interface
    ['{41780D81-7DD0-491B-831D-E3C247FB425F}']
  end;

  IChild = interface (IFather)
    ['{BAB88147-561D-4786-BC59-FDA90AFF3669}']
  end;

  TClass1 = class(TInterfacedObject, IFather, IChild)
  end;

  TClass2 = class(TInterfacedObject, IChild)
  end;

procedure Test;
var
  intf1: IInterface;
  intf2: IInterface;
begin
  intf1 := TClass1.Create;
  intf2 := TClass2.Create;
  if Supports(intf1, IFather) then
    ShowMessage('TClass1 supports IFather')
  else
    ShowMessage('TClass1 doesn''t support IFather');
  if Supports(intf2, IFather) then
    ShowMessage('TClass2 supports IFather')
  else
    ShowMessage('TClass2 doesn''t support IFather');
end;

Blup 6. Aug 2010 12:04

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1039986)
Interfaces können genau wie Klassen immer nur einen Vorfahren haben.

Darüber bin ich auch schon gestolpert. Bei Klassen ist Mehrfachvererbung kompliziert. Aber ich kenne keinen zwingenden Grund, warum Mehrfachvererbung für Interfaces nicht vorgesehen wurde.

Stevie 6. Aug 2010 12:13

AW: Interfaces - Multiple Inheritance
 
Uwe hat recht. Interfaces, die nicht explizit bei einer Klasse angegeben sind bekommen keinen VMT Slot und sind demnach auch nicht über Supports verfügbar. Auch wenn sie eigentlich über eine Interface Vererbung drin sein müssten.

himitsu 6. Aug 2010 12:29

AW: Interfaces - Multiple Inheritance
 
Zitat:

In diesem Fall wäre IWriteObject dann aber eigentlich überflüssig.
Wenn ich das jetzt nochmal durchlese:
Meinst du das Interface oder die Angabe dessen im Objekt?


Aber gut zu wissen ... dachte daß auch die Interfaces supportet werden, welche als Parent indirekt mit eingebunden sind. :shock:

Der schöne Günther 19. Nov 2013 22:20

AW: Interfaces - Multiple Inheritance
 
Ich hoffe das ist ok, dass ich das alte Thema wieder ausbuddele.

Aber genau die gleiche Frage stelle ich mir mittlerweile auch. Nochmal:
  • Interface
    Delphi-Quellcode:
    IParent
  • Interface
    Delphi-Quellcode:
    IChild
    erbt von
    Delphi-Quellcode:
    IParent
  • Klasse
    Delphi-Quellcode:
    TChild
    realisiert ausschließlich
    Delphi-Quellcode:
    IChild

Wie kann ich überhaupt erfahren, dass
Delphi-Quellcode:
TChild
auch
Delphi-Quellcode:
IParent
supported? Müsste ich in meiner Klasse das
Delphi-Quellcode:
QueryInterface
selbst überschreiben? Oder sollte man sich Interface-Vererbung generell abgewöhnen?

Uwe Raabe 19. Nov 2013 22:55

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1236632)
Wie kann ich überhaupt erfahren, dass
Delphi-Quellcode:
TChild
auch
Delphi-Quellcode:
IParent
supported? Müsste ich in meiner Klasse das
Delphi-Quellcode:
QueryInterface
selbst überschreiben? Oder sollte man sich Interface-Vererbung generell abgewöhnen?

Wenn du willst, daß TChild sowohl IChild als auch IParent supported, dann musst du beide Interfaces angeben. Andernfalls kannst du nur an das IChild Interface heran. Da du aber weißt (aus der Deklaration), daß IChild von IParent abgeleitet ist, kannst du natürlich auch die IParent-Methoden verwenden. Es kommt also wie immer darauf an, was man erreichen will: man muss halt wissen, was man tut.

Übrigens: Interface-Vererbung hast du immer dabei - jedes Interface ist von IInterface abgeleitet.

Der schöne Günther 19. Nov 2013 23:08

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1236634)
Da du aber weißt (aus der Deklaration), daß IChild von IParent abgeleitet ist, kannst du natürlich auch die IParent-Methoden verwenden.

Das offensichtlichste ist mir natürlich wieder nicht aufgefallen, natürlich! Ich finde es trotzdem komisch, dass Supports() angibt, das Interface würde nicht unterstützt. Dagegen kann ich wirklich nichts tun?

himitsu 19. Nov 2013 23:28

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1236636)
Dagegen kann ich wirklich nichts tun?

Zitat:

Zitat von Uwe Raabe (Beitrag 1236634)
Wenn du willst, daß TChild sowohl IChild als auch IParent supported, dann musst du beide Interfaces angeben.

:?:

Furtbichler 20. Nov 2013 07:19

AW: Interfaces - Multiple Inheritance
 
Sollte 'GetInterface' das Problem nicht lösen?

Dessenungeachtet frage ich mich, was 'Supports' für einen Sinn macht, wenn man damit nicht herausfinden kann, ob eine Instanz ein bestimmtes Interface implementiert. Oder einfach ausgedrückt: Wozu dient 'Supports'? Was es macht, weiß ich mittlerweile, aber wann benötigt man genau diese Funktionalität ("Ist IMyInterface in der Deklaration der Klasse TMyClass enthalten").

Stevie 20. Nov 2013 07:26

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Furtbichler (Beitrag 1236648)
Sollte 'GetInterface' das Problem nicht lösen?

Dessenungeachtet frage ich mich, was 'Supports' für einen Sinn macht, wenn man damit nicht herausfinden kann, ob eine Instanz ein bestimmtes Interface implementiert. Oder einfach ausgedrückt: Wozu dient 'Supports'? Was es macht, weiß ich mittlerweile, aber wann benötigt man genau diese Funktionalität ("Ist IMyInterface in der Deklaration der Klasse TMyClass enthalten").

Wenn du das weißt, dann solltest du wissen, dass:
a) Supports mehrfach überladen ist, so dass man nicht nur prüfen kann, ob eine Klasse ein Interface implementiert, sondern auch ein Interface auf ein anderes "gecastet" werden kann.

b) Supports nix anderes macht, als letztlich GetInterface bzw GetInterfaceEntry aufzurufen, welches in diesem Fall keinen Eintrag für ein durch Interface Vererbung implizit unterstütztes Interface zurückliefert (wie ich schon in meinem Post von 2010 erwähnte).

;)

P.S. Hab sogar gerade gefunden, warum das so ist: http://edn.embarcadero.com/article/29779 (der erste Satz nach dem Codebeispiel und der letzte Paragraph)

Patito 20. Nov 2013 07:44

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Furtbichler (Beitrag 1236648)
Sollte 'GetInterface' das Problem nicht lösen?

Dessenungeachtet frage ich mich, was 'Supports' für einen Sinn macht, wenn man damit nicht herausfinden kann, ob eine Instanz ein bestimmtes Interface implementiert. Oder einfach ausgedrückt: Wozu dient 'Supports'?

Ein sinnvoller Anwendungsfall von Supports ist eben gerade der, dass man einen Workaround für die fehlende Unterstützung von Multiple Interface- Inheritance hat.

IReadWrite = interface(IRead, IWrite) geht nicht. Daher kann leider nur
IReadWrite = interface(IRead) oder IReadWrite = interface(IWrite) machen.

Hat man jetzt ein IReadWrite und braucht sowohl IRead als auch IWrite, muss man zur Laufzeit einen der Casts über Supports() machen.

Für COM-Interfaces braucht man ausserdem so ein QueryInterface() um aus einem fremden Pointer ein Interface zu machen.
Als Sprachfeature intern ist die Verwendung von Supports ansonsten aber eher schlechter Stil (so in etwa wie wenn man Klassen und Methoden nicht normal benutzt, sondern alles zur Laufzeit über RTTI und irgendwelche Strings zusammenwurstet).

Furtbichler 20. Nov 2013 07:53

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Stevie (Beitrag 1236649)
Wenn du das weißt, dann solltest du wissen,

Mein Wissen ist exklusiv durch diesen Thread entstanden und daher ist alles, was Du sonst schreibst Neuland für mich, welches ich dankbar annehme und durchlese.

Zitat:

P.S. Hab sogar gerade gefunden, warum das so ist: http://edn.embarcadero.com/article/29779 (der erste Satz nach dem Codebeispiel und der letzte Paragraph)
Der zweite Satz ist für mich einleuchtender. Blöde Sache, diese Altlasten.

@panino: Ich bekomme niemals heraus, ob meine Klasse/Interface ein Interface implementiert, das nicht direkt in der Klassendeklaration angegeben ist. Das ist blöd.

Es bleibt (für mich) dabei: 'Supports' ist nicht wirklich zu gebrauchen und wer diesbezüglich richtig programmieren will, muss zu .NET wechseln. Ich arbeite gerade damit und schüttle über die Restriktionen, die einem hier zwangsweise begegnen, nur den Kopf.

Der schöne Günther 20. Nov 2013 08:50

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von himitsu (Beitrag 1236638)
beide Interfaces angeben

Ich habe davon bislang immer Abstand genommen da ich mich entweder irre oder man dadurch wieder mit anderen Anomalien zu kämpfen hatte wenn man beispielsweise die Realisierung eines Interfaces an irgendwelche aggregierten Objekte delegieren wollte. Ich teste das noch einmal durch...

Zitat:

Zitat von Furtbichler (Beitrag 1236654)
Blöde Sache, diese Altlasten.

@panino: Ich bekomme niemals heraus, ob meine Klasse/Interface ein Interface implementiert, das nicht direkt in der Klassendeklaration angegeben ist. Das ist blöd.

[...] Restriktionen, die einem hier zwangsweise begegnen[...]

Da geht bei mir auch wirklich der Blutdruck hoch. Statt lustiger Spielereien wie 3D-Firemonkey oder VCL-Styles hätte ich gerne für Nicht-Legacy-Code wenigstens einen Compilerschalter "Sinnvolles Verhalten AN|AUS". Einen Schalter für Spektakel wie Pentium-sicheres FDIV hat man ja auch bekommen.

Uwe Raabe 20. Nov 2013 08:58

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von himitsu (Beitrag 1236638)
Zitat:

Zitat von Der schöne Günther (Beitrag 1236636)
Dagegen kann ich wirklich nichts tun?

Zitat:

Zitat von Uwe Raabe (Beitrag 1236634)
Wenn du willst, daß TChild sowohl IChild als auch IParent supported, dann musst du beide Interfaces angeben.

:?:

OK, vielleicht unklar ausgedrückt: supported = liefert bei
Delphi-Quellcode:
Supports
true.

Bezieht sich halt auf meinen Beispielcode im Beitrag #5 weiter oben. TClass2 deklariert nur IChild bei den unterstützen Interfaces und deshalb kann man IFather nicht über Supports abrufen. Will man das, muss man es so deklarieren wie bei TClass1.

stahli 7. Jun 2014 11:56

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Patito (Beitrag 1236653)
IReadWrite = interface(IRead, IWrite) geht nicht. Daher kann leider nur
IReadWrite = interface(IRead) oder IReadWrite = interface(IWrite) machen.

Ich will das gleich nochmal anprangern (weil ich gerade auf das Problem gestoßen bin).

Ist das in XE6 immer noch so?
Eine Mehrfachvererbung von Interfaces wäre wirklich sehr übersichtlich.

Dass Supports die Vererbungen nicht nachvollziehen kann und in den Klassen daher die einzelnen Interfaces angegeben werden müssen finde ich weniger schlimm.

Aber ein Interface von mehreren anderen abzuleiten wäre schon sehr hilfreich.

Sir Rufo 7. Jun 2014 12:13

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von stahli (Beitrag 1261668)
Zitat:

Zitat von Patito (Beitrag 1236653)
IReadWrite = interface(IRead, IWrite) geht nicht. Daher kann leider nur
IReadWrite = interface(IRead) oder IReadWrite = interface(IWrite) machen.

Ich will das gleich nochmal anprangern (weil ich gerade auf das Problem gestoßen bin).

Ist das in XE6 immer noch so?
Eine Mehrfachvererbung von Interfaces wäre wirklich sehr übersichtlich.

Dass Supports die Vererbungen nicht nachvollziehen kann und in den Klassen daher die einzelnen Interfaces angegeben werden müssen finde ich weniger schlimm.

Aber ein Interface von mehreren anderen abzuleiten wäre schon sehr hilfreich.

Dadurch gibt es doch nur heilloses Durcheinander und da eine Klasse mehrere Interfaces implementieren kann ist es auch überflüssig.

himitsu 7. Jun 2014 12:22

AW: Interfaces - Multiple Inheritance
 
Wozu willst du mehrere Interfaces vererben?

IRead und IWrite und dann wird eines von Beiden oder Beides bei den entsprechenden Klassen angegeben.

Sowas geht zwar auch
Delphi-Quellcode:
type
  IRead = interface
    function ReadInt: Integer;
  end;
  IWrite = interface
    procedure WriteInt(Value: Integer);
  end;
  IReadWrite = interface
    function ReadInt: Integer;
    procedure WriteInt(Value: Integer);
  end;
oder sowas
Delphi-Quellcode:
type
  IRead = interface
    function ReadInt: Integer;
  end;
  IWrite = interface
    procedure WriteInt(Value: Integer);
  end;
  IReadWrite = interface(IRead)
    procedure WriteInt(Value: Integer);
  end;
Aber da muß man dann bei den Funktionen, welche auf diese Interfaces prüfen, die Interfaces doppelt abfragen, da es je "Funktion" zwei Interfaces gibt.
Read = IRead oder IReadWrite
Write = IWrite oder IReadWrite

Sir Rufo 7. Jun 2014 12:35

AW: Interfaces - Multiple Inheritance
 
Zudem man bei der Klasse (wie schon mehrfach erwähnt) jedes Interface angeben muss.

Somit hilft einem dieses Multi-Erben auch nicht wirklich.

BTW Wenn man die implementierende Klasse nicht von
Delphi-Quellcode:
TInterfacedObject
ableiten möchte.
Delphi-Quellcode:
type
  IMyInterface = interface
  end;

  TMyInterfacedObject = class( TObject, IMyInterface )
    ...
  end;
dann kann man das auch nicht gegen das Basis-Interface
Delphi-Quellcode:
IInterface
casten.

stahli 7. Jun 2014 13:26

AW: Interfaces - Multiple Inheritance
 
Da muss ich offenbar noch etwas drauf rum denken...

Mit der Klassendefinition habe ich kein Problem.
Wenn ich die Objekte in einer Factory erzeugen lasse und nur mit den Interfaces weiter arbeite, dann erscheint mir eine Mehrfachvererbung doch sinnvoll.

Delphi-Quellcode:
var RW: IReadWrite;
    X: Integer;
...
RW := TFactory.GetNewRW; // erzeugt ein TReadWrite und gibt es als IReadWrite zurück
...
// folgendes ist möglich
RW.WriteInt(1);
X := RW.ReadInt;
// bzw. auch über Property
RW.Int := 1;
X := RW.Int;
In RW sind beide Funktionalitäten deklariert.

IReadWrite selbst könnte einfach so definiert sein:

Delphi-Quellcode:
IReadWrite = Interface(IRead, IWrite);
end;

Ohne diese Mehrfachvererbung muss man die Funktionen (mindestens zum Teil) in den Interfacedefinitionen doppelt schreiben.

Wenn man RW nicht als IReadWrite definiert sondern als "BasisInterface" muss man immer Supports verwenden und casten.


PS:
Gibt es denn irgendwo ein Delphi-Tutorial, wie man korrekt mit einer Factory und komplexen Schnittstellen arbeitet?
So ganz konkret habe ich dazu noch nichts gefunden. Vielleicht bin ich ja noch etwas auf dem Holzweg...
(Das Entwufsmusterbuch habe ich jetzt zwar erhalten, aber das bleibt ja auch noch etwas abstrakt und ich konnte mich bisher noch nicht länger damit befassen.)

Sir Rufo 7. Jun 2014 14:24

AW: Interfaces - Multiple Inheritance
 
Warum denn nicht einfach kreativ so
Delphi-Quellcode:
IRead = interface
  function Read : Integer;
end;

IWrite = interface
  procedure Write( Value : Integer );
end;

IReadWrite = Interface;
  function Reader : IRead;
  function Writer : IWrite;
end;

TReadWrite = class( TInterfacedObject, IRead, IWrite, IReadWrite )
private
  FValue : Integer;
public
  // IRead
  function Read : Integer;
  // IWrite
  procedure Write( Value : Integer );
  // IReadWrite
  function Reader : IRead;
  function Writer : IWrite;
end;

function TReadWrite.Read : Integer;
begin
  Result := FValue;
end;

procedure Write( Value : Integer );
begin
  FValue := Value;
end;

function Reader : IRead;
begin
  Result := Self;
end;

function Writer : IWrite;
begin
  Result := Self;
end;

sahimba 7. Jun 2014 14:56

AW: Interfaces - Multiple Inheritance
 
Nur als Einwurf, etwas OT aber auch nicht ganz. Interface-Vererbung, zumal mehrfache/tiefe Vererbung, halte ich so ganz aus dem Bauch heraus für mitunter fragwürdig. Oft endet es dann, dass man eine komplexe Hierarchie Interfaces definiert, welche dann fast 1:1 als Hierarchie von Klassen implementiert wird. Der Gedanke, ein Interface als eine Art "Service" zu sehen, den ein Objekt anbietet/unterstützt, fällt dabei vollkommen heraus. Die meisten Interfaces sollten wohl eher "schlank" sein.
Und jetzt lese ich mir mal durch, warum man in Delphi die implementierten Interfaces explizit angeben muss. Mich hat das schon mehr als einmal genervt...
Schöne Feiertage btw!

stahli 7. Jun 2014 16:00

AW: Interfaces - Multiple Inheritance
 
Das ist ein guter Einwand.

Wenn man aber die Instanziierung der Klassen (Erzeugen der Objekte) auslagert und fortan nur noch mit Interfaces arbeiten will, dann braucht man wiederum mächtige Interfaces, die die gesamte Funktionalität veröffentlichen.
Andernfalls müsste man immer prüfen, ob das vorliegende (Objekt-)Interface nun gerade zufällig IRead oder IWrite unterstützt, darauf casten und dann auf die Propertys zugreifen.

Andererseits bringt das natürlich auch wieder Vorteile, da man so sehr flexibel mit den Objekt-Interfaces umgehen kann.
Eigentlich bräuchte man dann IReadWrite gar nicht sondern würde einfach mit IInterface arbeiten:

Delphi-Quellcode:
var RW: IInterface;
     X: Integer;
 ...
 RW := TFactory.GetNewRW; // erzeugt ein TReadWrite und gibt es als IInterface zurück
 ...
 if Supports(RW, IWrite) then
   (RW as IWrite).WriteInt(1);
 if Supports(RW, IRead) then
   X := (RW as IRead).ReadInt;
// bzw. auch über Property
 if Supports(RW, IWrite) then
   RW.Int := 1;
 if Supports(RW, IRead) then
   X := (RW as IRead).Int;

Dann braucht man sich gar nicht mehr kümmern, was genau da für ein Objekt vorliegt (eigentlich ja der Sinn von Interfaces).
Wenn man sicher ist, was man übergibt kann man Supports ja notfalls weg lassen.

Alternativ könnte man natürlich noch Variablen wie R: IRead und W: IWrite einführen.

Auf jeden Fall könnte man auf mächtige Schnittstellen als Klassenkopien so verzichten.

Aktuell scheint mir der Ansatz eigentlich sinnvoll zu sein (kann sich aber wieder ändern ;-)).
Ginge das so?

Stevie 7. Jun 2014 16:19

AW: Interfaces - Multiple Inheritance
 
Wenn man sich an das SRP und das ISP hält, dann möchte man möglichst schlanke Interfaces. Und die Zusammenführung mehrerer Interfaces zu einem großen wäre wohl ein klarer Verstoß dessen.

Genau genommen ist schon allein das Supports abfragen des einen Interfaces auf das andere ein Code Smell, denn dadurch greift man von hinten rum auf etwas zu, was einem gar nicht übergeben wurde. Wenn ich möchte, dass nur lesender Zugriff erfolgt, übergeb ich das IRead Interface. Wenn sich nun jemand mit Supports mal ebend aus diesem Interface Schreibzugriff besorgt, ist das ziemlich bedenklich.

stahli 7. Jun 2014 16:45

AW: Interfaces - Multiple Inheritance
 
Ok, die ersten 2 Sätze kann ich nachvollziehen.

Aber das Supports stinkt nicht so ganz.
Wenn ich einer Funktion bestimmte Objekt-Interfaces (also quasi irgendwelche Objekte) übergebe und dann diverse Dinge damit tun will, dann muss ich doch wissen, was ich vor mir habe.

Z.B. kann ich prüfen, ob ISerialization unterstützt wird und das Objekt dann serialisieren.
Wenn IOrganic unterstützt wird kann ich DoDestroying(1) aufrufen usw.

Dass Supports schlecht sein soll, erschließt sich mir irgendwie nicht.


NACHTRAG: Wenn ich absichtlich ein allgemeines Interface übergebe und damit ausdrücklich unterschiedliche Aktionen ermöglichen will, müsste mein Ansatz doch korrekt sein?
Ich sehe ein, dass der Ansatz fragwürdig wäre, wenn tatsächlich nur IRead übergeben wird und das Objekt aber dennoch auseinander genommen würde (ähnlich wie mit der RTTI).

Stevie 7. Jun 2014 22:07

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von stahli (Beitrag 1261692)
Z.B. kann ich prüfen, ob ISerialization unterstützt wird und das Objekt dann serialisieren.
Wenn IOrganic unterstützt wird kann ich DoDestroying(1) aufrufen usw.

Dass Supports schlecht sein soll, erschließt sich mir irgendwie nicht.

In einem solchen Fall würdest du aber ein nicht referenzgezähltes Objekt übergeben und dort auf IWasAuchImmer überprüfen, um dann damit was zu machen und nicht ein bestimmtes Interface. Ich bezeichne solche Objekte mal als Datenobjekte. Sie könnten bestimmte Aspekte über Interfaces zur Verfügung stellen (wobei ich das auch nicht für das Gelbe vom Ei halte, dazu unten mehr).

Ich kann aber auch Interfaces als Services übergeben (im Gegensatz zu Datenobjekten sind das Klassen bzw Interfaces, die irgendwelche Arbeit erledigen) und da sollte man aufpassen, irgendwelche Annahmen darüber zu machen, welche Interfaces möglicherweise noch implementiert sind (Stichwort http://en.wikipedia.org/wiki/Leaky_abstraction). D.h. ich bekomm ein IFoo übergeben und versuche dann über Supports noch IBar zu bekommen um damit was zu machen. Erstens hab ich ein Problem, dass ich der API nicht ansehen kann dass man nicht nur IFoo braucht sondern möglicherweise auch noch IBar. Und zweitens haben ich somit eine implizite Abhängigkeit auf die Implementierung (wer stellt denn sicher, dass beide Interfaces von derselben Klasse implementiert werden).

Zu dem oben angesprochenen Punkt. Wenn Datenobjekte allerlei Interfaces implementieren, die dann irgendwas mit dem Objekt machen, handelt man sich sehr schnell Probleme mit dem SRP ein (das Objekt hält also nicht nur Daten, sondern Serialisiert sich, schreibt sich in die Datenbank, kann sich ausdrucken und vieles mehr). Zudem kommt es dann auch sehr schnell zu einer vermischung von verschiedenen Layern, die nix miteinander zu tun haben. Ich will damit nicht sagen, dass man das keineswegs machen sollte, aber man sollte aufpassen, welche Funktionalität man damit zur Verfügung stellt und inwieweit man seine Datenobjekte dann von anderen Schnittstellen abhängig macht.

himitsu 7. Jun 2014 22:33

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Sir Rufo (Beitrag 1261676)
Zudem man bei der Klasse (wie schon mehrfach erwähnt) jedes Interface angeben muss.

Drum meinte ich ja, daß man sonst alle drei Interfaces auswerten muß, da wo auf die Interfaces getestet wird.
Dann braucht man nur noch jeweils Eines der Drei anzugeben.

Uwe Raabe 8. Jun 2014 10:30

AW: Interfaces - Multiple Inheritance
 
Zitat:

Zitat von Stevie (Beitrag 1261697)
D.h. ich bekomm ein IFoo übergeben und versuche dann über Supports noch IBar zu bekommen um damit was zu machen. Erstens hab ich ein Problem, dass ich der API nicht ansehen kann dass man nicht nur IFoo braucht sondern möglicherweise auch noch IBar. Und zweitens haben ich somit eine implizite Abhängigkeit auf die Implementierung (wer stellt denn sicher, dass beide Interfaces von derselben Klasse implementiert werden).

Wobei das ja durch Supports nicht vorgeschrieben ist. Supports sagt ja nur, ob dieses Interface unterstützt wird und liefert gegebenenfalls einen Zeiger darauf zurück. Nirgendwo steht, daß dieses Interface von derselben Klasse implementiert sein muss wie das Ausgangs-Interface. Immerhin ist Supports die einzige Möglichkeit, sinnvoll mit einer polymorphen IInterfaceList zu arbeiten. Hinter jedem IInterface dieser Liste kann ein ganzer Schwanz von Klasseninstanzen stecken, die für die verschiedenen Interfaces zuständig sind, die man gerade abfragt, und wo die Instanzen bis zur Abfrage vielleicht noch gar nicht erzeugt sein müssen.

Mehr noch - bei passender Implementierung (Stichwort: TAggregatedObject und implements) muss die effektiv IFoo-implementierende Klasse ja gar nichts über IBar wissen. Es kommt doch am Ende nur darauf an, welches QueryInterface bei Supports aufgerufen wird.

Natürlich könnte man um von IFoo auf IBar zu kommen in IFoo auch eine Funktion einbauen, die das IBar zurückgibt, aber damit hat man wiederum eine Abhängigkeit von IFoo zu IBar geschaffen. Auch die Verlagerung in ein Über-Interface, das sowohl IFoo als auch IBar als Funktionen bereitstellt, ist m.E. nicht gerade erstrebenswert. Der Weg über Interface-Aggregation liefert im Endeffekt das gleiche, die Verwendung von Supports finde ich aber deutlich übersichtlicher. Ob ich dann nicht doch lieber beide Interfaces in einer Klasse implementiere kann dem aufrufenden Code dann aber egal sein.

Stevie 8. Jun 2014 12:22

AW: Interfaces - Multiple Inheritance
 
Es geht darum, dass es sich um eine implizite Annahme handelt, dass man wie auch immer von IFoo an ein IBar kommen kann, was ein Implementierungsdetail ist.

Wenn man sich mal durchliest, wo das ISP seinen Usprung hat dann dürfte einem schnell klar werden, wie gefährlich so Annahmen sein können, wenn es darum geht, entkoppelten Code zu schreiben. Wenn ich mir nämlich in meinem Code der eigtl nur ein IStapleJob Interface bekommt mit Supports mal ebend die Funktionalitäten von IPrintJob hole und damit was mache, dann ist das erstens nicht aus der API ersichtlich ("wieso funktioniert das nicht, ich hab doch ein IStapleJob reingegeben" oder auch "häh, wieso druckt der nun, ich wollt doch nur zusammenheften") und zweitens erschwere ich mir mal wieder das Testen. Ich kann nicht einfach ein IStapleJob Mock basteln und ihn hineingeben, nein ich muss auch noch dafür sorgen, dass mein Mock auch IPrintJob supportet.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:02 Uhr.

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