Become a member!

Duck Typing in Delphi

During a new dorm feature development, I’m faced a nice problem:

I want to have a generic access to a “kind” of list

Let’s say:

procedure DoSomething(Obj: TMyListType);
begin
...
end;

But, I want to have that generic access without a Layer Supertype object, because I need to be able to use objects from other libraries or 3rd party. In this case traditional polimorphism is not usable, so I’ve opted for an interface…

procedure DoSomething(MyIntf: IMyListInterface);
begin
...
end;

Cool, but I want to have that access without any change to that object. So implement an interface is not a viable solution. This is particulary true because the generics data structure in Delphi do not implement an interface. Will be nice to have a fully interfaced list and dictionaries in a future Delphi version.

So, how I could implement a generic access to a generic list?

procedure DoSomething(MySomething: ???);
begin
...
end;

However, operations on that list are few and well known, so I’ve opted for a “DuckTyping”.

\

The name of the concept refers to the duck test, attributed to James Whitcomb Riley, which may be phrased as follows:

“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”

So, in my case, I’ve created an Adapter object wich adapts the external interface (few, well know operations) to the “duck” inside it.

The adapter is useful because I can use the compiler checking that the RAW RTTI access doesn’t allow.

Write an “RTTI adapter” using the new Delphi RTTI, is very simple. This is the code of the class TDuckTypedList that allow to use any object as a “list”. How I can define that the object is actually a list?

The criteria is:

**If the object has\

  • An “Add” method with one parameter of type TObject (or descendant);\
  • A “Count” property;\
  • A “GetItem” method that have an integer parameter and return an object;\
  • A “Clear” method;
    then, that object is a list.**

So I can write the following:

DuckObject := TDuckTypedList.Create(Coll);  //the adapter
for x := 0 to DuckObject.Count - 1 do
  DoSomething(DuckObject.GetItem(x));  

I’ve done some speed tests comparing this way to the classic static way, and the speed is almost the same because the RTTI lookup is cached in the constructor of the adapter. So, so far so good.

This solution is already in use inside the dorm code in a feature branch.

Full code is available in the dorm SVN

Any comments?

Comments

comments powered by Disqus