Skip to content

Software Flexibility

October 4, 2011

Software Architects have wonderful terms like scalability and flexibility but what do these mean in the real world and how do we start to put these into practice. I would like to start by looking at one of these terms; flexibility.

Software flexibility can mean a lot of things but when it is used to describe a whole system, it normally refers to the ability for the solution to adapt to possible or future changes in its requirements. When you design or build a solution you should try to cater for these changes which inevitably arrive in the future. This flexibility should be catered for with the design of the system as a whole but there is also no reason not to also include it with the smaller aspects of the system.

The key to building highly flexible systems is the loose coupling of its components. For example, there is no point building a solution that is tightly integrated with active directory if this part could change down the line. This is not something you should do even if you don’t expect it to change in the future. Nothing is cast in stone in software and at some point you will find yourself in a situation where you will wish you had done it differently.

This whole concept of flexibility is probably best explained through an example. In this blog I will build a small authentication sub-system. Starting with a nasty hard coded piece of code and slowly progressing to a configurable and flexible solution. Feel free to skip to the later steps if the early parts are obvious to you.

Starting out

For the purposes of simplicity, let’s assume that our application is a single threaded application. Threading adds some complexity that will detract from the core of the example. Error handling has also been kept to a minimum (actually, it’s pretty much non-existent) to remove any unnecessary clutter and no exceptions were harmed during the writing of this blog :).

In our example, let’s assume that our active directory implementation has the following; A LogOn method to verify a user name and password, a LogOff method to log the user off the system and a single property LoggedOnUser that returns the name of the user who is logged onto the system. The definition would look something like this.

  TtstActiveDirectoryAuthentication = class(TObject)
  private
    // ...
    FLoggedOn: Boolean;
    function GetLoggedOnUser: string;
    // ...
  public
    procedure AfterConstruction; override;
    procedure LogOff;
    procedure LogOn(AUserName: string; APassword: string);
    property LoggedOnUser: string read GetLoggedOnUser;
  end;

The developer creating the system would be quite impressed with themselves, having created a nice wrapper for all of the active directory logic. They would then start using this new shiny class throughout their application. Pretty soon the application would be riddled with calls to LogOn, LogOff and accesses to LoggedOnUser.

The big problem with this scenario is that eventually there will come a time when this code needs to be changed for a client that does not have active directory installed. Typically what happens then is that a new class is created that is almost identical previous class and a number of “if … then …” blocks are added.

This starts off being the easiest and quickest way to solve the problem but ends up as a maintenance nightmare. The new Oracle authentication class could look like this:

  TtstOracleAuthentication = class(TObject)
  private
    // ...
    FLoggedOn: Boolean;
    function GetLoggedOnUser: string;
    // ...
  public
    procedure AfterConstruction; override;
    procedure LogOff;
    procedure LogOn(AUserName: string; APassword: string);
    property LoggedOnUser: string read GetLoggedOnUser;
  end;

Throughout the code we would have nasty unmaintainable titbits like this:

procedure TfrmMiniApp.tSetUserNameTimer(Sender: TObject);
var
  _UserName: string;
begin
  if FActiveDirectory then
    _UserName := FtstActiveDirectoryAuthentication.LoggedOnUser
  else
    _UserName := FtstOracleAuthentication.LoggedOnUser;
  if _UserName <> '' then
    sbFooter.Panels[0].Text := 'User Name: '+_UserName
  else
    sbFooter.Panels[0].Text := '';
end;

As more and more methods and properties are added to the existing authentication classes and new authentication classes are added this quickly descends into chaos.

You can download the code for this part from Pass1.zip

Decoupling

So how do we solve this mess? The first step in getting out of this mess is to add an authentication interface. This interface will abstract out the Oracle and Active Directory authenticators from the rest of the application.

So where do we start with the interface? The easiest thing to do is just extract out the common methods and properties (not ideal for interfaces but will do for this example) from the classes and add them to the new interface.

  ItstAuthentication = interface(IInterface)
    ['{2ACA6F5F-4BD9-4C69-BB59-2C6502AF822B}']
    function GetLoggedOnUser: string;
    procedure LogOff;
    procedure LogOn(AUserName: string; APassword: string);
    property LoggedOnUser: string read GetLoggedOnUser;
  end;

