본문 바로가기
그룹 스터디 공부(IT 서적)/오브젝트

08 의존성 관리하기

by hanyugyeong 2023. 8. 28.
반응형
SMALL

잘 설계된 객체지향 애플리케이션은 작고 응집도 높은 객체들로 구성된다. 작고 응집도 높은 객체란 책임의 초점이 명확하고 한 가지 일만 잘 하는 객체를 의미한다.

 

01. 의존성 이해하기 

변경과 의존성

어떤 객체가 협력하기 위해 다른 객체를 필요로 할 때 두 객체 사이에 의존성이 존재하게 된다. 의존성은 실행 시점과 구현 시점에 서로 다른 의미를 가진다. 

* 실행 시점: 의존하는 객체가 정상적으로 동작하기 위해서는 실행 시에 의존 대상 객체가 반드시 존재해야 한다. 

* 구현 시점: 의존 대상 객체가 변경될 경우 의존하는 객체도 함께 변경된다. 

 

영화 예매 시스템의 PeriodCondition 클래스를 이용해 의존성의 개념을 설명하기로 한다. 

PeriodCondition 클래스의 isSatisfiedBy 메서드는 Screening 인스턴스에게 getStartTime 메시지를 전송한다. 

public class PeriodCondition implements DiscountCondition {
    private DayOfWeek dayOfWeek;
    private LocalTime startTime;
    private LocalTime endTime;

    public PeriodCondition(DayOfWeek dayOfWeek, LocalTime startTime, LocalTime endTime) {
        this.dayOfWeek = dayOfWeek;
        this.startTime = startTime;
        this.endTime = endTime;
    }

    public boolean isSatisfiedBy(Screening screening) {
        return screening.getStartTime().getDayOfWeek().equals(dayOfWeek) &&
                startTime.compareTo(screening.getStartTime().toLocalTime()) <= 0&&
                endTime.compareTo(screening.getStartTime().toLocalTime()) >= 0;
    }
}

PeriodCondition은 Screening에 의존하며 그림에 표현한 것처럼 PeriodCondition은 Screening으로 향하는 점선 화살표로 표시한다. 

두 요소 사시의 의존성은 의존되는 요소가 변경될 때 의존하는 요소도 함께 변경될 수 있다는 것을 의미한다. 따라서 의존성은 변경에 의한 영향의 전파 가능성을 암시한다. 

어떤 형태로든 DayOfWeek, LocalTime, Screening, DiscountCondition이 변경된다면 PeriodCondition도 함께 변경될 수 있다.

DayOfWeek과 LocalTime은 PeriodCondition의 인스턴스 변수로 사용된다. Screening은 메서드 인자로 사용된다. PeriodCondition이 DiscountCondition에 의존하는 이유는 인터페이스에 정의된 오퍼레이션들을 퍼블릭 인터페이스의 일부로 포함시키기 위해서다. 

 

의존성 전이

의존성은 전이될 수 있다. Screening의 코드를 살펴보면 Screening이 Movie, LocalDateTime, Customer에 의존한다는 사실을 알 수 있다. 의존성 전이가 의미하는 것은 PeriodCondition이 Screening에 의존할 경우 PeriodCondition은 Screening이 의존하는 대상에 대해서도 자동적으로 의존하게 된다는 것이다. 

Screening이 Movie,LocalDateTime,Customer에 의존하기 때문에 PeriodCondition 역시 간접적으로 Movie, LocalDateTime, Customer에 의존하게 된다. 

의존성은 전이될 수 있기 때문에 의존성의 종류를 직접 의존성과 간접 의존성으로 나누기도 한다. 

직접 의존성이란 말 그대로 한 요소가 다른 요소에 직접 의존하는 경우를 가리킨다. 

ex) PeriodCondition이 Screening에 의존하는 경우 

간접 의존성이란 직접적인 관계는 존재하지 않지만 의존성 전이에 의해 영향이 전파되는 경우를 가리킨다. 이 경우 의존성은 PeriodCondtion의 코드안에 명시적으로 드러나지 않는다. 

 

의존성이란 의존하고 있는 대상의 변경에 영향을 받을 수 있는 가능성이다. 

런타임 의존성과 컴파일타임 의존성

