Thema: Delphi Umgang mit Interfaces

Einzelnen Beitrag anzeigen

Whookie

Registriert seit: 3. Mai 2006
Ort: Graz
441 Beiträge
 
Delphi 10.3 Rio
 
#1

Umgang mit Interfaces

  Alt 5. Dez 2013, 16:04
Hallo!
Ich habe diesmal eine grundsätzliche Frage zum Umgang mit Interfaces, weil ich gerade eine entsprechendes Projekt erstelle.

Gedacht habe ich mir das so: Es ibt eine Fülle von "kleinen" Interfaces die selber auch eine entsprechende Hierarchie haben (Details wie CC und GUID weggelassen):

Delphi-Quellcode:
IBase=Interface(IUnknown)
  function GetCanSave: Boolean;
end;

IValue=Interface(IBase)
  function GetIsNaN: Boolean;
End;

IInteger=Interface(IValue)
  function GetValue: Integer;
  procedure SetValue(const AValue: Integer);
End;

ICompare=Interface(IBase)
  function Compare: Boolean;
End;

ISomeThing=Interface(IBase)
  function DoSomeThing: Integer;
End;
Aus den Intefaces baue ich mir dann verschiedene Klassen wie zum Beispiel (ohne Code):

Delphi-Quellcode:
TMyClasses = Class(TInterfacedObject)
End;

TMyBase = Class(TMyClasses, IBase)
End;

TMyInteger = Class(TMyBase, IInteger)
End;

TMyCompare = Class(TMyBase, ICompare)
End;

TMyDoSomething = Class(TMyCompare, IInteger, ISomeThing)
  fAValue: TMyClasses;
End;
Alle diese Klassen werden nun in einer List verwaltet:

Delphi-Quellcode:
TListData =Class
  fMy: TMyClasses;
  fName: String;
  fHash: Cardinal;
End;
TMyList = Class
  fList: TObjectList<TListData>
End;
Das eigentliche Problem habe ich nun aber beim Arbeiten mit der TMyList. Sie ist mit meinen (polymorphen) Objekten befüllt und nun geht es darum möglichst elegant auf die einzelnen Methoden zugreifen zu können.


Was funktioniert ist natürlich alle möglichen Klassen abzufragen und zu casten:

Delphi-Quellcode:
  If fList[i].fMy Is TMyDoSomething Then
  begin
    TMyDoSomething(fList[i].fMy]).SetValue(1);
    TMyDoSomething(fList[i].fMy]).DoSomething;
    If TMyDoSomething(fList[i].fMy]).fAValue Is TMyInteger Then
    begin
      TMyInteger(TMyDoSomething(fList[i].fMy]).fAValue).SetValue(2);
    end;
  end
  else if (fList[i].fMy Is TMyInteger) Then
  begin
    TMyInteger(fList[i].fMy).SetValue(1);
  end;
Das ist nicht besonders schön vor allem auch weil ich dann gerne SetValue unabhängig von der Klasse aufrufen möchte.

Ein Zugriff über das Interface führt aber immer zu Schutzverletzungen (wegen der Referenzzählung) und wird in einem komplexeren System wohl schnell unüberschaubar:

Delphi-Quellcode:
  If Supports(fList[i].fMy, IID_IInteger) then
  begin
    (fList[i].fMy As IInteger).SetValue(1);
  end;
Ich habe auch probiert die Referenzzählung "positiv" zu beeinflussen (_AddRef/_Release selber aufrufen) aber der Verwaltungsaufwand ist entsprechend groß und fehleranfällig wird das ganze auch.

Daher natürlich die Frage, wie kann man sowas besser organisieren? Ich dachte auch schon daran eine funktion in TMyClasses einzubauen die ein Interface liefert:

Delphi-Quellcode:
  If fList[i].fMy.GetInterface(IID_xx, AIntegerIntf) Then
    AIntegerIntf.SetValue(1);
aber da schlägt die Referenzzählung auch zu (und kapselt eigentlich nur den Supports-Aufruf).

Jedes einzelne Interface in eine Klasse zu packen und dann mit implements in den TMyXXX-Klassen als Delegate einzuhängen ist ein großer Aufwand und nimmt die Flexibilität der Interfaces wieder heraus.

Was wäre der beste Ansatz sowas möglichst transparent und sauber zu lösen?
Whookie

Software isn't released ... it is allowed to escape!

Geändert von SirThornberry ( 6. Dez 2013 um 09:51 Uhr) Grund: Code-Tags durch Delphi-Tags ersetzt - nächstes mal bitte bei Delphi-Code die delphi-tags verwenden
  Mit Zitat antworten Zitat