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?