의존성과 관련해서 다뤄야 하는 또 다른 주제는 런타임 의존성과 컴파일타임 의존성의 차이다. 

런타임은 애플리케이션이 실행되는 시점을 가리킨다. 

컴파일타임이란 작성된 코드를 컴파일하는 시점을 가리키지만 문맥에 따라서는 코드 그자체를 가리키기도 한다. 

 

런타임 의존성이 다루는 주제는 객체사이의 의존성이다. 반면 코드 관점에서 주인공은 클래스다. 따라서 컴파일타임 의존성이 다루는 주제는 클래스 사이의 의존성이다. 

 

런타임 의존성과 컴파일타임 의존성이 다를 수 있다. 

 

영화 예매 시스템을 예로 들어 살펴보자. Movie는 가격을 계산하기 위해 비율 할인 정책과 금액 할인 정책 모두를 적용할 수 있게 설계해야 한다. 다시 말해서 Movie는 AmountDiscountPolicy와 PercentDiscountPolicy모두와 협력할 수 있어야 한다. 

 

여기서 중요한 것은 Movie 클래스에서 AmountDiscountPolicy 클래스와 PercentDiscountPolicy 클래스로 향하는 어떤 의존성도 존재하지 않는다는 것이다. Movie 클래스는 오직 추상 클래스인 DiscountPolicy클래스에만 의존한다. 

 

public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private DiscountPolicy discountPolicy;

    public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
        this.title = title;
        this.runningTime = runningTime;
        this.fee = fee;
        this.discountPolicy = discountPolicy;
    }

    public Money getFee() {
        return fee;
    }

    public Money calculateMovieFee(Screening screening) {
        return fee.minus(discountPolicy.calculateDiscountAmount(screening));
    }
}

런타임 의존성을 살펴보면 상황이 완전히 달라진다. 코드를 작성하는 시점의 Movie 클래스는 AmountDiscountPolicy 클래스와 PercentDiscountPolicy 클래스의 존재에 대해 전혀 알지 못하지만 실행 시점의 Movie 인스턴스는 AmountDiscountPolicy 인스턴스와 PercentDiscountPolicy 인스턴스와 협력할 수 있어야 한다. 

두 클래스 모두를 포괄하는 DiscountPolicy라는 추상 클래스에 의존하도록 만들고 이 컴파일타임 의존성을 실행 시에 PercentDiscountPolicy 인스턴스나 AmountDiscountPolicy 인스턴스에 대한 런타임 의존성으로 대체해야한다. 

컨텍스트 독립성

유연하고 확장 가능한 설계를 만들기 위해서는 컴파일타임 의존성과 런타임 의존성이 달라야 한다는 사실을 이해했을 것이다. 

구체적인 클래스를 알면 알수록 그 클래스가 사용되는 특정한 문맥에 강하게 결합되기 때문이다. 

컴파일타임 의존성을 어떤 런타임 의존성으로 대체하느냐에 따라 달라질 것이다. 

 

클래스가 사용될 특정한 문맥에 대해 최소한의 가정만으로 이뤄져 있다면 다른 문맥에서 재사용하기가 더 수월해진다. 이를 컨텍스트 독립성이라고 부른다. 

 

설계가 유연해지기 위해서는 가능한 한 자신이 실행될 컨텍스트에 대한 구체적인 정보를 최대한 적게 알아야 한다. 

컨텍스트에 대한 정보가 적으면 적을수록 더 다양한 컨텍스트에서 재사용될 수 있기 때문이다. 결과적으로 설계는 더 유연해지고 변경에 탄력적으로 대응할 수 있게 될 것이다. 

의존성 해결하기 

컴파일타임 의존성은 구체적인 런타임 의존성으로 대체돼야 한다. 

Movie 클래스는 DiscountPolicy 클래스에 의존한다. 이것은 컴파일타임 의존성이다. Movie 인스턴스는 PercentDiscountPolicy 인스턴스나 AmountDiscountPolicy 인스턴스 중 하나에 의존한다. 

이것은 Movie 클래스와 DiscountPolicy 클래스 사이에 존재하는 컴파일타임 의존성이 Movie 인스턴스와 PercentDiscountPolicy 인스턴스 사이의 런타임 의존성이나 Movie 인스턴스와 AmountDiscountPolicy 인스턴스 사이의 런타임 의존성으로 교체돼야 한다는 것을 의미한다.

 

