Posts Tagged ‘Software Design’

h1

Horses for courses don’t invent wheels

September 26, 2009

The phrase ‘let’s not re-invent the wheel’ is often applied when looking at a new problem, and tells us that we should not work out a solution in isolation but look around for existing similar work. However it’s also often misapplied to building the solution, that is: when a wheel needs to be built, we should use an existing one. 

This is a Bad Thing. Wheels may come with the same side-on shape (generally round), but they come in many sizes and weights and widths and treads and load capacities and power hookups. 

Some wheels only look like other wheels at first glance. A motorcycle wheel for example, is about the same size as a car wheel, and it has a tyre and everything. But there are a variety of power deliveries (via chain or belt or direct shaft), none of which are compatible with modern cars, and the build is lighter and weaker, and the tyres have a different cross section that is vital for going around corners. 

Horses for courses

A wheel built a hundred years ago for a cart, with a hard rim and hand-greased rolling surfaces, would last a very very short time on a modern car before bursting into flames or fracturing into a fast flying cloud of sharp and pointy shards. 

Wooden wheels on a hummer

Although you can always count on someone, somewhere, to make it work

 

It would take quite a bit of work to fit a modern car wheel to a pedal bicycle, what with altering the frame to fit and adapting it to take the chain. And it would be heavy to move and awkward to steer and nearly impossible to repair by the side of the cyclepath. 

Attaching a bicycle wheel to each leg of my chair would certainly up the speed at which I zoom up and down the office cubical aisles, but the stability of my chair, its ergonomics and my typing rate are all also likely to interfere with my productivity. And health and safety might want a word. 

Building our own wheel

When it comes to a specific application – a wheeled horse for a hard-surfaced course perhaps – we should indeed look around and see what other wheels exist and the various wheel building techniques. 

May be there is one we can buy off the shelf that will work just as we like, but even so we will have to learn enough about wheels to properly evaluate these shelved ones against our requirements. And we need to ensure that the wheel can actually be supplied in time, that instructions for its installation and maintenance are understood, and that parts will be available as appropriate. 

If there does not appear to be an exact fit, we may be able to modify an existing wheel. This too means understanding not only general things about wheels, and establishing the same assurances of documentation and supply, but some very specific ones about that specific one; will the material take a weld for the hoofals? If we bore holes to take bolts what change will it make to the structural strength? What help will we get in doing this – if any? 

Where we want more control over the build our wheel, we can still re-use other components. We can build our wheel so it takes one of the standard motorcycle tyres for example, so that the horse can corner properly. 

And in any case, we can use modern tools – such as plasma laser lathes – that make the build time for specialised components very short and the quality very high, even where we are building our own specialised bespoke (heh) components. 

So, let’s not reinvent the wheel

When it comes to building a solution we need to establish and re-use skills and methods and tools. Even without re-inventing the wheel, we can and often should build our own wheel from various existing components where appropriate, but otherwise happily manufacture our own special components to create a wheel that will do the job.

h1

Dependency Inversion is really Dependency Inflation

September 26, 2009

This article (by Richard Martin?) looks like a good introduction to Dependency Inversion, which appears to be mostly an attempt to reduce downward dependencies (the so-called Traditional Design Approach).

As far as I can tell though, it does not really invert dependencies; you don’t end up with your low level I/O library depending on your business logic. Rather it decouples components, making both ‘high’ and ‘low’ level libraries depend on some abstract/interface library, or a runtime configuration that defines the assembly.

So, Dependency Inversion advocates creating an interface and implementation for all public classes. But it seems to me this is blindly following the ‘decoupling’ principle so far that it stops helping in practice and starts to hurt.

Where do package interfaces go?

For a start, where does the abstracted interface go? If the interface goes in the higher level library (so that it defines what it expects from the implementation), then your low level utility depends on business logic, and this is Bad – you can’t reuse it in another part of the business. It would be foolish if your TCP/IP library had to implement an interface defined in the invoicing part of your code.

If you put the interface in the utility library, then if you replace the library with another one your interface is going to be replaced by the one in the new library – with its own package name and potential differences. You might as well just use the implementation class and not have to create a redundant interface and factory class.

So interfaces really shouldn’t go in the higher library, and they’re redundant in the utility library.

To implement proper decoupling you need adaptors or façades between each coupling (see commons-logging, Java’s XML parser, etc) which gives you more code and packages to maintain. It also obscures execution: every boundary between libraries become a barrier, as it’s not clear at the point of use which library is being called upon.

Yet the desire to decouple is supposed to reflect the desire to make maintenance easier.

But coupling is… good…

Or rather, a library with a straightforward public API that defines its features is good. This API gives you a contract about how to use the library. This contract is enforced by the compiler, and by all decent IDEs, which will no doubt offer extra coding help based on those contracts. By coding to those contracts, we get compile time checking, and any student on work experience can follow the code.

The main bonus for dependency inversions is so that you can swap out low level libraries and replace them with another. For example, you may want to replace your XML parser with a new one that is much faster. But few libraries can genuinely be blindly replaced in practice as they have different features – in fact it’s likely you’re swapping libraries in order to use those new features.

If you’re using the libraries through a lowest-common-denominator interface then you’ll have to work around it using ‘generic’ methods, and that usually means you’re bypassing any compile-time checks. If at runtime a different library is used, there will be ‘undefined behaviour’ when these extra features are not available. For example, ‘property’ switches in the early Java XML libraries could be used to switch validation on and off, but some libraries allowed this and some did not, and some required different properties to do the same job.

In that case we end up with dependencies not only on the common abstracted interface, but also several implementation libraries, and on the mechanisms of deployment, to include extra checks that the right libraries are included.

