Design Compromises

The ideal design would be a mix of all good things in life, which as we know, is never ever achievable.

Therefore, in real life, designs strive to achieve an excess of at least one factor, which determines the personality of the system being constructed. It could therefore tend towards performance /  simplicity /  or aesthetic beauty(I hope you thought of apple here *) and so on.

Whenever a design achieves more than one of these goals, it comes into the realm of classics.

Change

I will use a piece of the data collection framework, which we were dicusssing to introduce the concept of change. Our framework, uses a callback pattern whereby handlers can be associated with data which is collected, which can be later invoked from within the framework, to make enable modifications and processing.

This used to be done like this

framework.addProcessor(fn_x1)
framework.addProcessor(fn_x2)
framework.addProcessor(fn_x3)
framework.DataToBeCollected = Metadataspecs; framework.collect()

framework.process (invokes the callback functions

x1,x2,x3 on the collected data

Later on, a need for pre-processing the meta – data was felt. i.e Even before the data was collected, some processing had to be done like loading already collected data into the columns from the database, so that they neednt be collected again.

We achieved this by adding a new classs of functions to the framework. Now the sequence of calls became something like this –

framework.addPreProcessor(fn1)
framework.addPreProcessor(fn2)
framework.addPreProcessor(fn3)
framework.addProcessor(fn_x1)
framework.addProcessor(fn_x2)
framework.addProcessor(fn_x3)
framework.DataToBeCollected = metadataspecs;

framework.preprocess -(invokes the callback functions)

fn1,fn2,fn3 on the MetaData

framework.collect()

framework.process (invokes the callback functions)

x1,x2,x3 on the collected data

This is stupid. We had to add to a new set of functions to the base framework, to add this new functionality. Adding the new set of functions to an already in use framework, requires lot of attention and thoroughness to ensure that nothing breaks, not to mention that extra bit of reading to understand how things used to work before.

However had we designed the classes like this instead, we could have saved a lot of trouble in this area.

framework.addCallBack(META, fn_x1)
framework.addCallBack(META, fn_x2)
framework.addCallBack(META, fn_x3)

framework.addCallBack(DATA,fn_x1)
framework.addCallBack(DATA,fn_x2)
framework.addCallBack(DATA,fn_x3)

framework.DataToBeCollected = MetaDataSpecs;

framework.Process(META) (invokes callback fns tagged with META)

fn1,fn2,fn3 on the metaData

framework.collect()

framework.process(DATA) (invokes callback fns tagged with DATA)

x1,x2,x3 on the collected data

Extensibility

I hope the above example was able to demonstrate, what it would mean to have extensibility aka maintainability in design. Extensibility in common parlance would mean avoiding changes to existing code ie code which is already tested and running should not require modification to add new functionality.Changes are evil extensibility is cool.

Changes to existing code would be nothing but lost investment and time on old stable code. Classes should be designed to facilitate this. The cool way of expressing this idea is to say that classes should be open for extension, but closed for change.

* Apple systems almost always seems to achieve and exceed their aesthetic goals but unlike the legendary Steve Wozniak built systems, none of them seems have achieved the same levels of engineering wizardry ever. (though the spirit of of engineering excellence might have survived)