이처럼 컴파일타임 의존성을 실행 컨텍스트에 맞는 적절한 런타임 의존성으로 교체하는 것을 의존성 해결이라고 부른다.

의존성을 해결하기 위해서는 일반적으로 다음과 같은 세 가지 방법을 사용한다. 

 

* 객체를 생성하는 시점에 생성자를 통해 의존성을 해결 

* 객체 생성 후 setter 메서드를 통해 의존성 해결 

* 메서드 실행 시 인자를 이용해 의존성 해결 

 

예를 들어, 어떤 영화의 요금 계산에 금액 할인 정책을 적용하고 싶다고 가정해보자. 다음과 같이 Movie 객체를 생성할 때 AmountDiscountPolicy의 인스턴스를 Movie의 생성자에 인자로 전달하면 된다. 

 Movie avatar = new Movie("아바타",
            Duration.ofMinutes(120),
            Money.wons(10000),
            new AmountDiscountPolicy());

Movie의 생성자에 PercentDiscountPolicy의 인스턴스를 전달하면 비율 할인 정책에 따라 요금을 계산하게 될 것이다. 

 Movie starWars = new Movie("스타워즈",
            Duration.ofMinutes(180),
            Money.wons(11000),
            new PercentDiscountPolicy());

 Movie 클래스는 PercentDiscountPolicy 인스턴스와 AmountDiscountPolicy 인스턴스 모두를 선택적으로 전달받을 수 있도록 이 두 클래스의 부모 클래스인 DiscountPolicy 타입의 인자를 받는 생성자를 정의한다. 

public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private DiscountPolicy discountPolicy;

    public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
        this.title = title;
        this.runningTime = runningTime;
        this.fee = fee;
        this.discountPolicy = discountPolicy;
    }

}

Movie의 인스턴스를 생성한 후에 메서드를 이용해 의존성을 해결하는 방법도 있다. 

Movie avatar = new Movie();
avatar.setDiscountPolicy(new AmountDiscountPolicy());

이 경우 Movie 인스턴스가 생성된 후에도 DiscountPolicy를 설정할 수 있는 setter 메서드를 제공해야 한다. 

public class Movie {
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }
}

setter 메서드를 이용하는 방식은 객체를 생성한 이후에도 의존하고 있는 대상을 변경할 수 있는 가능성을 열어 놓고 싶은 경우에 유용하다. 예를 들어, 다음과 같이 setter 메서드를 이용하면 금액 할인 정책으로 설정된 Movie의 인스턴스를 중간에 비율 할인 정책으로 변경할 수 있다. 

Movie avatar = new Movie();
avatar.setDiscountPolicy(new AmountDiscountPolicy());
avatar.setDiscountPolicy(new PercentDiscountPolicy());

setter 메서드를 이용하는 방법은 실행 시점에 의존 대상을 변경할 수 있기 때문에 설계를 좀 더 유연하게 만들 수 있다. 

Movie avatar = new Movie();
avatar.setDiscountPolicy(new AmountDiscountPolicy());
avatar.setDiscountPolicy(new PercentDiscountPolicy());
setter 메서드를 이용하는 방법은 실행 시점에 의존 대상을 변경할 수 있기 때문에 설계를 좀 더 유연하게 만들 수 있다. 

단점은 객체가 생성된 후에 협력에 필요한 의존 대상을 설정하기 때문에 객체를 생성하고 의존 대상을 설정하기 전까지는 객체의 상태가 불완전할 수 있다는 점이다 

아래 코드에서처럼 setter 메서드를 이용해 인스턴스 변수를 설정하기 위해 내부적으로 해당 인스턴스 변수를 사용하는 코드를 설정하면 NullPointerException 예외가 발생할 것이다. 따라서 시스템의 상태가 불안정해질 수 있다. 

     Movie avatar = new Movie();
       avatar.calculateMovieFee(); //예외발생
더 좋은 방법은 생성자 방식과 setter 방식을 혼합하는 것이다. 항상 객체를 생성할 때 의존성을 해결해서 완전한 상태의 객체를 생성한 후, 필요에 따라 setter 메서드를 이용해 의존 대상을 변경할 수 있게 할 수 있다. 

