ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 구조패턴 > 데코레이터 *
    기타/디자인패턴 2021. 2. 11. 10:10

    기본 기능 + 여러가지 추가 기능

    상속은 강력하지만 상속을 사용한다고 무조건 유연하고, 관리하기 쉬운 디자인이 만들어 지는 것은 아니다.

    Coffee 라는 추상 클래스의 상속을 통해 다양한 옵션의 커피를 만들려면 엄청나게 많은 수의 클래스를 생성, 관리해야한다.  이런 서브 클래스를 만드는 방식의 상속은 행동이 컴파일시에 완전히 결정되고 모든 서브 클래스가 똑같은 행동을 상속받아 어떤 클래스에서는 불필요한 행동을 가질 수 있다.

     

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

    이 기술을 통해  슈퍼 클래스에서는 생각치 못했던 새로운 임무를 기존 코드의 수정없이 개별 객체에 더할 수도 있다. 

    이는 객체지향 OCP 원칙이기도 하다.

     

    데코레이터는 객체를 "장식" 하는 패턴이다. "장식"은 데코레이터 객체가 래퍼객체라는 것을 의미한다.

    데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 첨가한다. 

    서브클래스를 만드는 것을 통해 이 기능을 유연하게 확장할 수 있는 방법을 제공한다.

     

    자바 I/O 패키지에도 데코레이터 패턴을 기반으로 만들어진 객체들이 있다.

    InputStream in = new LineNumberInputStream(new BufferedInputStream(new FileInputStream));

     

    데코레이터의 단점

    1. 특정 형식에 의존하는 클라이언트 코드를 가지고 와서 생각해보지 않고,  데코레이터 패턴을 적용하는 사람들이 적지 않게 있다.

    데코레이터의 큰 장점은 클라이언트에서 데코레이터 사용 여부를 알 수 없다는 것인데, 특정 형식에 의존하는 코드에 

    데코레이터를 적용하게 되면 그런 장점이 깨진다.

    2. 구성요소를 초기화하는 코드가 복잡하다.

     


    HeadFirst Design Pattern 에 나온 예시 커피주문코드 

    원두 DarkRoast 등 ConcreateComponent 와 Mocha , Whip 등 ConcreateDecorator 는 모두 Beverage 를 상속받았다.

    (Mocha -> CondimentDecorator -> Beverage)

    Decorator를 생성할 때마다 그 전까지 생성된 원두 혹은 작업된 원두를 인자로 받아 cost와 description 을 수정한다.

     

    public class StarBuzzCoffee {
        
        @Test
        public void test(){        
           Beverage b = new DarkRoast();
           b = new Mocha(b); // Mocha 로 감쌈 
           b = new Whip(b); // Whip 로 감쌈                      
        }
    }

     

    상품 주문을 할 때 쿠폰, 회원 등급 등 다양한 방법으로 할인 받을 수 있다고 가정하고

    계산된 가격을 가진 Order 를 데코레이터 패턴을 이용해 생성했다. 

    Concreate 상품인 ProductA와 Coupon은 모두 Order 의 상속을 받는다. 

    (Coupon -> DiscountDecorator -> Order)

     

    public class DecoratorTest {
        
        @Test
        public void testCoupon(){        
            /**
             * 1. 상품을 주문한다.
            */
            Order order = new DiscountCoupon(new ProductA("상품A",5000),CouponType.TWO);
            double price = order.getPrice();
            System.out.println(price);
            assertThat(price).isEqualTo(3000);
            
        }
    }
    public abstract class Order {
        double price;
        public abstract double getPrice();
    }
    
    public abstract class DiscountDecorator extends Order{
        public abstract double getPrice();
    }
    public  class ProductA extends Order {
        String name;
    
        public ProductA(String name,int price){
            this.name = name;
            this.price = price;
        }
    
        @Override
        public double getPrice() {
            return this.price;
        }
    }
    public class Coupon extends DiscountDecorator{
    
        Product order ; //주문 상품 
        CouponType type; //쿠폰 타입 
        
        public Coupon(Product order,CouponType type ) {
            this.order = order;
            this.type = type;
        }
        
        @Override
        public double getPrice() {
            System.out.println("2");
            return type.discount(this.order.getPrice()); 
            //return (this.order.getPrice() - 1000);
        }
        
        /**
         * 천원, 이천원, 삼천원 쿠폰 
         * */
        enum CouponType{
            ONE { 
                public double discount(double value) {
                    return value-1000;
                }
            },TWO {
                public double discount(double value) {
                    return value-2000;
                }
            },THREE {
                public double discount(double value) {
                    return value-3000;
                }
            };
            
            public abstract double discount(double value);
        }
        //https://honbabzone.com/java/java-enum/
    }

     

     

    반응형

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

    행위패턴 > 이터레이터 *  (0) 2021.02.11
    행위패턴 > 커맨드 *  (0) 2021.02.11
    구조패턴 > 퍼싸드 *  (0) 2021.02.11
    구조패턴 > 컴포지트 *  (0) 2021.02.11
    구조패턴 > 어댑터 *  (0) 2021.02.11
Designed by Tistory.