티스토리 뷰

 

* 요구 상황 가정

 

- 자동차의 종류 별로(기본, 세단, SUV, 트럭 등) 특정한 타이어, 브레이크, 엔진 오일 등의 부품들을 추가해서 완제품으로 구성하려고 한다.

- 자동차에는 기본 성능 점수가 배정되어 있고, 각 부품을 추가할 때마다 각각의 부품에 해당하는 성능 점수를 자동차의 기본 성능 점수에 더해 산출한다.

 

* 생각해볼 수 있는 방식

1)

각 부품과 자동차를 조합해서 새로운 클래스를 만들어 가는 방식

- 클래스 개수가 넘 많아짐. 너무 추상화가 안되어 있음.

 

2)

각 부품들의 존재 여부를 Car 클래스의 필드에 정의하고, 성능 점수를 처리해주는 메소드를 정의.

package nodecorator;

public class Car {
    boolean fastBrake;
    boolean thickTire;
    boolean goodEngineOil;

    public boolean hasFastBrake() {
        return fastBrake;
    }

    public boolean hasThickTire() {
        return thickTire;
    }

    public boolean hasGoodEngineOil() {
        return goodEngineOil;
    }

    public int addPerformanceRating() {
        int rating = 0;
        if (hasThickTire()) {
            rating += 10;
        }
        if (hasFastBrake()) {
            rating += 20;
        }
        if (hasGoodEngineOil()) {
            rating += 30;
        }
        return rating;
    }
}
package nodecorator;

public class BasicCar extends Car{

    int performanceRating = 10;
    
    public BasicCar() {
        fastBrake = true;
        goodEngineOil = true;
        thickTire = false;
    }

    @Override
    public int addPerformanceRating() {
        return this.performanceRating + super.addPerformanceRating();
    }
}
package nodecorator;

public class Main {
    public static void main(String[] args) {
        Car basicCar = new BasicCar();
        System.out.println(basicCar.addPerformanceRating());
    }
}

 

-문제점: 

1. 단일책임원칙 위반: 하나의 클래스는 하나의 책임만 져야한다. -> Car 클래스에서 브레이크, 엔진오일, 타이어 등의 책임을 지고 있음.

 

2. 개방폐쇄원칙 위반: 부품의 종류가 더 추가됨에 따라 Car 클래스의 메소드를 전부 수정해줘야 한다. 또한 상속 받은 BasicCar 클래스의 생성자 부분도 수정해야된다.   

 

3. 불필요한 메소드의 상속: 새로운 차종이 추가될 경우, 해당 차종이 좋은 엔진오일이 필요 없을 때, 즉 hasGoodEngineOil()이 필요없을 때에도 불구하고 상속을 받을 수 있음. 

 

3) 데코레이터 패턴 적용 

 

* 데코레이터란? (Decorator) - 장식자

 

* 무엇을 장식할까? - 위의 예에서는 Suv, Sedan, BasicCar 

 

* 무엇으로? - FastBrake, GoodEngineOil 등

 

* 클래스 다이어 그램 (Suv 클래스를 FastBrake와 ThickTire로 장식하는 상황 가정)

Suv 클래스를 FastBrake와 ThickTire 클래스로 CarDecoratror를 이용해서 장식한다. CarDecorator는 Car을 알고있음!! --> 중요한 부분(개인적인 생각..)

 

 

* 코드 예시

 

- Car 클래스

package org.example.decorator;

public abstract class Car {
    private int performanceRating;

    public abstract int addPerformanceRating();
}

 

 

- Suv 클래스

package org.example.decorator;

public class Suv extends Car {
    private final int performanceRating = 10;

    @Override
    public int addPerformanceRating() {
        return performanceRating;
    }
}

 

 

- CarDecorator 클래스

package org.example.decorator;

public abstract class CarDecorator extends Car{

    protected Car car;

    public CarDecorator(Car car) {
        this.car = car;
    }

    public abstract int addPerformanceRating();
}

 

- FastBrake 클래스 

package org.example.decorator;

public class FastBrake extends CarDecorator{

    public FastBrake(Car car) {
        super(car);
    }

    @Override
    public int addPerformanceRating() {
        return car.addPerformanceRating() + 10;
    }
}

 

 

-ThickTire 클래스

package org.example.decorator;

public class ThickTire extends CarDecorator{

    public ThickTire(Car car) {
        super(car);
    }

    @Override
    public int addPerformanceRating() {
        return car.addPerformanceRating() + 20;
    }
}

 

 

 

- Main 클래스

package org.example.decorator;

public class DecoratorMain {
    public static void main(String[] args) {
        Car suv = new FastBrake(new ThickTire(new Suv()));
        System.out.println(suv.addPerformanceRating());
    }
}

 

 

- 메모리 상 객체 생성 과정

 

new Suv() -> 1번

new ThickTire(new Suv()) -> 2번

new FastBrake(new ThickTire(new Suv())) -> 3번

Car suv = -> 4번

 

 

4) 앞서 말한 문제점이 해결 됐나?

 

1. 단일책임원칙 -> FastBrake, ThickTire 등 클래스를 분리하여 책임을 분산하였다.

2. 개방폐쇄원칙 -> 클래스 다이어그램을 보면 알 수 있듯이, Suv 클래스 입장에선 장식자가 기능을 추가해줌으로써 Suv 클래스 내부를 전혀 건드리지 않고 기능을 추가할 수 있다. 

3. 불필요한 메소드의 상속 -> 기능 별로 클래스를 분리하면서 자연스럽게 해결. 

 

 

5) 데코레이터 패턴의 특징 정리 

 

- 장식 대상은 데코레이터의 존재를 알 수 없다. 

- 데코레이터의 부모 클래스는 자신이 장식하려고 하는 객체의 부모 클래스와 동일하게 설정.

- 데코레이터 안에는 부모 클래스의 레퍼런스가 있다. (Car car)

- 한 객체를 여러 개의 데코레이터로 감쌀 수 있음.

- FastBrake, ThickTire 클래스의 addPerformanceRating() 메소드 내부에 추가적인 기능을 구현할 수 있음.

- 프로그램 실행 중 동적으로 데코레이터를 사용하여 객체에 추가 요소를 더할 수 있다. 

 

단점

- Suv 객체를 '필요로 하는' 로직을 처리해야할 경우에는, 데코레이터 패턴이 적절하지 않다. (외부에서 감싸고 있기 때문)

- 실행 단계가 중요하지 않음 -> 다시 말하면, 데코레이터 생성 순서가 중요한 경우에는 적합하지 않을 수 있음. 

- 기능을 추가할 때 마다 객체를 추가해줘야함. 

- 데코레이터를 많이 사용하면 코드가 복잡해짐. 

 

6) 느낀점

- Java I/O에 대한 궁금증 어느 정도 해결 

- 객체지향설계원칙을 지키기 위한 과정은 험난하다..

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/03   »
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
글 보관함