이 방법은 시스템의 상태를 안정적으로 유지하면서도 유연성을 향상시킬 수 있기 때문에 의존성 해결을 위해 가장 선호되는 방법이다. 

Movie avatar = new Movie(...., new PercentDiscountPolicy());
avatar.setDiscountPolicy(new AmountDiscountPolicy());
Movie가 항상 할인 정책을 알 필요까지는 없고 가격을 계산할 때만 일시적으로 알아도 무방하다면 메서드의 인자를 이용해 의존성을 해결할 수도 있다. 

public class Movie {
 public Money calculateMovieFee(Screening screening, DiscountPolicy discountPolicy) {
        return fee.minus(discountPolicy.calculateDiscountAmount(screening));
    }
}
메서드 인자를 사용하는 방식은 협력 대상에 대해 지속적으로 의존 관계를 맺을 필요 없이 메서드가 실행되는 동안만 일시적으로 의존 관계가 존재해도 무방하거나, 메서드가 실행될 때마다 의존 대상이 매번 달라져야 하는 경우에 유용하다. 

클래스의 메서드를 호출하는 대부분의 경우에 매번 동일한 객체를 인자로 전달하고 있다면 생성자를 이용하는 방식이나 setter 메서드를 이용해 의존성을 지속적으로 유지하는 방식으로 변경하는 것이 좋다. 

02. 유연한 설계 


설계를 유연하고 재사용 가능하게 만들기로 결정했다면 의존성을 관리하는 데 유용한 몇 가지 원칙과 기법을 익힐 필요가 있다. 먼저 의존성과 결합도의 관계를 살펴보는 것으로 시작하자. 

의존성과 결합도 

객체지향 패러다임의 근간은 협력이다. 객체들은 협력을 통해 애플리케이션에 생명력을 불어넣는다. 

객체들이 협력하기 위해서는 서로의 존재와 수행 가능한 책임을 알아야 한다. 

 

Movie가 비율 할인 정책을 구현하는 PercentDiscountPolicy에 직접 의존한다고 가정해보자. 

public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private DiscountPolicy discountPolicy;

    public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
        this.title = title;
        this.runningTime = runningTime;
        this.fee = fee;
        this.discountPolicy = discountPolicy;
    }

    public Money calculateMovieFee(Screening screening) {
        return fee.minus(discountPolicy.calculateDiscountAmount(screening));
    }
}

이 코드는 비율 할인 정책을 적용하기 위해 Movie가 PercentDiscountPolicy에 의존하고 있다는 사실을 코드를 통해 명시적으로 드러낸다. 

이 코든느 Movie를 PercentDiscountPolicy라는 구체적인 클래스에 의존하게 만들기 때문에 다른 종류의 할인 정책이 필요한 문맥에서 Movie를 재사용 할 수 있는 가능성을 없애 버렸다. 

 

의존성을 바람직하게 만들어라 Movie가 협력하고 싶은 대상이 반드시 PercentDiscountCondition의 인스턴스일 필요는 없다는 사실을 주목하라. 

 

추상 클래스인 DiscountPolicy는 calculateDiscountAmount 메시지를 이해할 수 있는 타입을 정의함으로써 이 문제를 해결한다. 

 

PercentDiscountPolicy에 대한 의존성은 바람직하지 않다. DiscountPolicy에 대한 의존성은 바람직하다. 

 

바람직한 의존성은 재사용성과 관련이 있다. 어떤 의존성이 다양한 환경에서 클래스를 재사용할 수 없도록 제한한다면 그 의존성은 바람직하지 못한 것이다. 어떤 의존성이 다양한 환경에서 재사용할 수 있다면 그 의존성은 바람직한 것이다. 

 

다른 환경에서 재사용하기 위해 내부 구현을 변경하게 만드는 모든 의존성은 바람직하지 않은 의존성이다. 바람직한 의존성이란 컨텍스트에 독립적인 의존성을 의미하며 다양한 환경에서 재사용될 수 있는 가능성을 열어놓는 의존성을 의미한다. 

 

개발 커뮤니티가 항상 그런 것처럼 바람직한 의존성과 바람직하지 못한 의존성을 가리키는 좀 더 세련된 용어가 존재한다. 

