Sunday, September 25, 2011

AOP and duck typing in Delphi

AOP - something that fascinated me from the very first moment when my coworker told me about PostSharp. Unfortunately something that was almost impossible in Delphi without the proper knowledge of assembler and all the internal low level stuff. However even with Delphi 7 it was possible to a certain degree to intercept virtual methods using MeAOP.

With the recent additions (namely enhanced RTTI) it became very easy to introduce this feature in a more safe way. However this is nothing compared to a framework like PostSharp which is able to enhance almost everything using IL weaving. Currently you can intercept any virtual method that has RTTI (by default any public/published method). It is very similar to what DynamicProxy does. Delphi XE introduced the TVirtualMethodInterceptor that made it possible to intercept the virtual method calls of an existing object. This is done by creating a new VMT at runtime and attaching it to the object. I went a step further and made it possible to patch any existing class VMT so that any object of this class (and every derived class if you wish) can be intercepted. You can add any aspect to any method you like (if it is virtual and has RTTI). There is currently one built-in aspect for logging which logs entering and leaving the method, all parameters and the result if there is one. You can find a simple demonstration on how to instrument two virtual methods to show logging in the SVN repository.

Did you ever face the problem of having a dependency in your code that you could not remove so simply? Like having that class in your code that is passed around but not being part of your source code so you cannot mock it out? Well you could inherit that class and implement some interface so you just have the dependency on that interface. Yes if that class is not sealed (did you know you can use that keyword in Delphi?). To be honest I haven't seen anyone using this yet (could be because I mostly work with sourcecode written in Delphi 7). But let's say you have a class that is sealed and you want to remove the dependency. You could write an abstract wrapper class and then inherit from it to remove the dependency from your code. Sounds like a lot of work, right?  How about duck typing?





When you look at what duck typing means this is very similar to as if the class implements a specific interface. "Walks like a duck and swims like a duck and quacks like a duck" basically means it got these 3 methods. Like implementing IDuck with these 3 methods. But duck typing does not require the class to implement this interface. Prism got duck typing just recently and DSharp now got it too! (only for XE2 yet, sorry) Of course no compile time support but something similar to what the RemObject guys call soft interfaces and dynamic duck typing mode.

Here is the program from the wikipedia page in Delphi!

program DuckTypingSample;

{$APPTYPE CONSOLE}

uses
  DSharp.Core.DuckTyping;

type
  IDuck = interface(IInvokable)
    procedure Quack;
    procedure Feathers;
  end;

  TDuck = class(TInterfacedObject, IDuck)
  public
    procedure Quack;
    procedure Feathers;
  end;

  TPerson = class
  public
    procedure Quack;
    procedure Feathers;
    procedure Name;
  end;

{ TDuck }

procedure TDuck.Feathers;
begin
  Writeln('The duck has white and gray feathers.');
end;

procedure TDuck.Quack;
begin
  Writeln('Quaaaaaack!');
end;

{ TPerson }

procedure TPerson.Feathers;
begin
  Writeln('The person takes a feather from the ground and shows it.');
end;

procedure TPerson.Name;
begin
  Writeln('John Smith');
end;

procedure TPerson.Quack;
begin
  Writeln('The person imitates a duck.');
end;

procedure InTheForest(duck: IDuck);
begin
  duck.quack();
  duck.feathers();
end;

var
  donald: IDuck;
  john: TPerson;
begin
  donald := TDuck.Create;
  john := TPerson.Create;
  InTheForest(donald);
  InTheForest(Duck<IDuck>(john));
  john.Free;
  Readln;
end.

Notice how this is different from dynamic typing you could do with invokable variants. Like the variants approach the evaluation is done at runtime. But in this case you have more type safety. Instead of just passing a variant to the InTheForest procedure and not having any type safety (you could also write duck.Bark() and compiler would not complain) you only can call methods of the IDuck interface. If the wrapped object does not have the required method (also matching parameters) it throws the not implemented exception.

What is it good for? This can open up certain parts of your application to unit testing and mocking that you could not do before because you had lots of static dependencies that could not be mocked. And of course to show that Delphi is catching up. ;)