I am trying to figure out how to write a generic factory in XE2. Lets say I have this:
type
TObjectTypes = (otLogger, otEmail);
type
TLoggerTypes = (lFile, lConsole, lDatabase);
type
TEmailTypes = (etPOP3, etSMTP);
Classes:
TSMTPEmail = class(TInterfacedObject, IEmail); // Supports emailing only
TPOP3Email = class(TInterfacedObject, IEmail); // Supports emailing only
TFileLogger = class(TInterfacedObject, ILogger); // Supports logging only
etc.
Now I do this to loop thru all TObjectTypes:
procedure TForm1.FormCreate(Sender: TObject);
var
_Interf: IInterface;
_Configuration: TDictionary<string, TValue>;
_ObjectType: TObjectTypes;
begin
_Configuration := nil;
_Configuration := TDictionary<string, TValue>.Create;
try
_Configuration.Add('FileLogFileName', '20160320.Log');
_Configuration.Add('SMTPEmailHost', 'mail.server.lt');
_Configuration.Add('POP3Server', 'some_server');
for _ObjectType := Low(TObjectTypes) to High(TObjectTypes) do
begin
_Interf := TTheFactory.Make(_ObjectType, _Configuration);
if Assigned(_Interf) then
begin
OutputDebugString(PWideChar((_Interf as TObject).ClassName));
if Supports(_Interf, IEmail) then
(_Interf as IEmail).Send('X');
if Supports(_Interf, ILogger) then
(_Interf as ILogger).GetLastErrorMsg;
end;
end;
finally
FreeAndNil(_Configuration);
end;
end;
So, I need a generic factory and be able to loop not thru all TObjectTypes, but thru all TLoggerTypes or thru all TEmailTypes and skip creating some e.g. lDatabase from TLoggerTypes or etPOP3 from TEmailTypes.
Factory should produce all kind of classes.
In Delphi making factories is pretty simple, thanks to metaclasses (class references), simple example of which is TClass:
TClass = class of TObject
In most cases, you should define your own abstract class for all factory members and metaclass for it:
TMyFactoryObject = class (TObject)
public
constructor FactoryCreate(aConfiguration: TConfiguration); virtual; abstract;
end;
TMyFactoryClass = class of TMyFactoryObject;
In this abstract class you can add some methods common for all descendants, in my example we have constructor which takes configuration as argument. How to react to it will be determined in descendants.
Then you declare descendant classes:
TMyLogger = class (TMyFactoryObject, ILogger)
private
...
public
constructor FactoryCreate(aConfiguration: TConfiguration); override;
... //implementation of ILogger interface etc
end;
TMyEmail = class (TMyFactoryObject, IEmail)
private
...
public
constructor FactoryCreate(aConfiguration: TConfiguration); override;
... //implementation of IEmail interface etc
end;
now you declare array of possible descendant classes:
var
MyFactory: array [otLogger..otEmail] of TMyFactoryClass;
and in initialization section or in other places you populate this array:
MyFactory[otLogger]:=TMyLogger;
MyFactory[orEmail]:=TMyEmail;
At last, TTheFactory.Make(_ObjectType, _Configuration); from your question can be replaced with:
MyFactory[_ObjectType].FactoryCreate(_Configuration);
and you'll get needed object as instance of type MyFactoryObject.
See http://docwiki.embarcadero.com/RADStudio/Seattle/en/Class_References for more information.
Related
I'm still a bit fuzzy with generics in Delphi, but have been using TObjectList<> quite widely. Now I have a situation where I have a base class with such a private field, but needs to be created for an arbitrary class, also inherited from another base.
To clarify, I have two base classes:
type
TItem = class;
TItems = class;
TItemClass = class of TItem;
TItem = class(TPersistent)
private
FSomeStuffForAllIneritedClasses: TSomeStuff;
end;
TItems = class(TPersistent)
private
FItems: TObjectList<TItem>;
FItemClass: TItemClass;
public
constructor Create(AItemClass: TItemClass);
destructor Destroy; override;
function Add: TItem;
...
end;
This pair of classes is then further inherited into more specific classes. I'd like the object list to be shared for all of them, while each holds actually a different type internally.
type
TSomeItem = class(TItem)
private
FSomeOtherStuff: TSomeOtherStuff;
...
end;
TSomeItems = class(TItems)
public
function Add: TSomeItem; //Calls inherited, similar to a TCollection
procedure DoSomethingOnlyThisClassShouldDo;
...
end;
Now the problem is when it comes to creating the actual object list. I'm trying to do it like this:
constructor TItems.Create(AItemClass: TItemClass);
begin
inherited Create;
FItemClass:= AItemClass;
FItems:= TObjectList<AItemClass>.Create(True);
end;
However, the code insight complains about this:
Undeclared Identifier AItemClass
Even more, the compiler has yet a different complaint:
Undeclared Identifier TObjectList
Where, I do in fact have System.Generics.Collections used in this unit.
What am I doing wrong here, and how should I do this instead?
Make TItems generic:
TItems<T: TItem, constructor> = class(TPersistent)
private
FItems: TObjectList<T>;
public
constructor Create;
destructor Destroy; override;
function Add: T;
...
end;
constructor TItems.Create;
begin
inherited Create;
FItems:= TObjectList<T>.Create(True);
end;
function TItems<T>.Add: T;
begin
Result := T.Create;
FItems.Add(Result);
end;
If you inherit, simply put the correct generic parameter:
TSomeItems = class(TItems<TSomeItem>)
public
procedure DoSomethingOnlyThisClassShouldDo;
...
end;
The TObjectList is not meant to be used in that manner. The fact that it was originally defined as TObjectList<TItem> means that it will expect you to create it this way as well. It needs to be defined with the precise class you intend to create it as.
Instead, just create it with TItem, and then whenever you create a new item which is supposed to be added to this list, then you create it using the class type. Any time you need to access the items in this list, just cast them on the fly.
For example...
Result:= FItemClass.Create;
FItems.Add(Result);
...can be the contents of your Add function.
I have a doubt that I cannot solve. I have read the documentation at embarcadero for class constructors but I cannot understand the meaning of that. In other words, what is the usage difference between a constructor and a class constructor? I did this:
type
TGeneric<T> = class
private
FValue: T;
aboutString: string;
procedure setValue(const a: T);
public
property V: T read FValue write setValue;
property about: string read aboutString;
constructor Create;
destructor Destroy; override;
end;
implementation
{ TGeneric<T> }
constructor TGeneric<T>.Create;
begin
inherited;
aboutString := 'test';
end;
Instead this code is not working properly:
type
TGeneric<T> = class
private
FValue: T;
aboutString: string;
procedure setValue(const a: T);
public
property V: T read FValue write setValue;
property about: string read aboutString;
class constructor Create;
destructor Destroy; override;
end;
implementation
{ TGeneric<T> }
class constructor TGeneric<T>.Create;
begin
inherited;
aboutString := 'test';
end;
I guess that the answer is in this line of the documentation:
Normally, class constructors are used to initialize the static fields
of the class or to perform a type of initialization, which is required
before the class or any class instance can function properly.
Tell me if I am correct:
Constructor: I can use the inherited Create;, initialize variables and so on.
Class constructor: I can use this when I have to create an object in my class immediately?
For example look at below:
type
TBox = class
private
class var FList: TList<Integer>;
class constructor Create;
end;
implementation
class constructor TBox.Create;
begin
{ Initialize the static FList member }
FList := TList<Integer>.Create();
end;
end.
Here I am going to create the object immediately when I call TBox.Create in the main form?
A class constructor executes exactly once, when the unit in which it is declared is initialized. A class constructor is a static class method, and so Self is not defined.
A constructor executes when explicitly called and has the job of initializing an instance of a class.
In the wild, class constructors are rare, constructors are as common as muck. Quite likely you have no immediate need for class constructors so feel free to ignore them for now. Concentrate on understanding how to write and use constructors.
If in the future you ever need to initialize variables owned by the class (as opposed to owned by an instance) then you might find yourself wanting to use a class constructor. Until that point in time, do feel free to ignore them.
In addition to my pre-posters, it should be mentioned that in your example, the class constructor of a generic class is being called each time you instantiate (reference) a genric specialization:
TGenClass<T: class> = class
public
class constructor Create;
class destructor Destroy;
end;
class constructor TGenClass<T>.Create;
begin
Writeln('TGenClass<', GetTypeName(TypeInfo(T)), '> created');
end;
class constructor TGenClass<T>.Create;
begin
Writeln('TGenClass<', GetTypeName(TypeInfo(T)), '> destroyed');
end;
initialization
RegisterClass(TGenClass<String>);
RegisterClass(TGenClass<Integer>);
The output will be:
TGenClass<String> created
TGenClass<Integer> created
TGenClass<String> destroyed
TGenClass<Integer> destroyed
Note however, that the class construction/destruction order does not necessarily have to match the declaration or reference order at all!
I'm doing a full rewrite of an old library, and I'm not sure how to handle this situation (for the sake of being understood, all hail the bike analogy):
I have the following classes:
TBike - the bike itself
TBikeWheel - one of the bike's wheel
TBikeWheelFront and TBikeWheelBack, both inherits from TBikeWheel and then implements the specific stuff they need on top of it
This is pretty straightforward, but now I decide to create multiple kind of bikes, each bikes having it's own kinds of wheel - they do the same stuff as a regular front/back wheels, plus the specific for that bike.
TBikeXYZ - inherits from TBike
TBikeWheelXYZ - inherits from TBikeWheel
And here is my problem: TBikeWheelFrontXYZ should inherit from TBikeWheelXYZ (to get the specific methods of an XYZ wheel), but it should also inherit from TBikeWheelFront (to get the specific methods of a front wheel).
My question here is, how can I implement that in a way that doesn't:
feel like a hack
force me to rewrite the same code several time
Delphi does not support Multiple Inheritance. But classes can support / implement multiple interfaces and you can delegate interface implementation, so you can kinda simulate multiple inheritence.
Use interfaces. Something like this (Off the top of my head, based on your description.....)
type
IBikeWheel = interface
...
end;
IXYZ = interface
...
end;
IFrontWheel = interface(IBikeWheel)
...
end;
TBike = class
...
end;
TBikeWheel = class(TObject, IBikeWheel);
TBikeWheelXYZ = class(TBikeWheel, IXYZ);
TBikeFrontWheelXYZ = class(TBikeWheelXYZ, IFrontWheel);
Then implement classes for the interfaces that do what the corresponding classes in your old (presumably C/C++) library does and instantiate them in the corresponding class's constructor.
Use polymorhism to implment each 'thing' as an object hierarchy in its own right and then add object properties to that object in turn. So, create a hierarchy of wheels, and a hierarchy of bikes. Then add wheels to bikes as fields in the ancestor bike object. See below.
TBikeWheel = class
TBikeWheelXYZ = class( TBikeWheel )
TBike = class
FFrontWheel : TBikeWheel;
property FrontWheel : TBikeWheel
read FrontWhell
TBikeABC = class( TBike)
constructor Create;
end;
constructor TBikeABC.Create;
begin
inherited;
FFrontWheel := TBikeWheel.Create;
end;
TBikeXYZ = class( TBike)
constructor Create;
end;
constructor TBikeXYZ.Create;
begin
inherited;
FFrontWheel := TBikeWheelXYZ.Create;
end;
A variation of Brian Frost's suggestion:
TBikeWheel = class
TBikeWheelXYZ = class( TBikeWheel )
TBike = class
FFrontWheel : TBikeWheel;
protected
function CreateWheel: TBikeWheel; virtual;
public
property FrontWheel : TBikeWheel
read FrontWheel
end;
TBikeABC = class( TBike)
protected
function CreateWheel: TBikeWheel; override;
end;
function TBikeABC.CreateWheel: TBikeWheel;
begin
result := TBikeWheel.Create;
end;
TBikeXYZ = class( TBike)
protected
function CreateWheel: TBikeWheel; override;
end;
function TBikeXYZ.CreateWheel: TBikeWheel;
begin
result := TBikeWheelXYZ.Create;
end;
Basically - you CAN'T. Delphi does not support multiple inheritance.
So left with that dilemma, the question is: could you possibly refactor that library in such a way that you can get away with using interface? Is the multiple inheritance mostly about functions and methods? If so - use interfaces. Delphi can support multiple interfaces on a class.
If the multi-inheritance is more about inheriting actual functionality in the classes, then you're probably looking at a bigger scale refactoring, I'm afraid. You'll need to find a way to break up those functional dependencies in such a way you can make it inherit from a single base class, possibly with some additional interfaces thrown in.
Sorry I can't provide an easy answer - that's just the reality of it.
Marc
You can try to extract an interface, say IFrontWheel, out of TBikeWheelFront, so that it is a subclass of TBikeWheel but implements IFrontWheel. Then TBikeWheelXYZ inherits from TBikeWheel and TBikeWheelFrontXYZ inherits from TBikeWheelXYZ and implements IFrontWheel.
Then you can define a class TFrontwheel and give it the same methods as the interface, but now you implement them. Then TBikeWheelFront and TBikeWheelXYZ get a private member of type TFrontwheel and the IFrontWheel implementations of them simply delegate to the private member methods.
This way you don't have double implementations.
Another alternative with newer versions of Delphi is to leverage generics in a compositional model. This is particularly useful in the case where the multiple base classes (TBarA and TBarB in this example) are not accessible for modification (ie: framework or library classes). For example (note, the necessary destructor in TFoo<T> is omitted here for brevity) :
program Project1;
uses SysUtils;
{$APPTYPE CONSOLE}
type
TFooAncestor = class
procedure HiThere; virtual; abstract;
end;
TBarA = class(TFooAncestor)
procedure HiThere; override;
end;
TBarB = class(TFooAncestor)
procedure HiThere; override;
end;
TFoo<T: TFooAncestor, constructor> = class
private
FFooAncestor: T;
public
constructor Create;
property SomeBar : T read FFooAncestor write FFooAncestor;
end;
procedure TBarA.HiThere;
begin
WriteLn('Hi from A');
end;
procedure TBarB.HiThere;
begin
WriteLn('Hi from B');
end;
constructor TFoo<T>.Create;
begin
inherited;
FFooAncestor := T.Create;
end;
var
FooA : TFoo<TBarA>;
FooB : TFoo<TBarB>;
begin
FooA := TFoo<TBarA>.Create;
FooB := TFoo<TBarB>.Create;
FooA.SomeBar.HiThere;
FooB.SomeBar.HiThere;
ReadLn;
end.
you can try this way, if you do not want to repeat the code several times and want a decoupled code.
type
TForm1 = class(TForm)
btnTest: TButton;
procedure btnTestClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TBike = class
end;
IBikeWheel = interface
procedure DoBikeWheel;
end;
TBikeWheel = class(TInterfacedObject, IBikeWheel)
public
procedure DoBikeWheel;
end;
IBikeWheelFront = interface
procedure DoBikeWheelFront;
end;
TBikeWheelFront = class(TInterfacedObject, IBikeWheelFront)
public
procedure DoBikeWheelFront;
end;
IBikeWheelBack = interface
end;
TBikeWheelBack = class(TInterfacedObject, IBikeWheelBack)
end;
TBikeWheelFrontXYZ = class(TInterfacedObject, IBikeWheel, IBikeWheelFront)
private
FIBikeWheel: IBikeWheel;
FBikeWheelFront: IBikeWheelFront;
public
constructor Create();
property BikeWheel: IBikeWheel read FIBikeWheel implements IBikeWheel;
property BikeWheelFront: IBikeWheelFront read FBikeWheelFront implements IBikeWheelFront;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TBikeWheel }
procedure TBikeWheel.DoBikeWheel;
begin
ShowMessage('TBikeWheel.DoBikeWheel');
end;
{ TBikeWheelFrontXYZ }
constructor TBikeWheelFrontXYZ.Create;
begin
inherited Create;
Self.FIBikeWheel := TBikeWheel.Create;
Self.FBikeWheelFront := TBikeWheelFront.Create;
end;
{ TBikeWheelFront }
procedure TBikeWheelFront.DoBikeWheelFront;
begin
ShowMessage('TBikeWheelFront.DoBikeWheelFront');
end;
procedure TForm1.btnTestClick(Sender: TObject);
var
bikeWhell: TBikeWheelFrontXYZ;
begin
bikeWhell := nil;
try
try
bikeWhell := TBikeWheelFrontXYZ.Create;
IBikeWheelFront(bikeWhell).DoBikeWheelFront;
IBikeWheel(bikeWhell).DoBikeWheel;
except
on E: Exception do
begin
raise;
end;
end;
finally
if Assigned(bikeWhell) then FreeAndNil(bikeWhell);
end;
end;
Sorry, Delphi does not support Multiple Inheritance.
I would like to suggest the following steps:
Inherit the TBikeWheelFrontXYZ class from either TBikeWheelXYZ or TBikeWheelFront (since in Delphi multiple inheritance is impossible as mentioned in the answers above).
Convert one of the parent classes TBikeWheelXYZ or TBikeWheelFront to class helper for the TBikeWheel class.
Add the class helper unit to the unit, where the TBikeWheelFrontXYZ class is declared.
Can you have a class function that creates an instance of a class:
TMyClass = class(TSomeParent)
public
class function New(AValue : integer) : TMyClass;
end;
TDerivedClass = class(TMyClass)
public
function Beep;
end;
and then use it as follows
...
var
myList : TList<T>;
item : TDerivedClass;
begin
myList.Add(TDerivedClass.New(1))
myList.Add(TDerivedClass.New(3))
myList.Add(TDerivedClass.New(5))
for item in myList do
item.Beep; //times the count in the class function
...
And if so, what does that function code look like? Do you use TObject's NewInstance method and do you re-implement every-time for every derived class? Is it saver/better to use the Constructor?
The goal is to use this approach in a command pattern and load the command list with class types and a receiver e.g:
//FYI: document is an instance of TDocument
commandList.Execute(TOpenDocument(document));
commandList.Execute(TPasteFromClipboard(document));
//... lots of actions - some can undo
commandList.Execute(TPrintDocument(document));
commandList.Execute(TSaveDocument(document));
And the reason for this is that some commands will be specified via text/script and will need to be resolved at runtime.
What you're looking for is called the factory pattern. It can be done in Delphi; it's how the VCL deserializes forms, among other things. What you're missing is the registration/lookup part of the system. Here's the basic idea:
Somewhere, you set up a registration table. If you're on Delphi XE, you can implement this as a TDictionary<string, TMyClassType>, where TMyClassType is defined as class of TMyClass. This is important. You need a map between class names and class type references.
Put a virtual constructor on TMyClass. Everything that descends from it will use this constructor, or an override of it, when the factory pattern creates it.
When you create a new descendant class, have it call a method that will register itself with the registration table. This should happen at program startup, either in initialization or in a class constructor.
When you need to instantiate something from a script, do it like this:
class function TMyClass.New(clsname: string; [other params]): TMyClass;
begin
result := RegistrationTable[clsName].Create(other params);
end;
You use the registration table to get the class reference from the class name, and call the virtual constructor on the class reference to get the right type of object out of it.
Yes, it is technically possible to create an instance from a class method, simply call the actual constructor and then return the instance it creates, eg:
type
TMyClass = class(TSomeParent)
public
constructor Create(AValue : Integer); virtual;
class function New(AValue : integer) : TMyClass;
end;
TDerivedClass = class(TMyClass)
public
constructor Create(AValue : Integer); override;
function Beep;
end;
constructor TMyClass.Create(AValue : Integer);
begin
inherited Create;
...
end;
function TMyClass.New(AValue : integer) : TMyClass;
begin
Result := Create(AValue);
end;
constructor TDerivedClass.Create(AValue : Integer);
begin
inherited Create(AValue);
...
end;
var
myList : TList<TMyClass>;
item : TMyClass;
begin
myList.Add(TDerivedClass.New(1))
myList.Add(TDerivedClass.New(3))
myList.Add(TDerivedClass.New(5))
for item in myList do
TDerivedClass(item).Beep;
In which case, you are better off just using the constructor directly:
type
TMyClass = class(TSomeParent)
end;
TDerivedClass = class(TMyClass)
public
constructor Create(AValue : Integer);
function Beep;
end;
var
myList : TList<TDerivedClass>;
item : TDerivedClass;
begin
myList.Add(TDerivedClass.Create(1))
myList.Add(TDerivedClass.Create(3))
myList.Add(TDerivedClass.Create(5))
for item in myList do
item.Beep;
Can you have a class function that creates an instance of a class.
Is it saver/better to use the Constructor?
Constructor is a class function that creates an instance of class.
Just put:
constructor New(); virtual;
And you are good to go.
The virtual; part will let you call same New() constructor for all descendant classes.
Another option is to use RTTI. The code below runs as a normal method in my class as a way to get a new instance of the object with a subset of items, but as the items (along with the list object itself) are probably of descendent objects, creating an instance of the object in which the method is defined isn't good enough as it needs to be of the same type of the instance.
i.e.
TParentItem = Class
End;
TParentList = Class
Items : TList<TParentItem>;
Function GetSubRange(nStart,nEnd : Integer) : TParentList;
End;
TChildItem = Class(TParentItem)
end
TChildList = Class(TParentList)
end
List := TChildList.Create;
List.LoadData;
SubList := List.GetSubRange(1,3);
The implementation if GetSubRange would be something like...
Function TParentList.GetSubRange(nStart,nEnd : Integer) : TParentList;
var
aContext: TRttiContext;
aType: TRttiType;
aInsType : TRttiInstanceType;
sDebug : String;
begin
aContext := TRttiContext.Create;
aType := aContext.GetType(self.ClassType);
aInsType := aType.AsInstance;
Result := aInsType.GetMethod('Create').Invoke(aInsType.MetaclassType,[]).AsType<TParentList>;
sDebug := Result.ClassName; // Should be TChildList
// Add the items from the list that make up the subrange.
End;
I appreciate for some things it may be a bit OTT, but in the design above, it works and is another alternative, although I appreciate, its not a class method.
You should use a constructor (a special "kind" of class function). TObject.NewInstance is not a suitable option, unless you require special memory allocation.
And regarding the Execute routine of the command list: the action involved now depends on the type of the object. Imagine a document being able to open, print, paste and save at the same time (not a weird assumption), that would be difficult to implement in this structure. Instead, consider to add interfaces (IOpenDocument, IPasteFromClipboard, IPrintable, ISaveDocument) which indeed could all be actions of one document instance.
I'm doing a full rewrite of an old library, and I'm not sure how to handle this situation (for the sake of being understood, all hail the bike analogy):
I have the following classes:
TBike - the bike itself
TBikeWheel - one of the bike's wheel
TBikeWheelFront and TBikeWheelBack, both inherits from TBikeWheel and then implements the specific stuff they need on top of it
This is pretty straightforward, but now I decide to create multiple kind of bikes, each bikes having it's own kinds of wheel - they do the same stuff as a regular front/back wheels, plus the specific for that bike.
TBikeXYZ - inherits from TBike
TBikeWheelXYZ - inherits from TBikeWheel
And here is my problem: TBikeWheelFrontXYZ should inherit from TBikeWheelXYZ (to get the specific methods of an XYZ wheel), but it should also inherit from TBikeWheelFront (to get the specific methods of a front wheel).
My question here is, how can I implement that in a way that doesn't:
feel like a hack
force me to rewrite the same code several time
Delphi does not support Multiple Inheritance. But classes can support / implement multiple interfaces and you can delegate interface implementation, so you can kinda simulate multiple inheritence.
Use interfaces. Something like this (Off the top of my head, based on your description.....)
type
IBikeWheel = interface
...
end;
IXYZ = interface
...
end;
IFrontWheel = interface(IBikeWheel)
...
end;
TBike = class
...
end;
TBikeWheel = class(TObject, IBikeWheel);
TBikeWheelXYZ = class(TBikeWheel, IXYZ);
TBikeFrontWheelXYZ = class(TBikeWheelXYZ, IFrontWheel);
Then implement classes for the interfaces that do what the corresponding classes in your old (presumably C/C++) library does and instantiate them in the corresponding class's constructor.
Use polymorhism to implment each 'thing' as an object hierarchy in its own right and then add object properties to that object in turn. So, create a hierarchy of wheels, and a hierarchy of bikes. Then add wheels to bikes as fields in the ancestor bike object. See below.
TBikeWheel = class
TBikeWheelXYZ = class( TBikeWheel )
TBike = class
FFrontWheel : TBikeWheel;
property FrontWheel : TBikeWheel
read FrontWhell
TBikeABC = class( TBike)
constructor Create;
end;
constructor TBikeABC.Create;
begin
inherited;
FFrontWheel := TBikeWheel.Create;
end;
TBikeXYZ = class( TBike)
constructor Create;
end;
constructor TBikeXYZ.Create;
begin
inherited;
FFrontWheel := TBikeWheelXYZ.Create;
end;
A variation of Brian Frost's suggestion:
TBikeWheel = class
TBikeWheelXYZ = class( TBikeWheel )
TBike = class
FFrontWheel : TBikeWheel;
protected
function CreateWheel: TBikeWheel; virtual;
public
property FrontWheel : TBikeWheel
read FrontWheel
end;
TBikeABC = class( TBike)
protected
function CreateWheel: TBikeWheel; override;
end;
function TBikeABC.CreateWheel: TBikeWheel;
begin
result := TBikeWheel.Create;
end;
TBikeXYZ = class( TBike)
protected
function CreateWheel: TBikeWheel; override;
end;
function TBikeXYZ.CreateWheel: TBikeWheel;
begin
result := TBikeWheelXYZ.Create;
end;
Basically - you CAN'T. Delphi does not support multiple inheritance.
So left with that dilemma, the question is: could you possibly refactor that library in such a way that you can get away with using interface? Is the multiple inheritance mostly about functions and methods? If so - use interfaces. Delphi can support multiple interfaces on a class.
If the multi-inheritance is more about inheriting actual functionality in the classes, then you're probably looking at a bigger scale refactoring, I'm afraid. You'll need to find a way to break up those functional dependencies in such a way you can make it inherit from a single base class, possibly with some additional interfaces thrown in.
Sorry I can't provide an easy answer - that's just the reality of it.
Marc
You can try to extract an interface, say IFrontWheel, out of TBikeWheelFront, so that it is a subclass of TBikeWheel but implements IFrontWheel. Then TBikeWheelXYZ inherits from TBikeWheel and TBikeWheelFrontXYZ inherits from TBikeWheelXYZ and implements IFrontWheel.
Then you can define a class TFrontwheel and give it the same methods as the interface, but now you implement them. Then TBikeWheelFront and TBikeWheelXYZ get a private member of type TFrontwheel and the IFrontWheel implementations of them simply delegate to the private member methods.
This way you don't have double implementations.
Another alternative with newer versions of Delphi is to leverage generics in a compositional model. This is particularly useful in the case where the multiple base classes (TBarA and TBarB in this example) are not accessible for modification (ie: framework or library classes). For example (note, the necessary destructor in TFoo<T> is omitted here for brevity) :
program Project1;
uses SysUtils;
{$APPTYPE CONSOLE}
type
TFooAncestor = class
procedure HiThere; virtual; abstract;
end;
TBarA = class(TFooAncestor)
procedure HiThere; override;
end;
TBarB = class(TFooAncestor)
procedure HiThere; override;
end;
TFoo<T: TFooAncestor, constructor> = class
private
FFooAncestor: T;
public
constructor Create;
property SomeBar : T read FFooAncestor write FFooAncestor;
end;
procedure TBarA.HiThere;
begin
WriteLn('Hi from A');
end;
procedure TBarB.HiThere;
begin
WriteLn('Hi from B');
end;
constructor TFoo<T>.Create;
begin
inherited;
FFooAncestor := T.Create;
end;
var
FooA : TFoo<TBarA>;
FooB : TFoo<TBarB>;
begin
FooA := TFoo<TBarA>.Create;
FooB := TFoo<TBarB>.Create;
FooA.SomeBar.HiThere;
FooB.SomeBar.HiThere;
ReadLn;
end.
you can try this way, if you do not want to repeat the code several times and want a decoupled code.
type
TForm1 = class(TForm)
btnTest: TButton;
procedure btnTestClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TBike = class
end;
IBikeWheel = interface
procedure DoBikeWheel;
end;
TBikeWheel = class(TInterfacedObject, IBikeWheel)
public
procedure DoBikeWheel;
end;
IBikeWheelFront = interface
procedure DoBikeWheelFront;
end;
TBikeWheelFront = class(TInterfacedObject, IBikeWheelFront)
public
procedure DoBikeWheelFront;
end;
IBikeWheelBack = interface
end;
TBikeWheelBack = class(TInterfacedObject, IBikeWheelBack)
end;
TBikeWheelFrontXYZ = class(TInterfacedObject, IBikeWheel, IBikeWheelFront)
private
FIBikeWheel: IBikeWheel;
FBikeWheelFront: IBikeWheelFront;
public
constructor Create();
property BikeWheel: IBikeWheel read FIBikeWheel implements IBikeWheel;
property BikeWheelFront: IBikeWheelFront read FBikeWheelFront implements IBikeWheelFront;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TBikeWheel }
procedure TBikeWheel.DoBikeWheel;
begin
ShowMessage('TBikeWheel.DoBikeWheel');
end;
{ TBikeWheelFrontXYZ }
constructor TBikeWheelFrontXYZ.Create;
begin
inherited Create;
Self.FIBikeWheel := TBikeWheel.Create;
Self.FBikeWheelFront := TBikeWheelFront.Create;
end;
{ TBikeWheelFront }
procedure TBikeWheelFront.DoBikeWheelFront;
begin
ShowMessage('TBikeWheelFront.DoBikeWheelFront');
end;
procedure TForm1.btnTestClick(Sender: TObject);
var
bikeWhell: TBikeWheelFrontXYZ;
begin
bikeWhell := nil;
try
try
bikeWhell := TBikeWheelFrontXYZ.Create;
IBikeWheelFront(bikeWhell).DoBikeWheelFront;
IBikeWheel(bikeWhell).DoBikeWheel;
except
on E: Exception do
begin
raise;
end;
end;
finally
if Assigned(bikeWhell) then FreeAndNil(bikeWhell);
end;
end;
Sorry, Delphi does not support Multiple Inheritance.
I would like to suggest the following steps:
Inherit the TBikeWheelFrontXYZ class from either TBikeWheelXYZ or TBikeWheelFront (since in Delphi multiple inheritance is impossible as mentioned in the answers above).
Convert one of the parent classes TBikeWheelXYZ or TBikeWheelFront to class helper for the TBikeWheel class.
Add the class helper unit to the unit, where the TBikeWheelFrontXYZ class is declared.