Design Pattern: Decorator Pattern

Decorator Pattern

1) Motivation:

    Extending an object's functionality can be done statically (at compile time) by using inherientence. However it might be necessary to extend an object's functionality dynamically(at runtime) as an object is used.

    Consider the typical example of a graphical window. To extend the functionality of a graphical window, for example by adding a frame to the window, would require extending the window class to create a FrameWindow class. To create a FrameWindow, it is necessary to create an object of FrameWindow class. However, it would be impossible to start with a plain window and to extend its functionality at runtime to become a framed window.

2) Intent:

    The intent of this pattern is to add additional responsibilty dynamically(at runtime) to an object.

3) Implementation:


    1> Component:

            Interface for objects that can have responsibilities added to them dynamically.

    2> ConcreteComponent:

            Defines an object to which additional responsibilites can be added.

    3> Decorator:

            Maintains a reference to a Component object and defines an interface that conforms to Component's interface.

    4> ConcreteDecorator:

            Extends the functionality of the Component by adding state or adding behavior. 

4) Description:

    The decorator pattern applies when there is a need to dynamically add as well as remove responsibilites to a class, and when subclassing would be impossible due to the large number of subclasses that could result.

5) Applicability & Examples:

    Example: Extending capabilities of a Graphical Window at runtime



package edu.xmu.oop.decorator;

public interface Window {
    public void renderWindow();
}
package edu.xmu.oop.decorator;

public class SimpleWindow implements Window {
    public void renderWindow() {
	System.out.println(String.format("Start render for: [%s]", this));
	System.out.println(String.format("Finished render for: [%s]", this));
    }
}
package edu.xmu.oop.decorator;

public class DecoratedWindow implements Window {
    private Window windowReference;

    public DecoratedWindow(Window windowReference) {
	super();
	this.windowReference = windowReference;
    }

    public void renderWindow() {
	windowReference.renderWindow();
    }
}
package edu.xmu.oop.decorator;

public class ScrollableWindow extends DecoratedWindow {

    private String scrollBarObjectRepresentation;

    public ScrollableWindow(Window windowReference,
	    String scrollBarObjectRepresentation) {
	super(windowReference);
	this.scrollBarObjectRepresentation = scrollBarObjectRepresentation;
    }

    @Override
    public void renderWindow() {
	renderScrollBar();
	super.renderWindow();
    }

    private void renderScrollBar() {
	System.out.println(String.format("Start renderScrollBar for: [%s]",
		this));
	System.out.println(scrollBarObjectRepresentation);
	System.out.println(String.format("Finished renderScrollBar for: [%s]",
		this));
    }
}
package edu.xmu.oop.decorator;

public class FramableWindow extends DecoratedWindow {

    public FramableWindow(Window windowReference) {
	super(windowReference);
    }

    @Override
    public void renderWindow() {
	addFrame();
	super.renderWindow();
    }

    private void addFrame() {
	System.out.println(String.format("Start addFrame for: [%s]", this));
	System.out.println(String.format("Finished addFrame for: [%s]", this));
    }
}
package edu.xmu.oop.decorator;

import org.junit.Test;

public class WindowTest {
    private Window window;

    @Test
    public void renderWindowTest() {
	window = new SimpleWindow();
	window.renderWindow();
	System.out.println("======================================");

	window = new FramableWindow(window);
	window.renderWindow();
	System.out.println("======================================");

	window = new ScrollableWindow(window, "ScrollBar");
	window.renderWindow();
    }
}

    Example: Java I/O Stream

package edu.xmu.oop.decorator;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.LineNumberInputStream;

import org.junit.Test;

@SuppressWarnings("deprecation")
public class InputStreamTest {
    @Test
    public void testRead() throws IOException {
	LineNumberInputStream inputStream = new LineNumberInputStream(
		new BufferedInputStream(new FileInputStream(new File(
			"src/test/resources/test.dat"))));
	while (-1 != inputStream.read()) {
	    System.out.println(inputStream.getLineNumber());
	}
	inputStream.close();
    }
}

6) Related Patterns:

    1> Adapter Pattern: A decorator is different from an adapter in that a Decorator changes object's responsibilites, while an Adapter changes object interfaces.

    2> Composite Pattern: A decorator can be viewed as a degenerate composite with only one component. However, a decorator adds additional responsibilites.

7) Consequences:

    1> Decoration is more convenient for adding functionalities to objects instead of entire class at runtime. With Decoration it is also possible to remove the added functionalities dynamically.

    2> Decoration added functionalities to object at runtime which would make debugging system functionality harder which just like Proxy Pattern.

Reference Links:

1) http://www.oodesign.com/decorator-pattern.html

2) http://en.wikipedia.org/wiki/Decorator_pattern

3) http://stackoverflow.com/questions/3068912/what-is-the-most-used-pattern-in-java-io

4) http://oreilly.com/catalog/hfdesignpat/chapter/ch03.pdf

猜你喜欢

转载自davyjones2010.iteye.com/blog/2088775