I would like to add some comments to your current solution by answering here, as this cannot really be done in the comments section:
type IComponentFrobber = interface end; TComponentFrobberClass = class of TComponentFrobber; TComponentFrobber = class(TInterfacedObject, IComponentFrobber) strict private FComponent: TComponent; protected constructor Create(AComponent: TComponent); property Component: TComponent read FComponent; public class function FindFrobberClass(AComponentClass: TComponentClass): TComponentFrobberClass; overload; static; class function FindFrobberClass(AComponent: TComponent): TComponentFrobberClass; overload; static; class procedure RegisterFrobber(AComponentClass: TComponentClass; AFrobberClass: TComponentFrobberClass); static; end;
It makes no sense to use TInterfacedObject for the base class, since you always need an object, not an interface that it implements - how else would you find your specific Frobber class? I would split it into TComponentFrobber, go down from TInterfacedObject and the TComponentRegistry class (descent from TObject) which has class methods. You can, of course, make the registry class more general, it is not tied to TComponentFrobber and can be reused.
Edit: I used similar class registries, for example, when downloading files: load the identifier for the next object (maybe, for example, string, integer or GUID), and then get the correct class to create an instance from the registry, then create and load the object.
type TComponentFrobberRegistryItem = record ComponentClass: TComponentClass; FrobberClass: TComponentFrobberClass; end; var FComponentFrobberRegistry: array of TComponentFrobberRegistryItem;
This is normal if you never add or remove classes from the registry, but usually I would not use an array, but a list for registry entries.
class function TComponentFrobber.FindFrobberClass(AComponentClass: TComponentClass): TComponentFrobberClass; var i: Integer; begin // Search backwards, so that more specialized frobbers are found first: for i := High(FComponentFrobberRegistry) downto Low(FComponentFrobberRegistry) do if FComponentFrobberRegistry[i].ComponentClass = AComponentClass then begin Result := FComponentFrobberRegistry[i].FrobberClass; Exit; end; Result := nil; end;
Searching back in the array will not help find the most specialized frobber unless you add them in the correct order (the least specialized first). Why don't you check if ClassType is equal? There is also a ClassParent to traverse the class hierarchy if you need to check the base classes as well.