AspectJ を使ったデザインパターンの改善と支援 >
Decorator

最終更新日 : 2003/8/28
asato <asato@ncfreak.com>

index > decorator

オブジェクト Decorator

意図: 動的にオブジェクトに責任を追加する。

サンプルコード (実装例 1):

public class StringComponent {

	private String str;

	public StringComponent(String str) {
		this.str = str;
	}

	public String getString() {
		return str;
	}
}
public class ConcreteDecoratorA {

	public String getString() {

		return "[ " + getComponent().getString() + " ]";
	}
}
public class ConcreteDecoratorB {

	public String getString() {

		return "*** " + getComponent().getString() + " ***";
	}
}
public class Client {

	public static void main(String[] args) {

		StringComponent comp = new StringComponent("aaa");

		System.out.println( comp.getString() ); // aaa


		Decorator.decorate(comp, new ConcreteDecoratorA() );

		System.out.println( comp.getString() ); // [ aaa ]


		Decorator.decorate(comp, new ConcreteDecoratorB() );

		System.out.println( comp.getString() ); // *** [ aaa ] ***
	}
}
public class Decorator { }
public aspect DecoratorAspect {

	public static void Decorator.decorate(
		StringComponent component,
		StringComponentAspect.StringComponentDecorator decorator)
	{
		StringComponentAspect.aspectOf().decorate(component, decorator);
	}
}
リスト. DecoratorProtocol (簡単のため、いくつかのメソッド (undecorate, undecorateAll) の実装を省いている)
import java.util.Map;
import java.util.HashMap;

public abstract aspect DecoratorProtocol {

	private Map decorationMap = new HashMap();
	private Map pairMap       = new HashMap();


	protected interface Component { }
	protected interface Decorator { }


	pointcut componentObj(Component component) :
		target(component) && !within(Decorator);


	protected void decorate(Object component, Object decorator) {

		if ( getDecorator(component) == null) {
			pairMap.put(decorator, component);
		} else {
			pairMap.put( decorator, getDecorator(component) );
		}

		decorationMap.put(component, decorator);
	}

	protected Object getComponent(Object decorator) {
		return pairMap.get(decorator);
	}

	protected Object getDecorator(Object component) {
		return decorationMap.get(component);
	}
}
public aspect StringComponentAspect extends DecoratorProtocol {

	declare parents : StringComponent            implements Component;
	declare parents : StringComponentDecorator implements Decorator;

	declare parents : StringComponent            implements StringComponentInter;
	declare parents : StringComponentDecorator implements StringComponentInter;

	declare parents : ConcreteDecoratorA implements StringComponentDecorator;
	declare parents : ConcreteDecoratorB implements StringComponentDecorator;


	protected interface StringComponentInter { }
	protected interface StringComponentDecorator { }


	public StringComponentInter StringComponentDecorator.getComponent() {
		return StringComponentAspect.aspectOf().getComponent( this );
	}


	public abstract String StringComponentInter.getString();


	pointcut decoratedComponent(Component component) :
		componentObj(component) && if ( isDecorated(component) );


	pointcut getString(Component component) :
		decoratedComponent(component) &&
		call( String StringComponent.getString() );

	Object around(Component component) : getString(component) {
		return getDecorator(component).getString();
	}


	private static boolean isDecorated(Object component) {
		return StringComponentAspect.aspectOf().getDecorator(component) != null;
	}

	private StringComponentInter getComponent(Decorator decorator) {
		return (StringComponentInter)super.getComponent(decorator);
	}

	private StringComponentDecorator getDecorator(Component component) {
		return (StringComponentDecorator)super.getDecorator(component);
	}
}
サンプルコード (実装例 2): AspectJ バージョン 1.1beta4 で動作確認
public class Decorator { }
public aspect DecoratorAspect {

	public static void Decorator.decorate(
		StringComponent component,
		StringComponentAspect.StringComponentDecorator decorator)
	{

		StringComponentAspect.aspectOf(component).decorate(component, decorator);
	}
}
public abstract aspect DecoratorProtocol pertarget( componentTarget() ) {

	abstract pointcut componentTarget();

	declare parents: Decorator implements Component;

	protected interface Component { }
	protected interface Decorator { }

	Component Decorator.component;
	Decorator Component.decorator;

	boolean Component.isDecorated() {
		return decorator != null;
	}

	protected void decorate(Component component, Decorator decorator) {
		if ( component.isDecorated() ) {
			decorator.component = component.decorator;
		} else {
			decorator.component = component;
		}

		component.decorator = decorator;
	}

	protected pointcut decoratedComponent(Component component) :
		target(component) &&
		!within(Decorator) &&
		if ( component.isDecorated() );
}
public aspect StringComponentAspect extends DecoratorProtocol {

	pointcut componentTarget() : target(StringComponent);

	declare parents: StringComponentInter implements Component;
	declare parents: StringComponentInter implements Decorator;

	declare parents: StringComponent            implements StringComponentInter;
	declare parents: StringComponentDecorator implements StringComponentInter;

	declare parents: ConcreteDecoratorA implements StringComponentDecorator;
	declare parents: ConcreteDecoratorB implements StringComponentDecorator;


	protected interface StringComponentInter { }
	protected interface StringComponentDecorator { }


	public abstract String StringComponentInter.getString();

	public StringComponentInter StringComponentInter.getComponent() {
		return (StringComponentInter)component;
	}


	pointcut getString(Component component) :
		decoratedComponent(component) &&
		call( String StringComponent.getString() );

	Object around(Component component) : getString(component) {
		return getDecorator(component).getString();
	}

	public void decorate(StringComponent component, StringComponentDecorator decorator) {
		super.decorate(component, decorator);
	}

	private StringComponentDecorator getDecorator(Component component) {
		return (StringComponentDecorator)component.decorator;
	}
}