The interface should look something like the one above. It’s pretty easy to hook in the new interface to the two classes. The Active Directory class would look like this:

  TtstActiveDirectoryAuthentication = class(TInterfacedObject, ItstAuthentication)
  private
    // ...
    FLoggedOn: Boolean;
    function GetLoggedOnUser: string;
    // ...
  public
    procedure AfterConstruction; override;
    procedure LogOff;
    procedure LogOn(AUserName: string; APassword: string);
    property LoggedOnUser: string read GetLoggedOnUser;
  end;

Using the interface in the application is where you see the real differences. The code is much cleaner as all of the “if … then …” blocks are replaced with a single line of code. The interface variable would be declared like this.

  private
    { Private declarations }
    FtstAuthentication: ItstAuthentication;

The code to creating the objects would look like this:

  if chkActiveDirectory.Checked then
    FtstAuthentication := TtstActiveDirectoryAuthentication.Create
  else
    FtstAuthentication := TtstOracleAuthentication.Create;

And using the interface in the code looks a lot cleaner than it previously did.

procedure TfrmMiniApp.tSetUserNameTimer(Sender: TObject);
var
  _UserName: string;
begin
  _UserName := FtstAuthentication.LoggedOnUser;
  if _UserName <> '' then
    sbFooter.Panels[0].Text := 'User Name: '+_UserName
  else
    sbFooter.Panels[0].Text := '';
end;

All in all we have a much smarter solution for very little work. We have also started the process of breaking up our solution into loosely coupled components.

You can download the code for this part from Pass2.zip

Factories

This solution looks, at face value, like we have found the sweet spot but it falls short in a couple of places. For one, every time we add a new authentication provider we need to rebuild our system. We also need to retest the entire application for each new provider, or at least the parts that interface with our authentication providers. It’s also not the easiest thing to use these components in other systems. So how do we take this forward? We create a factory. I find that I draw pictures better than I write so I will show a simple class diagram that includes our factory.

What I have done is start breaking up the components of the authentication system into different libraries. One library for the factory (more about this now) and the ItstAuthentication interface and additional libraries for each of the authentication providers.

How the factory works is this. We only couple our application to the Authentication Factory and Authentication Interface. At the same time we also couple the authentication providers to the Authentication Factory and Authentication Interface but there is no coupling between the application and providers themselves. The authentication providers register themselves with the factory then they are first loaded and the application asks the factory for the active authentication provider. It’s important for the factory to be a singleton as both the application and the providers need to access the same instance of the factory class.

This blog is taking the factory a bit further by building a mini plug-in system to dynamically load our provider.

So what does this factory look like?

  TtstAuthenticationFactory = class(TObject)
  private
    FActiveAuthenticator: ItstAuthentication;
  protected
    constructor CreateInstance;
    class function AccessInstance(Request: Integer): TtstAuthenticationFactory;
  public
    constructor Create;
    destructor Destroy; override;
    class function Instance: TtstAuthenticationFactory;
    class procedure ReleaseInstance;
    procedure SetActiveAuthenticator(AAuthenticator: ItstAuthentication);
    property ActiveAuthenticator: ItstAuthentication read FActiveAuthenticator;
  end;

You can ignore the majority of the code as that code is singleton code added by ModelMaker. Any robust singleton implementation will do. The important lines are 12, 21 and 22. These lines are responsible for getting and setting the provider. We put this factory along with the authentication interface into a BPL package.

We create a DLL for each provider. It’s important to make sure that you compile the DLL with runtime packages enabled and set the package list to contain our factory package. We also add the factory and interface unit names to the uses clause.

At the end of each unit where our provider is declared we add a line to automatically create an instance of the provider and set it as the active authenticator. This is done by using the following line of code.

begin
  TtstAuthenticationFactory.Instance.SetActiveAuthenticator(
    TtstActiveDirectoryAuthentication.Create);