Instead I much prefer early-defined, specific, suitable, easy, compile-time checks; they’re the easy first-stage check that my code is sound, they help my IDE help me, they help those who review my code and, most importantly, those who use it for real.

Runtime applications

By coupling our components through extra interface/factory packages, we can assemble different applications at runtime, rather than having to compile a particular assembly first. This might be done through a configuration file (picocontainer) or ‘automagically’ by putting the right implementations on the classpath (commons-logging). This gives you tremendous flexibility at runtime, allowing you to specify, say, faster or more robust libraries as required at various customer sites.

On the other hand, the first requires learning the format for a secial configuration file, editing it, validating it and controlling it. Which is, essentially, just more programming. The second can make classpath set ups and debugging the most frustrating nightmare – and on-site too.

Case – Commons logging

For example, commons-logging was created (I gather) to solve the problem where low level libraries used different logging mechanisms such as log4j or JLog or System.out or some bespoke one.

“If only all library writers used commons logging”, the reasoning goes, “then it would forward log messages to the mechanism preferred by the owning application, or component container such as Tomcat”. If only All Had Obeyed. Now of course, we have some libraries writing to log4j and some to JLog and some to System.out and some to their own… and some to commons-logging. And commons-logging gets confused about which library it’s supposed to write to, because there are several loaded.

It’s a particularly easy target because there were very few widespread logging libraries by the time it was implemented, so writing adaptors between them was not hard. If you wanted to use a library that logged to log4j in Websphere (which uses JLog) you could write a log4j handler that forwarded the messages to JLog. And now that JDK 1.4 includes a logging library, you could argue that all low level libraries should use that – if, that is, you think low level libraries should be logging at all.

You can see the principle behind it though; by having a single common adaptor, we could move our libraries between applications (or our components between containers) without worrying about the logging environment. In fact though, it couldn’t solve the problem; even now some library writers use log4j over JDK 1.4 logging, despite the latter being ‘zero-effort’ to include, because log4j offers them more. Instead it made the problem worse, by adding in new runtime class loader problems as well as the config file editing required by all the logging libraries in use.

On an Extra Bonus Whinge about commons-logging; logging must be reliable and robust. Bad Things happen when starting up complex applications – especially if some of it is defined at runtime – and the log must report them if you have any hope to look even vaguely professional on site.

Randomly moving jar files from directory to directory for an unfamiliar web server between mute crashes because the logger has failed is frustrating enough, but doing so sat in a customer’s office is an experience I do not want to repeat.

Conclusion

Where you have a well-defined requirement for different components to be used in different runtime environments, a configurable factory makes a lot of sense. I, however, would recommend that that factory be specific to task rather than a generic one like picocontainer, because with picocontainer you lose type constraints and early failures become late failures.

Swapping between libraries that share the same API is fairly rare; people swap to use new features. Application-wide search-and-replace on library-using code is straightforward with modern IDEs. When writing a library, define your API well with as much contractual information as possible (typing, named attributes, etc), and as little exposure to the workings as possible. This reduces effort writing, reviewing, debugging and maintaining code, and in deploying applications, that overwhelm occasional library changes.

All this applies equally well, of course, to services…

Related

See also IBM Developer Works Quality Buster Customising Applications, Think Again, and for a rude rant the Bile Blog

[An old article from my boring old site, brought to the blogosphere for fame and fortune]

h1

Singletons Are Good

September 26, 2009

Some articles (at picocontainer, MSDN and others) bemoan the Singleton pattern as Bad Design (a so-called Antipattern) because it is ”global” and therefore it causes ”tight coupling”.

Singletons are indeed usually globally available – that’s often what they’re for. But assuming ‘global is bad’ misses some good uses.

Singleton is indeed bad when it is a single global point for all components to find all global things. You can find then that the Singleton uses types or interfaces defined in component packages, and in turn these packages make use of the Singleton. So your packages, or components, depend on the Singleton which in turn depends on other packages… so all your components are interdependant through the singleton and you can’t decouple anything from anything else. This only Might Be Bad in your project, but is Always Bad in large software systems.

Singleton is also bad when used to bypass structured and/or object oriented programming; a Singleton might be used to hold data for a task instead of using a data-wrapping object and passing that as an argument. It might be used to shortcut data transfer around layers of a system. Again, this only Might Be Bad in a small prototype program, but is Always Bad in large software systems as they can make applications a nightmare to debug and update.

Banning Singletons altogether stops these abuses but also loses the benefits, which can be convenience, simple and obvious code, and easy maintenance.

Even the pico guys say that Logging is an exception, but exceptions are sneaky; they can either be one that ‘proves the rule’ because it really is exceptional, or one that demonstrates the rule is pants. In fact logging isn’t a special case; it is merely a centralised – and global – route for monitoring a particular kind of event.

Other messaging hubs – such as UI event queues – are similar. Configuration too benefits from a single class so that you can have one central place to configure your application. Pool factories necessarily require a Singleton (although Pooling is normally bad).

We want to centralise these tasks, but each one is a separate special to purpose Singleton; they are not combined into one single global Singleton. Which is how Singletons ought to be used. Note that in all these cases the Singleton implementation is ‘standalone’; our components depend on a particular specialised Singleton and its package, but the Singleton does not depend on our components or other ‘high level’ components.

This loose-coupling is best seen in message-passing hubs that tend to be Singletons (such as logging). The hub package must include whatever interfaces it requires for its message objects (or events), and these are implemented by whatever components use the hub package, either as senders or listeners

(Just trying out blog; this blog was cut and paste from web pages here)