Delegation over Inheritance

A common design anti-pattern in object oriented programming is to use inheritance too often.

It usually better to delegate a behaviour to another class, rather than adding the behaviour into a sub-class.

For example suppose we want to automate some tasks to run every night. We create a Task interface, with a run method.

BackupFolder implements Task and, as the name suggests, it makes a backup copy of a Folder.

If the backup fails, we either need to send an email, or log a ticket in an issues tracker.

It is tempting to create two subclasses : EmailBackupFolder and TicketBackupFolder. This is an anti-pattern. Don't do it!

Instead, we should create ANOTHER interface called ErrorHandler, and have two implementations : SendEmail and LogTicket. BackupFolder has an ErrorHandler, and delegates the error handling behaviour.

It becomes clear why this is better when we create new Tasks.

For example, BackupDatabase also needs to handle errors, and if we were to create EmailBackupDatabase and TicketBackupDatabase, the code becomes messy. If we need another form of error handling (such as flashing lights and a siren!), it is easy with delegation, but a really horrible mess using inheritance alone.

So instead of a single, complex class hierarchy:

Task
    BackupFolder extends Task
        EmailBackupFolder extends BackupFolder
        TicketBackupFolder extends BackupFolder
    BackupDatabase extends Task
        EmailBackupDatabase extends BackupDatabase
        TicketBackupDatabase extends BackupDatabase

we have two simple class hierarchies :

Task
    BackupFolder extends Task
    BackupDatabase extends Task

ErrorHandler
    EmailErrorHandler extends ErrorHandler
    TicketErrorHandler extends ErrorHandler

Not only is our code neater, it is also much more flexible. It is easy to add SirenErrorHandler.

Taking it one step further, we can create CompoundErrorHandler, which has a list of ErrorHandlers, and calls each in turn. So when a critical backup fails, we can send out multiple emails and sound the alarm!

davesBackup = new BackupFolder( davesFolder )
davesBackup.errorHandler = EmailErrorHandle( davesEmailAddress )

importantBackup = new BackupDatabase( payrollDatabase )
importantErrorHandler = CompoundErrorHandler()
importantErrorHandler.add( new EmailErrorHandler( theBossEmailAddress ) )
importantErrorHandler.add( new EmailErrorHandler( payrollEmailAddress ) )
importantErrorHandler.add( new SirenErrorHandler() )
importantBackup.errorHandler = importantErrorHandler

I am surprised how often this pattern crops up, and I still fall foul of the anti-pattern every now and then.