end.

We add the next couple of lines of code to our main application to load the authenticator that we would like to use (normally the authenticator would be provided by configuration data) and then get the active authenticator from the authentication factory.

begin
  LoadLibrary(PChar('ActiveDirectory\ActiveDirectoryAuthentication.dll'));
  FtstAuthentication := TtstAuthenticationFactory.Instance.ActiveAuthenticator;
end;

You also need to make the same package changes to the host application. Enable runtime packages and set the package list to contain our factory package. Why is all of this necessary? Because it ensures that both the host application and the provider access the same factory. Without it, they will both have their own singleton of the factory in different memory spaces and as they say, “and never the twain shall meet”.

At this point it would probably be a good idea to open the project and look at the code. This type of solution adds significant flexibility to our previous effort. Yes there is a bit more code, but we are slowly starting to arrive at that word “flexible”. No longer is our application tied to the providers. We can happily build a brand new authentication provider and not even rebuild our application (if our configuration is working the way it should). Taking it a step forward, we also now have a solution where we can create a set of test packs for our providers and individually test and certify them. Any provider that passes the tests should work properly with our application(s).

You can download the code for this part from Pass3.zip

The next step

Where do we go to from here? It looks like we have the ideal solution and for users of Delphi who are still working with Delphi versions older than Delphi 2010 it probably is. There are some enhancements that we can make to the existing solution. We can get the factory to create the adapter instead of the provider itself and other tweaks but not much more than that.

The big problem with this approach is that we are still duplicating code and there is more coupling than there needs to be. For every different provider type (authenticator, configuration adapter, etc) we have another factory. There is also the problem that both your application and providers need to know about two things, the authentication interface and the authentication factory. What would need to change if we switched from a single authenticator instance to a pool of authenticators to cater for threading? We would have to make some fairly big changes to our factory and also make some changes to the providers and the core application.

Wouldn’t it be nice to have a generic solution where we only need to create the interface? Think of it as a big all-purpose factory. But not just a factory; it’s a solution that caters for singletons, pools and pretty much any type of implementation that you would like. That’s where dependency injection comes in.

With dependency injection, an application will request a service or component from the dependency injection container and the container will be responsible for loading and configuring the requested service.

Looking at the existing example, what if I wanted to support both Active Directory and Oracle Authentication at the same time? I would need to pull out the hammer and make a few modifications to my factory. Rather than do that I will start with a new version, one that makes use of Dependency Injection.

To compile any code from this point onwards, you will need to download the Delphi Spring Framework from http://code.google.com/p/delphi-spring-framework/.

So what do we need to switch across to Dependency Injection and the Delphi Spring Framework? Well, the changes are actually quite minimal. First we make a couple of boilerplate changes.

1) We need to delete our factory from the authentication package as it is no longer needed.

2) We need to add the spring framework packages to the runtime packages list by adding the following two packages “Spring.Core;Spring.System”. This should be done to the providers and the host application.

3) We add the Spring.Container unit to the uses clause of our providers and the Spring, Spring.Container, Spring.Services units to the uses clause of our host application.

We change the code at the end of each unit where our provider is declared to now register our providers (services) with the global spring container instead of our factory.

begin
  GlobalContainer.RegisterComponent<TtstActiveDirectoryAuthentication>.
    Implements<ItstAuthentication>('ActiveDirectory');
end.

This registers the provider (TtstActiveDirectoryAuthentication) with the container and tells it that the provider implements the ItstAuthentication interface. We also give it a friendly name (yes, I know that I have hardcoded the name :)) so that we can load the class through a string in the future. This is very important because it lets us add our providers as configuration data.

All we need to do now is change the way that we retrieve our class. Instead of using the factory, we now ask the spring singleton ServiceLocator to retrieve our class based on the friendly name. We still need to load the library as we did before, except in this case, we can load both libraries. We also need to call the build method which instructs the container to inspect all of the meta-data.

