Decorator Pattern to Solve Integration Scenarios in Existing Systems
This article provides an example of how the Java Decorator Pattern helps create designs from scratch and fix integration problems in existing systems.
Join the DZone community and get the full member experience.Join For Free
When we are in the learning phase of some technology, we always find the cleanest scenarios and examples in books and tutorials. It is perfectly logical because then we have to grasp the very core of the concepts involved, and we don't want to be confused by the imperfections coming from the real world.
When we make the first steps in the actual implementations of such learned material we always face some discomfort. Reality is always dirtier than our imagination.
In this article, we present a scenario inspired by a real use case that we keep totally abstract in the description. The Decorator Pattern has been used for this use case to make things easier in the context of integrating an external service into an existing system.
The general scenario is this: we have a complex system composed of several different sub-systems. Among the sub-systems, one of them plays a central role, and we may refer to it as MainSystem. In order to perform one of its many features, the main system must perform a number of queries and eventually post some data to another sub-system that represents a sort of public registry, which we can call simply TargetSystem.
This information flow is done by calling an API made of SOAP services exposed by TargetSystem both for querying and storing data. This flow involves a high number of different points of execution in the main system, and the related code is hugely convoluted, to the point we might name it, without the fear of being too harsh, "spaghetti code".
Suddenly a new requirement dictates that the information pushed to the target system must be first compared to that returned by a REST service exposed by an external server that we choose to name, with great lack of imagination, ExternalSystem.
A first choice to implement the use case would be to introduce the integration logic in all the involved parts of the main application, but we want to avoid cluttering the existing code, which is already messed up by itself. To communicate with the target system a Java SOAP proxy generated from a WSDL definition is used, which we name here TargetSystemProxy.
This proxy is exposed by a singleton factory, a very lucky case to solve our problem. Thanks to this we can use a decorator pattern to make a wrapper of the SOAP proxy and provide a specialized version of the method involved in MainSystem and TargetSystem to post the data, making the factory return the wrapper and minimizing the impact on the existing code.
In the following section, we provide a brief summary of the decorator pattern before going on with the solution applied to the real case.
Decorator Pattern Overview
Java language, like all the Object Oriented languages, is based on the concept of inheritance. We can specialize classes using the extension feature and that is a nice way to reuse code. There is a drawback though: it is a very static way. It is more suitable for writing common libraries and frameworks, but when it comes to designing and implementing new systems for targeting specific requirements on the market suffers a great lack of flexibility.
An alternative to the static extension is provided by a Java design pattern named Delegation. Delegation simply means that we can extend a class simply using the features of another class by its instance without any extension. Delegation is more dynamic as we are not forced to use the extension syntax and the resulting rigid coupling of the two classes. The delegated instance can be provided by some factory and extend some abstract class or implement some interface known to the delegating class.
But to take things further, we can devise a way to use delegation and at the same time replace the delegating class instances with the delegated ones wherever they are used. This is made possible by the Decorator Pattern. The following picture shows the basic idea.
The Decorator Pattern mixes delegation and ordinary extension mechanisms to provide a nice way to extend the existing functionality of a class. We can define an abstract class AbstractDecorator that extends the decorated class and at the same time uses an instance of it to implement the non-abstract methods.
The abstract methods are then implemented by an extension of AbstractDecorator. These implementations define the specialized logic and since the decorator class is derived by the decorated one an instance of it can be assigned to any variable declared with the decorated type.
Applying the Decorator Pattern to the Real Case
Following the description of the previous paragraph, we implement the decorator pattern for the case described in this article defining an abstract extension of the TargetSystemProxy class that implements its non-abstract methods using an instance of it (i.e. by delegation).
This abstract class defines a single abstract method named push that overrides the method with the same name of the parent class. The push method has the responsibility to post the data to the target system and we choose it as the entry point for the required customization.
We then define an extension of the abstract class that implements the abstract push method. You can see this design in the following picture.
How the Main System Can Get a Proxy Decorator Instance
Given the fact that there is already a factory serving a TargetSystemProxy to the main system, we customize that factory to provide a TargetSystemProxyDecorator instance instead. This does not break the existing code in any way because the decorator class extends the TargetProxy and so its type is fully compatible.
Description of the Whole Interaction
To implement the whole use case the main system gets first a TargetSystemProxyDecorator instance from the factory. Then the whole process can start: the main system calls the push method of the proxy decorator which contains all the logic related to the integration with the external system.
The push method implementation queries the external system, merge its response to the existing payload, and eventually calls the method of the original proxy instance with the updated data. The original proxy as usual calls the SOAP push service of the target system which performs the actual storing.
The basic steps of this interaction are shown in the following picture:
This way, all the required work to perform the integration consists of creating the decorator and customizing the existing factory formerly used to get the original class instance: we don't have to touch other parts of the existing software.
Sometimes we are faced with systems already in production for a long time and flawed by design errors and maybe obsolete technologies and badly organized code. The classical Java design patterns can be used to some extent to alleviate the burden. This is especially true when new requirements come into place under a limited budget. The decorator pattern in these cases can play the role of a sharp instrument, fitted to implement new required extensions with a minimal impact.
Opinions expressed by DZone contributors are their own.