decorator pattern

Pattern 2011. 1. 2. 20:47
  • 개요
    • the decorator pattern is a design pattern that allows new/additional behaviour to be added to an existing object dynamically. (아래의 '구조 예'에서 새로운 추가 행동은 Sugar.getIngredient()안의 ", Sugar" 이다. 여기서는 문자열을 추가로 반환 하지만, 구현에 따라서 기능이나 비즈니스를 추가로 호출해도 된다.)
    • 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다. (클래스의 상속을 사용하면 클래스 설계시에 해당 클래스의 기능이 정적으로 정해져서 동적으로 기능을 추가할 수가 없다. 아래의 예에서는 BlackCoffee및 MilkCoffee 클래스에 동적으로 Sugar와 Cream을 추가할 수 있다. Sugar를 여러 번 BlackCoffee에 추가할 수도 있다.)
  • 구조 예



  • 소스
public interface ICoffee {
public String getIngredient();
public void description();
}
public class BlackCoffee implements ICoffee {
public void description() {
System.out.println("black coffee");

}
public String getIngredient() {
return "coffee";
}
}
public class MilkCoffee implements ICoffee {
public void description() {
System.out.println("milk coffee");
}
public String getIngredient() {
return "coffee" + ", milk";
}
}
public abstract class ACoffeeDecorator implements ICoffee {
protected ICoffee coffee = null;
public ACoffeeDecorator(ICoffee coffee)
{
this.coffee = coffee;
}
public void description() {
}
abstract public String getIngredient();
}
public class Sugar extends ACoffeeDecorator {
public Sugar(ICoffee coffee) {
super(coffee);
}
public String getIngredient() {
return super.coffee.getIngredient() + ", Sugar";
}
}
public class Cream extends ACoffeeDecorator {
public Cream(ICoffee coffee) {
super(coffee);
}
public String getIngredient() {
return super.coffee.getIngredient() + ", Cream";
}
}
public class Client {
public static void main(String[] args) {
System.out.println("--- black coffee ---");
ICoffee blackCoffee = new BlackCoffee();
blackCoffee = new Sugar(blackCoffee);
System.out.println(blackCoffee.getIngredient());

blackCoffee = new Sugar(blackCoffee);
System.out.println(blackCoffee.getIngredient());

blackCoffee = new Cream(blackCoffee);
System.out.println(blackCoffee.getIngredient());

System.out.println("--- milk coffee ---");
ICoffee milkCoffee = new Cream(new MilkCoffee());
System.out.println(milkCoffee.getIngredient());
}
}

결과 화면 :
--- black coffee ---
coffee, Sugar
coffee, Sugar, Sugar
coffee, Sugar, Sugar, Cream
--- milk coffee ---
coffee, milk, Cream
  • 구현 관련
    • Decorate 되는 클래스(BlackCoffee, MilkCoffee) 와 Decorate 하는 클래스(Sugar, Cream)가 구분 되어야 한다.
    • 최상위 클래스 or 인터페이스(ICoffee) 에서는 데이터 멤버가 없거나 최소화 하고, interface 갯수도 최소화하는 것이 좋다. 객체에 기능을 추가 할려면 많은 객체들이 사용되는데 최상위 클래스에서 데이터 멤버가 많으면 여러 객체들에서 메모리를 많이 차지하게 되고, 인터페이스가 많으면 불필요한 인터페이스에 대해 재정의 해야 한다.
    • ACoffeeDecorator 클래스 없이 ICoffe를 바로 구현하는 Sugar와 Cream을 두는 구조로 만들면, ICoffee에서는 'Decorator역할을 하는 Sugar, Cream등의 인터페이스'와 'Decorator의 대상이 되는 BlackCoffee, MilkCoffee 등의 인터페이스'를 같이 정의하므로 두가지 중 한가지 종류의 인터페이스및 멤버를 추가 하거나 확장하기에 좋지 않다. 두가지 역할을 하므로 SRP에도 어긋난다.
    • Decorator 클래스(ACoffeeDecorator)는 객체생성을 못하게 abstract class로 만들는것이 좋다.
    • ICoffee와 ACoffeeDecorator에 cost()를 두고 ICoffee와 ACoffeeDecorator를 구현하는 클래스들에서 cost()를 클래스별 가격을 반환하게 구현하면 BlackCoffee 가격에 2번 Sugar를 추가한 가격을 합산할 수 있다.
  • 차이점
    • Decorator 패턴은 객체에게 추가 기능을 위해 객체로 감싸주는 역할을 하며, Strategy 패턴은 내부 비즈니스(알고리즘)을 위임 시키기 위한 것이다.
  • 참고
    • java.io class도 데코레이터 패턴이다. InputStream in = new BufferedInputStream(new FileInputStream());

Posted by 파이팅야
,