책너두 (헤드 퍼스트 디자인 패턴) 9일차 (~139p)

요약

  • 객체 꾸미기 (데코레이터 패턴) 계속
    • Beverage 클래스 장식하기
    • 커피 주문 시스템 코드 만들기
    • 음료 코드 구현하기
    • 첨가물 코드 구현하기
    • 커피 주문 시스템 코드 테스트
    • 데코레이터가 적용된 예 : 자바 I/O
      • 자바 I/O 데코레이터 만들기
    • 디자인 도구상자 안에 들어가야 할 도구들 추가
      • OCP
      • 데코레이터 패턴

메모

Beverage 클래스 장식하기

  • CondimentDecorator (첨가물 데코레이터)는 꾸밀 대상인 Beverage 객체를 상속함.
    • 상속을 통해, 데코레이터 객체가 자신이 감싸고 있는 객체랑 같은 형식을 맞춘다.
    • 데코레이터가 꾸밀 대상을 구성으로 가지고 있기 때문에 행동(비용 계산)을 실행중에 동적으로 설정할 수 있음.
      • 음료에 첨가물이 추가되도 유연성을 잃지 않음.

커피 주문 시스템 코드 만들기

  • 더블 모카에 두유와 휘핑 크림을 추가한 음료를 만든다면?
public abstract class Beverage {
    String description = "제목 없음";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}
public abstract class CondimentDecorator extends Beverage {
    Beverage beverage;
    public abstract String getDescription();
}
  • 첨가물은 음료 객체를 상속함.
  • 데코레이터가 감쌀 음료 객체를 구성으로 가짐.
    • 모든 음료 객체를 감쌀 수 있도록 음료 객체 슈퍼 클래스를 가짐

음료 코드 구현하기

public class Espresso extends Beverage {

    public Espresso() {
        description = "에스프레소";
    }

    public double cost() {
        return 1.99;
    }
}
  • 실제 음료인 에스프레소는 description 변수를 설정함.
  • 에스프레소 가격을 계산함.
    • 첨가물 가격을 걱정할 필요가 없음.
public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "하우스 블렌드 커피";
    }

    public double cost() {
        return .89;
    }
}
  • 에스프레소와 거의 동일함.

첨가물 코드 구현하기

public class Mocha extends CondimentDecorator {
    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    public String getDescription() {
        return beverage.getDescription() + ", 모카";
    }

    public double cost() {
        return beverage.cost() + .20;
    }
}
  • 첨가물 구상체는 인스턴스 변수로 Beverage 를 구성으로 가짐
    • 생성 시에 설정함.
  • 음료 설명에 첨가되는 아이템 설명이 추가됨.
  • 음료 가격에 모카 가격을 추가하여 계산함.

커피 주문 시스템 코드 테스트

public class StarbuzzCoffee {
    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()
            + " " + beverage2.cost());

        Beverage beverage3 = new HouseBlend();
        beverage3 = new Soy(beverage3);
        beverage3 = new Mocha(beverage3);
        beverage3 = new Whip(beverage3);
        System.out.println(beverage3.getDescription()
            + " " + beverage3.cost());
    }
}
  • 감쌀 대상인 Beverage 를 먼저 선언하고, 이를 첨가물 생성자를 통해 계속 감싸주기만 하면 됨.

데코레이터가 적용된 예 : 자바 I/O

  • FileInputStream 은 데코레이터로 장식할 예정인 구상 객체임. → 추상 구성 요소는 InputStream.
    • BufferedInputStream은 구상 데코레이터로, FileInputStream에 입력을 미리 읽어서 더 빠르게 처리할 수 있게 해주는 버퍼링 기능을 더해 주는 역할임
    • ZipInputStream도 구상 데코레이터 → zip 파일에서 데이터를 읽어 올 때, 그 속에 있는 항목을 읽는 기능임.
      • 추상 데코레이터는 FilterInputStream.
  • 출력 스트림 디자인도 똑같음.
  • 이러한 데코레이터 패턴은, 잡다한 클래스가 너무 많아짐.
    • 데코레이터 패턴이 적용된 API를 사용하는 개발자는 괴로움.
    • 하지만, 데코레이터 패턴이 어떤 식으로 작동하는지 이해하면, 데코레이터 패턴으로 감싸서 원하는 행동을 구현할 수 있음.

자바 I/O 데코레이터 만들기

  • 입력 스트림에 있는 대문자를 전부 소문자로 바꿔주는 데코레이터를 만든다면?
public class LowerCaseInputStream extends FilterInputStream {
    public LowerCaseInputStream(InputStream in) {
        super(in);
    }

    public int read() throws IOException {
        int c = in.read();
        return (c == -1 ? c Character.toLowerCase((char)c));
    }

    public int read(byte[] b, int offset, int len) throws IOException {
        int result = in.read(b, offset, len);
        for (int i = offset; i < offset+result; i++) {
            b[i] = (byte) Character.toLowerCase((char)b[i]);
        }
        return result;
    }
}
public class InputTest {
    public static void main(String[] args) throws IOException {
        int c;

        try {
            InputStream in =
                new LowerCaseInputStream(
                    new BufferedInputStream(
                        new FileInputStream("test.txt")));

            while((c = in.read()) >= 0) {
                System.out.print((char)c);
            }

            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 이런식으로 데코레이터를 감싸서 InputStream을 사용할 수 있음.

디자인 도구상자 안에 들어가야 할 도구들

  • 데코레이터 패턴과 OCP가 추가됨.

댓글

Designed by JB FACTORY