ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Decorator Pattern
    디자인 패턴 2022. 2. 8. 15:59

    해당 책을 통해 공부한 내용을 정리하고 있습니다.


     

    OCP

     좋은 객체 지향 설계를 위한 원칙 중 SOLID라는 것이 있다. 약어들의 모임인 SRP, OCP, LSP, ISP, DIP의 약자다. 그 중에 OCP(Open closed Principle)이라는 것이 있는데 이는 클래스는 ‘확장’에는 열려있어야 하지만 코드 변경에는 ‘닫혀’있어야 한다는 원칙이다. 즉 기존 코드는 변경하지 않고 확장을 통해 변경 사항을 반영 할 수 있어야 한다는 것을 의미한다. . 코드를 변경하지 않고 변화를 만든다니 뭔가 말이 안되는 말 같은데 이를 어떤식으로 적용 할 수 있을까? 예를 들어보자면 Observer Pattern을 들 수 있다. Observer 패턴에서 옵저버를 추가하면 Subject에 코드를 추가하지 않고도 얼마든지 확장 할 수 있다. 이런식으로 설계에 OCP를 적용하면 변화하는 환경에 잘 적응되면서 강하고 튼튼한 디자인을 만들 수 있다

     


    Decorator Pattern

     OCP를 적용하는 방법 중 하나는 Decorator 패턴을 사용하는 것이다. Decorator 패턴은 물건안에 상자를 싸고 그 위에 포장지를 싸고 그 위에 선물 포장지 등을 싸는 행위를 연상하면 이해하기 편하다.

     

     예시를 들어서 커피숍에서 커피를 주문하는 상황을 생각해보자. 커피는 ‘디카페인 커피’, ‘에스프레소’, ‘프라푸치노’ 등등 기본적인 커피의 베이스에 ‘모카’, ‘우유’, ‘두유’, ‘휘핑크림’등을 추가해서 먹을 수가 있다. 여기에 데코레이터 패턴을 사용하면 편하다. 다음 그림을 보자.

     위의 그림과 같이 커피에 추가를 할 수 있는 부분을 Decorator로 나눠주고 기본의 베이스가 되는 커피들을 Beverage로 만들었다. 이렇게 설계한 시스템의 기능에 가격을 정산하는 기능과 커피의 설명을 나타내는 기능이 있다고 가정하고 메소드로 만들어 클래스를 구현해보자.

     

    package decoratorPattern;
    
    public abstract class Beverage {
        String description = "제목 없음";
    
        public String getDescription() {
            return description;
        }
    
        public abstract double cost();
    }
    package decoratorPattern;
    
    public abstract class CondimentDecorator extends Beverage{
        public abstract String getDescription();
    }
    package decoratorPattern;
    
    public class Espresso extends Beverage{
        public Espresso() {
            description = "에스프레소";
        }
    
        public double cost() {
            return 1.99;
        }
    }
    package decoratorPattern;
    
    public class Whip extends CondimentDecorator{
        Beverage beverage;
    
        public Whip(Beverage beverage) {
            this.beverage = beverage;
        }
    
        public String getDescription() {
            return beverage.getDescription() + ", 휘핑크림";
        }
    
        public double cost() {
            return .20 + beverage.cost();
        }
    }
    package decoratorPattern;
    
    public class Soy extends CondimentDecorator{
        Beverage beverage;
    
        public Soy(Beverage beverage) {
            this.beverage = beverage;
        }
    
        public String getDescription() {
            return beverage.getDescription() + ", 두유";
        }
    
        public double cost() {
            return .15 + beverage.cost();
        }
    }
    package decoratorPattern;
    
    public class SteamMilk extends CondimentDecorator{
        Beverage beverage;
    
        public SteamMilk(Beverage beverage) {
            this.beverage = beverage;
        }
    
        public String getDescription() {
            return beverage.getDescription() + ", 스팀밀크";
        }
    
        public double cost() {
            return .10 + beverage.cost();
        }
    }

     Decorator 패턴으로 우유, 두유, 휘핑크림에는 Beverage 클래스가 변수로 들어가있는 것을 볼 수 있다. 이는 다형성을 이용해 이전 클래스를 Wrapping하기 위한 변수이다.

    그리고 실행 코드를 만들어 테스트해보자.

    package decoratorPattern;
    
    public class Coffee {
    
        public static void main(String[] args) {
            Beverage beverage = new Espresso();
            System.out.println(beverage.getDescription() + " $" + beverage.cost());
    
            Beverage beverage2 = new DarkRoast();
            beverage2 = new Mocha(beverage2);
            beverage2 = new Mocha(beverage2);
            beverage2 = new Whip(beverage2);
            System.out.println(beverage2.getDescription() + " $" +beverage.cost());
    
            Beverage beverage3 = new HouseBlend();
            beverage3 = new Soy(beverage3);
            beverage3 = new Mocha(beverage3);
            beverage3 = new Whip(beverage3);
            System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
        }
    }
    

     

    output

    위의 코드에서 beverage2를 보면 DarkRoast, Mocha, Mocha, Whip순으로 객체를 래핑하는 것을 볼 수 있다. 이처럼 여러 클래스를 래핑함으로써 기존의 코드를 변경하지 않고 기능을 확장해 나갈 수 있다.


    주의할점

    데코레이터는 그 데코레이터가 감싸고 있는 객체에 행동을 추가하기 위한 용도로 만들어졌다. 여러 단계의 Decorator를 파고 들어가서 어떤 작업을 해야한다면 원래 데코레이터 패턴이 만들어진 의도와 어긋나는 것이다.

    '디자인 패턴' 카테고리의 다른 글

    Command Pattern  (0) 2022.02.23
    Factory Pattern  (0) 2022.02.14
    Observer Pattern  (0) 2022.02.03
    Strategy Pattern  (0) 2022.02.02
    싱글톤 패턴(Singleton Pattern)  (0) 2021.10.28

    댓글

Designed by Tistory.