__alex
asked on
Pattern required
I want to provide myself with a little framework that looks something like:
A = class
A1 = class (A)
A2 = class(A)
A1 and A2 are still abstract and I have to derive some Bs from either A1 or A2. A1 and A2 implement a method Foo that I want to call from A but I don’t want any B to be able to override Foo. I know I can put A, A1 and A2 into one single file and access private methods but I want to avoid it (I like small units). Tricky?
A = class
A1 = class (A)
A2 = class(A)
A1 and A2 are still abstract and I have to derive some Bs from either A1 or A2. A1 and A2 implement a method Foo that I want to call from A but I don’t want any B to be able to override Foo. I know I can put A, A1 and A2 into one single file and access private methods but I want to avoid it (I like small units). Tricky?
ASKER
Nice, but...
The only reason for A1 and A2 being derived from A is because I want to reuse code. It’s not the kind of A1/A2-is-an-A relationship, semantically speaking. If I’ll go for interfaces like your approach implementation is shifted from A to A1/A2. My actual workaround is a procedural type property fnFoo in A that can be set only once. It is set in the constructor of A1 or A2. => A can call fnFoo but Bs can’t alter it. But as I said, it’s just a workaround.
The only reason for A1 and A2 being derived from A is because I want to reuse code. It’s not the kind of A1/A2-is-an-A relationship, semantically speaking. If I’ll go for interfaces like your approach implementation is shifted from A to A1/A2. My actual workaround is a procedural type property fnFoo in A that can be set only once. It is set in the constructor of A1 or A2. => A can call fnFoo but Bs can’t alter it. But as I said, it’s just a workaround.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Hi Alex,
I don't understand your interface thingy above... why do A1 and A2 have to declare the code for Foo whereas B doesn't? Since B uses the A interface as well...
I don't understand your interface thingy above... why do A1 and A2 have to declare the code for Foo whereas B doesn't? Since B uses the A interface as well...
B doesn't have to implement the Foo method since the implementation fo the interface is passed onwards to the ImplementFoo property. You assign some object to this property and it will handle the Foo function for you.
Basically this is the only way to get close to what you're trying to do, though. Once you have created a method that could be overridden, then you can't prevent that some new object will override the method even further. But by using interfaces, you just force developers to use them in a certain way.
Basically this is the only way to get close to what you're trying to do, though. Once you have created a method that could be overridden, then you can't prevent that some new object will override the method even further. But by using interfaces, you just force developers to use them in a certain way.
... and if they passed in a nil?
ASKER
Thanks again!
If they pass nil, well... If that is possible then you have to check for it. Or create some default object if they pass nil. But since you can control this value from the Create() method, you can decide what you want to fill it with.
The use of interfaces might be what you are looking for but it is a bit complex.
The use of interfaces might be what you are looking for but it is a bit complex.
As an alternative, consider using interfaces instead. An interface is like an object with no data fields and only abstract methods. Thus you would get:
type
A = interface
procedure Foo;
end;
A1 = class( TInterfacedObject, A )
procedure Foo;
end;
A2 = class( TInterfacedObject, A )
procedure Foo;
end;
B = class( TInterfacedObject, A )
FA: A;
constructor Create( Foo: A );
property ImplementFoo: A read FA write FA implements A;
end;
procedure A1.Foo;
begin
MessageBox( GetDesktopWindow, 'A1', 'A1', MB_OK );
end;
procedure A2.Foo;
begin
MessageBox( GetDesktopWindow, 'A2', 'A2', MB_OK );
end;
constructor B.Create( Foo: A );
begin
inherited Create;
ImplementFoo := Foo;
end;
procedure CallFoo;
var
FooB: A;
begin
FooB := B.Create( A1.Create );
FooB.Foo;
FooB := B.Create( A2.Create );
FooB.Foo;
end;
See, the methods aren't even virtual! :-)
Now, the fun part is that B can use any class that supports the A interface. (Thus, even itself!) But B is NOT able to overwrite the Foo method in whatever interface that is passed to it.
Interfaces don't really add much overhead either. We're talking about a few bytes of overhead. Then again, if you want B to only support the Foo method of class A2 then use this instead:
B = class( TInterfacedObject, A )
FA: A2;
constructor Create( Foo: A2 );
destructor Destroy; override;
property ImplementFoo: A2 read FA write FA implements A;
end;
Remember though that you need a destructor now to free the class that is now linked to your class.
Working with interfaces is difficult at first, but extremely useful for using patterns.