결합도가 바로 그것이다. 

어떤 두 요소 사이에 존재하는 의존성이 바람직할 때 두 요소가 느슨한 결합도 또는 약한 결합도를 가진다고 말한다. 

반대로 두 요소 사이의 의존성이 바람직하지 못할 때 단단한 결합도 또는 강한 결합도를 가진다고 말한다. 

 

Movie 클래스가 추상 클래스인 DiscountCondition에 의존하면 AmountDiscountCondition과 PercentDiscountCondition 모두와 협력할 수 있다. 따라서 Movie와 DiscountCondition은 느슨하게 결합된다. 

 

Movie와 PercentDiscountCondition에 직접 의존하는 경우 Movie는 PercentDiscountCondition  외의 다른 인스턴스와 협력하는 것이 불가능하다. 

지식이 결합을 낳는다. 

Movie 클래스가 PercentDiscountCondition 클래스에 직접 의존한다고 가정하자. 이 경우 Movie는 협력할 객체가 비율 할인 정책에 따라 할인 요금을 계산할 것이라는 사실을 알고 있다. 

 

Movie 클래스가 추상 클래스인 DiscountPolicy 클래스에 의존하는 경우에는 구체적인 계산 방법은 알 필요가 없다. 그저 할인 요금을 게산한단다는 사실만 알고 있을 뿐이다. 

 

Movie가 PercentDiscountCondition에 의존하는 것보다 DiscountPolicy 에 의존하는 경우 알아야 하는 지식의 양이 적기 때문에 결합도가 느슨해지는 것이다. 

 

결합도를 느슨하게 만들기 위해서는 협력하는 대상에 대해 필요한 정보 외에는 최대한 감추는 것이 중요하다. 

추상화에 의존하라 

추상화란 어떤 양상, 세부사항, 구조를 좀 더 명확하게 이해하기 위해 특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡도를 극복하는 방법이다. 

추상활르 사용하면 현재 다루고 있는 문제를 해결하는 데 불필요한 정보를 감출 수 있다. 따라서 대상에 대해 알아야 하는 지식의 양을 줄일 수 있기 때문에 결합도를 느슨하게 유지할 수 있다. 

 

Movie와 DiscountPolicy 사이의 결합도가 더 느슨한 이유는 Movie가 구체적인 대상이 아닌 추상화에 의존하기 때문이다. 

 

목록에서 아래쪽으로 갈수록 클라이언트가 알아야 하는 지식의 양이 적어지기 때문에 결합도가 느슨해진다. 

 

* 구체 클래스 의존성

* 추상 클래스 의존성 

* 인터페이스 의존성 

 

의존하는 대상이 더 추상적일수록 결합도는 더 낮아진다 

명시적인 의존성 

아래 코드는 한 가지 실수로 인해 결합도가 불필요하게 높아졌다. 

public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private DiscountPolicy discountPolicy;

    public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
        this.title = title;
        this.runningTime = runningTime;
        this.fee = fee;
        this.discountPolicy = new AmountDiscountPolicy();
    }
}

Movie의 인스턴스 변수인 discountPolicy는 추상 클래스인 DiscountPolicy 타입으로 선언돼 있다. 

discountPolicy는 DiscountPolicy 타입으로 선언돼 있지만 생성자에서 구체 클래스는 AmountDiscountPolicy의 인스턴스를 직접 생성해서 대입하고 있다. 따라서 Movie는 추상 클래스인 DiscountPolicy뿐만 아니라 구체 클래스인 AmountDiscountPolicy에도 의존하게 된다.

 

모든 경우에 의존성은 명시적으로 퍼블릭 인터페이스에 노출된다. 이를 명시적인 의존성이라고 부른다. 

의존성을 구현 내부에 숨기지말고 퍼블릭 인터페이스를 통해 의존성을 명시적으로 드러내라

 

new는 해롭다 

클래스의 인스턴스를 생성할 수 있는 new 연산자를 제공한다. 하지만 안타깝게도 new를 잘못 사용하면 클래스 사이의 결합도가 극단적으로 높아진다. 결합도 측면에서 new가 해로운 이유는 크게 두 가지다. 

