Thursday, August 12, 2004

Medium and the message

Object orientation has been a fantastic tool for developers over the past 20 years. While it is not the only paradigm that helps create quality software, it is extremely good at managing complexity.

The fundamental part of object orientation is to stop thinking about procedures (e.g. transformation of data), and instead think about discrete objects that send and receieve messages to and from other. Like many software engineering techniques procedural and OO approaches are equivalent (they are both Turing machines after all), but object orientation leads to systems that have tend to have clearly defined responsibilities, and minimal side effects. [Note: A determined (poor) OO developer can always create a system which doesn't have these properties, as can, not suprisingly, an effective procedural developer create a system which does]

Recently, there have been moves to enhance message passing to recognise that messages do not travel directly from client to implementation. Techniques such as aspect-orientation and technologies such as object containers perform intermediation between the sender and the receiver of a message. These intermediators can be called interceptors, or aspects, or container-based services, but collectively I think of them as the medium for the message.

The medium can manage all sorts of contextual information. A given message may be rerouted to a remote location. It might be logged, or it might force a new database transaction. The possibilities of medium-based systems are boundless, but generally they are there to allow design considerations to be tuned at a stage closer to deployment.

Example:



class Client {
public Client( Service service){
this.service = service
}

public void doClientThang(){
service.doServiceThang();
}
}

interface Service {
public void doServiceThang();
}

class ServiceImpl implements Service {
public ServiceImpl( Calculation c ){
this.calculation = c;
}
public void doServiceThang(){
calculation.doCalculation();
}
}

The above example illustrates a simple message between client and service.
The service is injected (using constructor based dependency injection), but could just as easily been retrieved using JNDI or some other factory.
The service implementation performs some calculation, but this is really a place holder for some resource intensive task.

At this level, we have made no architectural design decisions, except to say that the boundary of architectural change is at the Client<->Service interface, and at the Service<->Calculation interface.

When it comes to deployment, we are now free to inject alternative implementations of Service into Client (and Calculation into Service). At some point, however, we will trust that the business logic in Service is sound (it has passed all tests and been approved by the customer). In this case, we don't want to inject alternative business logic, but we may want to intercept messages to the business logic to support changes in infrastructure. If we do this, we are changing the medium of the message.