Auch wenn es nicht gerade das Schönste ist was ich je getan habe.
... zeigen!
Das ist gar nicht so einfach und ohne ganz großen Kontext sieht es wahrscheinlich noch schlimmer aus :X
Ich versuche es mal zu erklären. Grob gesagt:
Es gibt eine abstrakte Basisklasse "TSearchProvider" mit einer Methode Search() und einer Liste von Methodenzeigern (TProcessSearchResult= procedure(SearchResult: TSearchResultObject) of object) die Funktionen auf die Daten bereitstellen die der SearchProvider findet.
Nun gibt es ein "TSearchEngine" (Singleton) bei der TSearchProvider-Ableitungen (Instanzen dieser Ableitungen) registriert werden können.
Im Programm selbst wird nur SearchEngine.Search('Suchbegriff') aufgerufen was daraufhin intern das Search('Suchbegriff') aller registrierten TSearchProvider-Objekte aufruft.
TSearchEngine gruppiert die Suchergebnisse nach Providern. D.h. SearchEngine.Search gibt eine Liste mit Listen der Suchergebnisse der einzelnen Searchprovider zurück. Diese Listen (in der großen Liste) besitzen eine Referenz auf den Searchprovider der diese Liste "beigetragen" hat.
Derjenige der nun SearchEngine.Search aufruft hat nun quasi sowas wie eine TGroupedResultList<TSearchProviderResultList<TSear chResultObject>> mit den Ergebnissen (wie gesagt: pro Searchprovider eine Liste).
Der Aufrufer stellt nun die Ergebnisse in einem Grid dar und möchte dem Endbenutzer nun die Möglichkeit geben Operationen auf den Suchergebnissen auszuführen (per Popupmenu z.B.). Die SearchEngine hat natürlich keine Ahnung was für Daten sie da ausgespuckt hat.
ALLERDINGS gibt es da die Liste der Operationen die die einzelnen SearchProvider bereitstellen (können). TSearchProviderResultList beinhaltet eine Referenz auf den Provider der wiederrum die Liste mit Methoden inkl. Caption für die Methoden bereitstellt.
Nun kann der Aufrufer beim Popup des Popupmenus einfach dynamisch das Popupmenu mit den Aktionen füllen die im zum ausgewählten Suchergebnis zugehörigen Searchprovider enthalten sind.
Nun kann es aber sein dass nicht für alle Suchergebnisse des selben Providers alle Operationen zur Verfügung stehen sollen (z.B. ein SearchProvider für Adressen mit der Operation "Bestellung schreiben", die natürlich nur bei Lieferanten und nicht bei Kunden angezeigt werden soll).
Soweit so toll. Falls noch jemand am lesen ist: Nun zum eigentlichen Problem:
Ich dachte damit derjenige der von TSearchProvider ableitet es relativ bequem hat erlaube ich einfach sowas:
Delphi-Quellcode:
TAdresseSearchProvider = class(TSearchProvider)
public
// Search() für Adressen überschreiben + anderer Kram ...
// Operationen
[TSearchResultOperation('Bestellung schreiben',
function(AData: TSearchResultObject): Boolean
begin
Result := TAdressResult(AData).IstLieferant;
end)
]
procedure BestellungSchreiben(AData: TSearchResultObject);
end;
Im constructor des abstrakten TSearchproviders würde ich dann per
RTTI die Methoden durchlaufen und nach Methoden mit dieser Signatur + dem TSearchResultOperation-Attribut suchen und diese automatisch zu der Operationen-Liste hinzufügen mit dem im Attibut angegebenen Namen und anonymen Check-Procedure.
Somit muss derjenige der Ableitet sich um das hinzufügen zur Operationen-Liste nicht kümmern.
Und beim Aufbau des PopupMenüs:
Delphi-Quellcode:
for i:= 0 to SelSearchResultObject.Provider.Operations.Count-1 do
begin
// ACHTUNG: Starker Pseudocode:
if SelSearchResultObject.Provider.Operations[i].CanUse(SelSearchResultObject) then // <-- anonyme Methode aus dem Attribut
PopupMenu.AddItem(SelSearchResultObject.Provider.Operations[i].Name, SelSearchResultObject.Provider.Operations[i].Code);
end;
Da das mit den anonymen Methoden in den Attributen nicht geht habe ich es so gemacht dass TSearchProvider beim Durchsuchen der Methoden nachdem es eine Methode mit TSearchResultOperation-Attribut gefunden hat nach einer Methode sucht die genauso heißt + 'Check':
oeprationCheckMethod := rttiType.GetMethod(operationMethod.Name + 'Check');
Und diese wird dann in Operation.CanUse eingetragen.
Der SearchProvider sieht dann so aus:
Delphi-Quellcode:
TAdresseSearchProvider = class(TSearchProvider)
public
// Search() für Adressen überschreiben + anderer Kram ...
// Operationen
[TSearchResultOperation('Bestellung schreiben']
procedure BestellungSchreiben(AData: TSearchResultObject);
function BestellungSchreibenCheck(AData: TSearchResultObject): Boolean;
end;
Und das ist nicht so schön.
Und Daniel: Ich hoffe du hast auch brav alles gelesen, du wolltest es ja wissen
(Ich hoffe man konnte es halbwegs verstehen
)