Thursday, May 31, 2007

Abstract Factory: Part III

In this post I'm going to address a couple of issues left out in the discussion of the abstract factory pattern. If you haven't read the earlier discussions in regards to this pattern you can see them here and here.

The abstract factory pattern is a useful and highly used pattern and sometimes we automatically pick this pattern without thinking about the consequences or without asking the question what value does the pattern add? Or does it limit the design in any way?

Like most patterns Abstract Factory also has negative aspects and concerns that have to be thought about before actually trying to use it. In most situations where we need to support multiple competing technologies (i.e. the database example in previous discussions) or we want our client code to be absolutely free of hard coded creation commands (the 'new' keyword) this pattern looks like it's doing the job perfectly but we should always consider the negative aspects too:

Abstract Factory does not support adding new products. (what I mean by support is support without the need for re-coding)

As I explained in the previous discussions one of the main features of this pattern is that adding new product families is a breeze. You just develop your new product family (in the database example I would develop the classes that would communicate with my new database technology), create a product family concrete factory (the class inheriting from AbstractFactory and in charge of the actual product creations) and your set to go. You don't need to change even a single line of code in your previous implementation and with a little help from reflection we can plug in our new family of products, change the config file to start using the new family and presto! It works.

BUT what if you wanted to add a new product? Take a look at the previous example discussed in part 1 of my abstract factory discussion, the diagram below summarizes it:



What if we needed to add a SupplierAccess as a new product how could we go about doing that? Well the pattern doesn't give you any features for adding new products and the truth is in design scenarios where adding a new product is going to happen quite often you shouldn't be using this pattern. It's just too much trouble:

In this example to add a the new SupplierAccess product we would have to change the AbstractFactory class to define a new abstract method called CreateSupplier which in turn would return an ISupplierAccess interface. We would have to create the new interface and create an implementation for the new interface for every single product family that we already have (i.e. the Oracle & SQL product families) and then change the current implementation of all our concrete factories (the SQLFactory & OracleFactory classes) to implement the new create method. This is the biggest possible mess we could have gotten ourselves into. Basically every package (or worst yet every .net assembly) in the original design has to be modified to support the change.

As I mentioned before this pattern is not suitable for situations where you have a lot of new products being added to the system, in these situations you should look into the prototype pattern or other patterns (I will hopefully get around to talk about these in later posts).

Overkill

As is the case with many design patterns you don't want to use a pattern in a situation where it's overkill. Obviously if your requirements specifically state that you are going to have only one product family why would you design it using this pattern? If you have only one product type (or have identified only one so far) why would you use this pattern (what's the use of a factory with just one single create method)? But there are also situations where the patterns use might seem to be overkill but really isn't. For example:

Suppose we are developing a system that is going to support two and only two database technologies: SQL Server & Oracle. We have 7 or 8 product types and we first think of the abstract factory pattern then turn it down on the grounds that it's overkill. It's overkill due to the fact that we are not going to support any other product families in the future. There is going to be only two product families!!! Is it overkill? Well let's see what happens if we want to implement this using specific features of each technology but not use this pattern (or any other pattern that is aimed at solving this problem). In one way or another we would end up with this kind of code:


class WarehouseAccess {
public void Save(some data) {
if (IsInSQLMode) {
// SQL Server specific code
}
else {
// Oralce specific code
}
}
/* the rest of the code */
}

The above code has many problems one of which is logical cohesion (which is a very bad level of cohesion). We have put two completely different pieces of code in one place (the SQL and the Oracle implementation). We are also adding an extra if statement in the path of a save (although an 'if statement' might be a very inexpensive code addition, but it's still something extra). Also from a development/maintenance point of view we have two different code types which might need different programmers to work on them, they will require different assemblies to be referenced and namespaces to be imported, etc.

All the mentioned problems seem trivial but from a "clean code" perspective & a performance perspective implementing the abstract factory pattern here is worth while.

2 comments:

Kevin Jones said...

Thanks for the well structured and informative factory posts

avadhraj said...

it was very nice article, and i think this is about the web design so i have a information also that Web Design Services, Neosoft Technologies are experts in Website Designing, Portal Development, Web Re-designing, Corporate Branding, CD/DVD Authoring etc.