実装上の選択肢

クライアントから見た場合、どのようにして component オブジェクトを装飾するかのインタフェースの実装方法には、二通りの方法が考えられる: 後者の場合、前者の場合と違って装飾後のオブジェクトを戻り値としてそのまま返せるため (引数として渡された component オブジェクトと返り値として返される component オブジェクトは同一のもの)、従来の Decorator パターンと同じような使い方ができる:
	StringComponent comp1 = new StringComponent("aaa");
	StringComponent comp2 =
		new ConcreteDecoratorB().decorate( new ConcreteDecoratorA().decorate(comp1) );
		
	System.out.println(comp1 == comp2); // true

実際には ConcreteDecoratorA.decorate(comp)Decorator.decorate(comp, ConcreteDecoratorA.class ) なども考えられる。あるいは component 側から見た実装も考えられる: comp.decorate( new ConcreteDecoratorA() )comp.decorate(ConcreteDecoratorA.class) など。

実装 - その 3

段階 - その 1

public interface StringComponentInterface {
	public String getString();
}
public class StringComponent {

	private String str;

	public StringComponent(String str) {
		this.str = str;
	}

	public String getString() {
		return str;
	}
}
public aspect StringComponentDecorator {

	declare parents: StringComponent implements StringComponentInterface;

	public StringComponentInterface StringComponentInterface.decorator;


	String around(StringComponent comp) :
		target(comp) &&
		call( String StringComponent.getString() ) && 
		if (comp.decorator != null) &&
		!within(StringComponentInterface+)
	{

		StringComponentInterface temp      = comp.decorator;
		StringComponentInterface decorator = comp.decorator;

		while(temp != null) {
			decorator = temp;
			temp = temp.decorator;
		}

		return decorator.getString();
	}
}
public aspect ConcreteDecoratorA {

	public static void decorate(StringComponentInterface comp) {

		if (comp.decorator != null) {
			decorate(comp.decorator);
			return;
		}
		comp.decorator = new ConcreteDecoratorA.Decorator(comp);
	}


	private static class Decorator implements StringComponentInterface {

		private StringComponentInterface comp;

		public Decorator(StringComponentInterface comp) {
			this.comp = comp;
		}

		public String getString() {
			return "[[[ " + comp.getString() + " ]]]";
		}
	}
}
public aspect ConcreteDecoratorB {

	public static void decorate(StringComponentInterface comp) {

		if (comp.decorator != null) {
			decorate(comp.decorator);
			return;
		}
		comp.decorator = new ConcreteDecoratorB.Decorator(comp);
	}

	private static class Decorator implements StringComponentInterface {

		private StringComponentInterface comp;
	
		public Decorator(StringComponentInterface comp) {
			this.comp = comp;
		}
	
		public String getString() {
			return "*** " + comp.getString() + " ***";
		}
	}
}
public class Client {

	public static void main(String[] args) {

		StringComponent comp = new StringComponent("aaa");

		System.out.println( comp.getString() );


		ConcreteDecoratorA.decorate(comp);

		System.out.println( comp.getString() ); // [[[ aaa ]]]


		ConcreteDecoratorB.decorate(comp);

		System.out.println( comp.getString() ); // *** [[[ aaa ]]] ***
	}
}

段階 - その 2

public class StringComponent {

	private String str;

	public StringComponent(String str) {
		this.str = str;
	}

	public String getString() {
		return str;
	}
}
public interface StringComponentInterface {
	public String getString();
}
public aspect StringComponentAspect {

	declare parents: StringComponent implements StringComponentInterface;

	public StringComponentInterface StringComponentInterface.decorator;


	String around(StringComponent comp) :
		target(comp) &&
		call( String StringComponent.getString() )
		&& if (comp.decorator != null)
	{

		StringComponentInterface decorator = comp.decorator;

		while(decorator.decorator != null) {
			decorator = decorator.decorator;
		}

		return decorator.getString();
	}
}
public abstract class StringComponentDecorator implements StringComponentInterface {

	protected StringComponentInterface comp;

	protected StringComponentDecorator(StringComponentInterface comp) {
		this.comp = comp;
	}
	
	public abstract String getString();
}
public class ConcreteDecoratorA extends StringComponentDecorator {

	public ConcreteDecoratorA(StringComponentInterface comp) {
		super(comp);
	}