* new 연산자를 사용하기 위해서는 구체 클래스의 이름을 직접 기술해야 한다. 따라서 new를 사용하는 클라이언트는 추상화가 아닌 구체 클래스에 의존할 수 밖에 없기 때문에 결합도가 높아진다. 

* new 연산자는 생성하려는 구체 클래스뿐만 아니라 어떤 인자를 이용해 클래스의 생성자를 호출해야 하는지도 알아야 한다. 따라서 new를 사용하면 클라이언트가 알아야 하는 지식의 양이 늘어나기 때문에 결합도가 높아진다. 

 

다음은 생성자를 통해 외부의 인스턴스를 전달받아 의존성을 해결하는 Movie의 코드를 나타낸 것이다. 

public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private DiscountPolicy discountPolicy;

    public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
        this.title = title;
        this.runningTime = runningTime;
        this.fee = fee;
        this.discountPolicy = discountPolicy;
    }

}

Movie는 AmountDiscountPolicy의 인스턴스를 직접 생성하지 않는다. 필요한 인스턴스를 생성자의 인자로 전달받아 내부의 인스턴스 변수에 할당한다. 

가끔은 생성해도 무방하다. 

Movie의 대부분의 경우에는 AmountDiscountPolicy의 인스턴스와 협력하고 가끔씩만 PercentDiscountPolicy의 인스턴스와 협력한다고 가정해보자. 

public class Movie {
    private String title;
    private Duration runningTime;
    private Money fee;
    private DiscountPolicy discountPolicy;
     public Movie(String title, Duration runningTime) {
       this(title,runningTime,new AmountDiscountPolicy());
    }

    public Movie(String title, Duration runningTime, Money fee, DiscountPolicy discountPolicy) {
        this.title = title;
        this.runningTime = runningTime;
        this.fee = fee;
        this.discountPolicy = discountPolicy;
    }

}

DiscountPolicy의 인스턴스를 인자로 받는 메서드와 기본값을 생성하는 메서드를 함께 사용한다면 클래스의 사용성을 향상시키면서도 다양한 컨텍스트에서 유연하게 사용될 수 있는 여지를 제공할 수 있다. 

  public Money calculateMovieFee(Screening screening) {
        return calculateMovieFee(screening, new AmountDiscountPolicy());
    }

    public Money calculateMovieFee(Screening screening,DiscountPolicy discountPolicy) {
        return fee.minus(discountPolicy.calculateDiscountAmount(screening));
    }

이 예는 설계가 트레이드오프 활동이라는 사실을 다시 한번 상기시킨다. 여기서 트레이드오프의 대상은 결합도와 사용성이다. 구체 클래스에 의존하게 되더라도 클래스의 사용성이 더 중요하다면 결합도를 높이는 방향으로 코드를 작성할 수 있다.

표준 클래스에 대한 의존은 해롭지 않다. 

 의존성이 불편한 이유는 그것이 항상 변경에 대한 영향을 암시하기 때문이다. 따라서 변경될 확률이 거의 없는 클래스라면 의존성이 문제가 되지 않는다. 

public abstract class DiscountPolicy {
    private List<DiscountCondition> conditions = new ArrayList<>();
}

클래스를 직접 생성하더라도 가능한 추상적인 타입을 사용하는 것이 확장성 측면에서 유리하다. 위 코드에서 conditions의 타입으로 인터페이스인 List를 사용한 것은 이 때문이다. 이렇게 하면 다양한 List타입의 객체로 conditions를 대체할 수 있게 설계의 유연성을 높일 수 있다.

의존성에 의한 영향이 적은 경우에도 추상화에 의존하고 의존성을 명시적으로 드러내는 것은 좋은 설계 습관이다. 

public abstract class DiscountPolicy {
    private List<DiscountCondition> conditions = new ArrayList<>();

    public void switchConditions (List<DiscountCondition> conditions) {
        this.conditions = conditions;
    }
}

                   

 

 

 

 

 

 

 

 

반응형
LIST

'그룹 스터디 공부(IT 서적) > 오브젝트' 카테고리의 다른 글

chapter 10 상속과 코드 재사용  (0) 2023.09.12
09 유연한 설계  (0) 2023.09.04
07 객체 분해  (0) 2023.08.21
06 메시지와 인터페이스  (0) 2023.08.18
05 책임 할당하기_02  (0) 2023.08.09