Gang of Four Structural pattern: Decorator
Structural Pattern
Add responsibilities to objects dynamically. Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
The Decorator pattern is useful when some data needs to be transformed several times by different plugable transformers.
A Decorator can transform the data and then call another Decorator it is chained to, to do some further processing on the data. When the data has been transformed by all the decorators it is returned.
To get the code for this example:
git clone https://github.com/spotadev/gangoffour.git
In src/main/java navigate to this package:
com.javaspeak.designpatterns.go4.structural.decorator
You can run the code from the main method of:
DecoratorApplication
In our example we have 2 decorators. One decorator transforms the text to upper case and the other decorator replaces multiple spaces with one space.
Each Decorator (transformer) holds a reference to another Decorator unless it is the last Decorator in the chain.
package com.javaspeak.designpatterns.go4.structural.decorator;
/**
* Text book description:
* <ul>
* "Decorator: Add responsibilities to objects dynamically. Attach additional responsibilities
* to an object dynamically. Decorators provide a flexible alternative to subclassing for
* extending functionality."
* </ul>
* The Decorator pattern is useful when some data needs to be transformed several times by
* different plugable transformers.
* <p>
* A Decorator can transform the data and then call another Decorator it is chained to, to do some
* further processing on the data. When the data has been transformed by all the decorators it
* is returned.
* <p>
* In our example we have 2 decorators. One decorator transforms the text to upper case and the
* other decorator replaces multiple spaces with one space.
* <p>
* Each Decorator (transformer) holds a reference to another Decorator unless it is the last
* Decorator in the chain.
* <p>
* Note that the code for the structural Decorator pattern is the same as the behavioural Chain
* of Responsibility pattern.
*
* @see com.javaspeak.designpatterns.go4.behavioural.chainofresponsibility.ChainOfResponsibilityApplication
*
* @author John Dickerson - 20 February 2020
*/
public class DecoratorApplication {
private Transformer<String> transformer;
public void buildDecorators() {
Transformer<String> removeMultipleSpacesTransformer = new RemoveMultipleSpacesTransformer();
transformer = new TextCapitaliseTransformer();
transformer.addDecoration( removeMultipleSpacesTransformer );
}
public void transform() {
String message = "Hello World!";
System.out.println( this.transformer.transform( message ) );
}
public static void main( String[] args ) {
DecoratorApplication application = new DecoratorApplication();
application.buildDecorators();
application.transform();
}
}
package com.javaspeak.designpatterns.go4.structural.decorator;
/**
* This interface is central to this example's Decorator pattern. This Transformer (Decorator) has
* a transform method which can transform data of type E.
* <p>
* The implementation of this Transformer interface can optionally decorate another Transformer.
* <p>
* What the decoration means is that the implementation of the transform( E input ) method should
* do its transformation on E and then if a Transformer has been previously added via its
* addDecoration( Transformer transformer ) method then it should call the transform( E input )
* method of that transformer.
* <p>
* This allows many Decorators to be chained together, each Decorator holding a reference to
* (decorating) the next Decorator. The last Decorator in the chain will not hold a reference to
* another Decorator.
*
* @author John Dickerson - 20 February 2020
*/
public interface Transformer<E> {
/**
* The implementation of this method should perform a transformation on the input. If the
* Transformer holds a reference to another Transformer it should then call transform( E input )
* on the Transformer.
*
* @param input
* The input data to transform
*
* @return data
* The transformed data
*/
public E transform( E input );
/**
* The implementation of this method needs to add a reference to the next transformer in
* the chain.
*
* @param transformer
* The next transformer in the chain.
*/
public void addDecoration( Transformer<E> transformer );
}
package com.javaspeak.designpatterns.go4.structural.decorator;
/**
* This class is a Decorator. It holds a reference to another decorator. The decorators implement
* Transformer. The reference to another decorator can be added by calling
* addDecoration( Transformer<String> transformer).
* <p>
* After calling transform on some input data it calls transform on the decorator it references.
* <p>
* In this way a chain of decorators (transformers) is created, the last transformer in the chain
* holding no reference to a decorator.
*
* @author John Dickerson - 20 February 2020
*/
public class TextCapitaliseTransformer implements Transformer<String> {
private Transformer<String> transformer;
@Override
public void addDecoration( Transformer<String> transformer ) {
this.transformer = transformer;
}
@Override
public String transform( String input ) {
String output = input.toUpperCase();
if ( this.transformer != null ) {
return this.transformer.transform( output );
}
return output;
}
}
package com.javaspeak.designpatterns.go4.structural.decorator;
/**
* This class is a Decorator. It holds a reference to another decorator. The decorators implement
* Transformer. The reference to another decorator can be added by calling
* addDecoration( Transformer<String> transformer).
* <p>
* After calling transform on some input data it calls transform on the decorator it references.
* <p>
* In this way a chain of decorators (transformers) is created, the last transformer in the chain
* holding no reference to a decorator.
*
* @author John Dickerson - 20 February 2020
*/
public class RemoveMultipleSpacesTransformer implements Transformer<String> {
private Transformer<String> transformer;
@Override
public void addDecoration( Transformer<String> transformer ) {
this.transformer = transformer;
}
@Override
public String transform( String input ) {
String output = input.replaceAll( "\\s+", " " );
if ( this.transformer != null ) {
return this.transform( output );
}
return output;
}
}
Back: Gang of Four
Page Author: JD