티스토리 뷰

Programing/DesignPattern

Decorator 패턴

오통 2016. 8. 17. 21:44

정의

  • 기존 상속구조에서 부모 코드를 변경하지 않고, 런타임에 동적으로 기능을 추가하기 위한 pattern





  • 예제



    문제상황

    먼저 coffee 가게에서 각 음료의 가격을 알 수 있는 system을 개발한다고 할 때,
    일반적인 설계구조를 본 뒤, Decorator pattern을 적용하여 개선해 보자.

    1. HouseBlend, DarkRoast, Decaf, Espresso 의 가격을 알 수 있는 class 설계

                  


    2. 각 음료에 mocha, milk, wheeping 등 재료가 추가 될 수 있으므로 상위 클래스에 관련 기능 추가.



    위 설계의 문제점?
    • 재료가 추가되면 부모클래스(Beverage)에 method가 계속 추가됨.
    • 재료가 변경되거나, 가격이 변경되면 부모클래스(Beverage) 수정 필요함.
    • 절대 사용하지 않을 재료의 method도 모든 음료가 상속받게 됨.

    위 설계는 중요한 디자인 원칙인 OCP(Open-Closed Principle) 에 적합하지 않음.
    클래스는 확장에 대해서는 열려 있어야 하지만, 코드 변경에 있어서는 닫혀 있어야 한다.
    즉, 기존 코드는 수정하지 않고, 기능을 확장할 수 있는 설계.


    개선방법


    각각의 재료를 Decorator class로 빼보자!


     


    • 부모 클래스(Beverage)에 존재하던 유동적인 재료 부분을 모두 각각의 class로 뺐으므로  재료에 대한 정보가 변경되더라도 기존 부모클래스를 수정할 필요 없음. 재료 class를 수정하거나 추가하면 됨.. OCP을 따름.


    
    // 최상위 추상클래스인 Beverage
    public abstract class Beverage{
        public abstract int cost(); 
    }
    
    public class HouseBlend extends Beverage{
        public int cost() {
            return 3000;
        }
    }
    
    public class DarkRoast extends Beverage{
        public int cost() {
            return 2000;
        }
    }
    
    public class Decaf extends Beverage{
        public int cost() {
            return 1000;
        }
    }
    
    public class Espresso extends Beverage{
        public int cost() {
            return 500;
        }
    }
    
    
    // decorator class 는 다시 Decorator를 가질 수 있음.
    public abstract Decorator extends Beverage{
        private Beverage beverage;
        public int cost() {
           beverage.cost();
        }
    }
    
    public class Whip extends Decorator{
        @overriding
        public int cost() {
            return 30 + super.cost();
        }
    }
    
    public class Mlik extends Decorator{
        @overriding
        public int cost() {
            return 20 + super.cost();
        }
    }
    
    public class Soy extends Decorator{
        @overriding
        public int cost() {
            return 10 + super.cost();
        }
    }
    
    public class Mocha extends Decorator{
        @overriding
        public int cost() {
            return 5 + super.cost();
        }
    }
    
    
    public class Main{
        public static void main(String[] args) {
            Beverage espresso = new Espresso();
            System.out.println("espresso cost = " + espresso.cost());    // 500
    
            Beverage mochaEspresso = new Mocha(espresso);
            System.out.println("mochaEspresso cost = " + mochaEspresso.cost());    // 505
    
            Beverage whipMilkSoyHouseBlend = new Whip(new Milk(new Soy(new HouseBlend())));
            System.out.println("whipMilkSoyHouseBlendcost = " + mochaEspresso.cost());    // 3060
        }
    }
    


결론

서브클래스를 만드는 방식으로 행동을 상속받으면 그 행동은 컴파일 시에 완전히 결정(정적)되며 모든 서브클래스에서 똑같은 행동을 상속 받는다.

하지만 구성을 통해 객체의 행동을 확장하면 실행중에 동적으로 행동을 설정할수 있다.(동적) 


장점


- 기본 데이터에 첨가할 기능이 다양하고 일정하지 않을 때, 기존 code를 건드리지 않고 동적으로 기능 확장을 할 수 있으므로 용이.

 

단점


- 데코레이터 패턴을 이용해서 디자인 하다 보면 잡다한 클래스들이 많아 질 수 있다.

- 코드 가독성이 떨어진다. 구조를 이해하기 자세히 보기 전에는 이해하기 어려움.


Java에서 적용 예

java IO 입출력 에서 Decorator 패턴이 적용 되어 있다.






BufferedReader br = new BufferedReader(new FileReader(new File("test.txt")));


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함