	public static void decorate(StringComponentInterface comp) {
	
		if (comp.decorator != null) {
			decorate(comp.decorator);

		} else {
			comp.decorator = new ConcreteDecoratorA(comp);
		}
	}

	public String getString() {
		return "[[[ " + comp.getString() + " ]]]";
	}
}
public class ConcreteDecoratorB extends StringComponentDecorator {

	public ConcreteDecoratorB(StringComponentInterface comp) {
		super(comp);
	}

	public static void decorate(StringComponentInterface comp) {

		if (comp.decorator != null) {
			decorate(comp.decorator);

		} else{
			comp.decorator = new ConcreteDecoratorB(comp);
		}
	}

	public String getString() {
		return "*** " + comp.getString() + " ***";
	}
}
public class Client {

	public static void main(String[] args) {

		StringComponent comp = new StringComponent("aaa");

		System.out.println( comp.getString() );


		ConcreteDecoratorA.decorate(comp);

		System.out.println( comp.getString() ); // [[[ aaa ]]]


		ConcreteDecoratorB.decorate(comp);

		System.out.println( comp.getString() ); // *** [[[ aaa ]]] ***
	}
}

段階 - その 3 : メソッドが 1 つ以上の場合

public class StringComponent {

	private String str;

	public StringComponent(String str) {
		this.str = str;
	}

	public String getString() {
		return str;
	}
	public String getString2() {
		return str;
	}
}
public interface StringComponentInterface {
	public String getString();
	public String getString2();
}
public aspect StringComponentAspect {

	declare parents: StringComponent implements StringComponentInterface;

	public StringComponentInterface StringComponentInterface.decorator;


	pointcut component(StringComponent comp) : 
		target(comp) && if (comp.decorator != null);

	String around(StringComponent comp) :
		component(comp) && call( String StringComponent.getString() )
	{
		return getDecorator(comp).getString();
	}

	String around(StringComponent comp) :
		component(comp) && call( String StringComponent.getString2() )
	{
		return getDecorator(comp).getString2();
	}


	private StringComponentInterface getDecorator(StringComponent comp) {

		StringComponentInterface decorator = comp.decorator;

		while(decorator.decorator != null) {
			decorator = decorator.decorator;
		}

		return decorator;
	}
}
public abstract class StringComponentDecorator implements StringComponentInterface {

	protected StringComponentInterface comp;

	protected StringComponentDecorator(StringComponentInterface comp) {
		this.comp = comp;
	}
	public abstract String getString();
	public abstract String getString2();
}
public class ConcreteDecoratorA extends StringComponentDecorator {

	// ... 前と同じ

	public String getString() {
		return "[[[ " + comp.getString() + " ]]]";
	}
	public String getString2() {
		return "+++ " + comp.getString2() + " +++";
	}
}
public class ConcreteDecoratorB extends StringComponentDecorator {

	// ... 前と同じ

	public String getString() {
		return "*** " + comp.getString() + " ***";
	}

	public String getString2() {
		return "--- " + comp.getString2() + " ---";
	}
}
public class Client {

	public static void main(String[] args) {

		StringComponent comp = new StringComponent("aaa");

		System.out.println( comp.getString() );  // aaa
		System.out.println( comp.getString2() ); // aaa


		ConcreteDecoratorA.decorate(comp);

		System.out.println( comp.getString() );  // [[[ aaa ]]]
		System.out.println( comp.getString2() ); // +++ aaa +++


		ConcreteDecoratorB.decorate(comp);

		System.out.println( comp.getString() );  // *** [[[ aaa ]]] ***
		System.out.println( comp.getString2() ); // --- +++ aaa +++ ---
	}
}

実装 - その 4

public class StringComponent {

	private String str;

	public StringComponent(String str) {
		this.str = str;
	}

	public String getString() {
		return str;
	}
}
public interface StringComponentInterface {

	public String getString();
}
public class Client {

	public static void main(String[] args) {

		StringComponent comp = new StringComponent("xxx");
		
		System.out.println( comp.getString() ); // "xxx"
		
		StringComponentDecoratorAspect.decorate(comp);

		System.out.println( comp.getString() ); // "[ xxx ]"


		StringComponentDecoratorAspect.decorate(comp);

		System.out.println( comp.getString() ); // "[ [ xxx ] ]"
	}
}
public aspect StringComponentDecoratorAspect {

	declare parents: StringComponent implements StringComponentInterface;

	private boolean StringComponentInterface.isDecorated;
	private StringComponentInterface StringComponentInterface.decorator;

	public static void decorate(StringComponentInterface comp) {
		if (comp.decorator == null) {
			comp.isDecorated = true;
		} else {
			comp.decorator.isDecorated = true;
		}
	}
	
	Object around(final StringComponentInterface comp) :
		target(comp) && 
		call( String StringComponentInterface.getString() ) &&
		if (comp.isDecorated)
	{
		if (comp.decorator == null) {
			StringComponentInterface decorator = new StringComponentInterface() {
				public String getString() {
					return "[ " + proceed(comp) + " ]";
				}
			};

			comp.decorator = decorator;
		} 

		return comp.decorator.getString();
	}
}

更新履歴

todo

index > decorator