begin
  LoadLibrary('ActiveDirectory\ActiveDirectoryAuthentication.dll');
  LoadLibrary('Oracle\OracleAuthentication.dll');
  GlobalContainer.Build;
  FtstAuthentication := ServiceLocator.GetService<ItstAuthentication>('Oracle');
end;

That’s it; our application can now pick the authentication provider it would like to use by just specifying the friendly name to the service locator.

But wait, there’s more 🙂

If you remember above I mentioned something about changing the lifespan of the classes (Singleton, etc.). In my example, let’s say that our interface to Oracle would only work as a single connection. All we would need to do is add the [Singleton] attribute before the class. The framework will do the rest.

type
  [Singleton]
  TtstOracleAuthentication = class(TInterfacedObject, ItstAuthentication)
  private
    // ...
    FLoggedOn: Boolean;

So what is the point of my last weak attempt at humour? The point is that switching to this sort of framework gives you far more flexibility in your solution and that’s what this blog is ultimately about.

You can download the code for this part from Pass4.zip

More Information on Dependency Injection with Delphi

There is a lot more to Dependency Injection than what I have covered in this blog. Constructor, Property, Method and Field injection are all valid forms of Injection. If you want some more information on Dependency Injection with the Spring Framework in Delphi, hop over to Nick’s blog at http://www.nickhodges.com/.

Interface Design

Before I finish there are a couple of important aspects of interface design that I should mention. The temptation is always there to take all of the methods and properties from a class and slap them in an interface. This will not necessarily solve the tight coupling problem. Why is this? Often people take functionality that is specific to a provider and add this to their interface. All that does is restrict your application to only use the provider in question.

For example, let’s say that Active Directory in our example above includes a neat function to send a user a sms when they log on. Including this SendSmsToUser option in our interface and application will pretty much force us to use Active Directory until we can mimic the behaviour in the other providers.

There are three approaches you can take to defining your interfaces.

1) The first is to look at the lowest common denominator and create an interface around that.

2) Secondly, you can try and create interfaces that include all of the functionality that you would like to use and then use some attributes or a function to tell the application which functionality is supported.

3) Finally, you can try and find a balance between the previous two. Use the lowest common denominator and add some of the more advanced functionality as optional features.

You can make use of multiple interfaces to cover the functionality. Think carefully when creating interfaces. These should be seen as a contract that you don’t break very easily.

Interfaces in Delphi

No blog that includes Delphi and interfaces would be complete without a word of warning. Interfaces in Delphi are, by default, reference counted. When there are no more variables pointing to the interface, the object will be destroyed. What that means is that you should never mix interfaces and object pointers. Doing so is risky and likely to end up with access violations.

Summary

In summary, things have definitely changed over time. In the old days, people used to clutter their code with “if x then y” statements, trying to cover all of the bases. A lot of modern systems use factories to provide a level of abstraction and the trend is now towards dependency injection.

Think flexibility when you design the solution and you will definitely come out with a better solution.

From → Coding Tips

9 Comments
  1. Thanks for the great post! I’m very impressive with this informative blog 🙂

    At present, I’m still investigating on where to put the registration code. It’s simple to do this in the initialization section but this makes the application code couple with the spring4d container.

    • Thank you for the kind words.

      You have to have some code that is tightly coupled with Spring. The initialization block is a nice place to put it because then you don’t need to worry about finding a way to initialize it when you load the libraries. If you want to decouple your interfaces and providers from the spring framework then just create another unit in the package and register all of your classes from there and call your registration code from the initialization block of that unit. If you really want to break it off completely, you could put this registration unit in a separate package.

  2. Epo permalink

    Duck typing can be a plus for this :http://delphisorcery.blogspot.com/2011/09/aop-and-duck-typing-in-delphi.html#comments. With dynamic interface, we have a new possible approach.

  3. lcarrasco permalink

    Hi Dean, the links to the source code files are broken, can you please fix them to download and test them? Thanks in advance. Great article!

  4. lcarrasco permalink

    Hi Dean, the download links are broken. Could you please fix them? Thanks in advance. Great article

Leave a reply to Dean Hill Cancel reply