인프런 커뮤니티 질문&답변

OR님의 프로필 이미지
OR

작성한 질문수

오브젝트 - 기초편

5-3-예제. 유연하고 일관적인 협력

Movie 객체와 Screening 객체의 순환참조?

해결된 질문

작성

·

95

1

안녕하세요 😀, 강의가 너무 재밌어서 2회차 돌고 있는 학습자 인사 드립니다. 🙇

복습하고 있는 중 궁금한 게 있어 글 올리게 되었습니다.

 

일단, 두 객체가 서로를 참조하는 상황이면 순환참조의 가능성이 있다고 알고 있습니다.

Member, Team에서 두 객체가 서로 참조하고 있는 것을 예로써 이 상황을 설명하는 것을 많이 봐 왔습니다.

class Team {
    private long id;
    private String name;
    private List<Member> members;
}

class Member {
    private long id;
    private String name;
    private Team myTeam;
}

 

강좌의 Movie 객체와 Screening 객체도 위 처럼 필드로서 서로 참조하는 것은 아니지만, 아래와 같이 Movie는 Screening을 calculateFee 메소드의 인수로 전달받아 참조하고 있고, Screening은 필드로서 movie를 참조하고 있습니다.

public class Movie {
    private Money fee;
    private DiscountPolicy discountPolicy;

    public Movie(Money fee, DiscountPolicy discountPolicy) {
        this.fee = fee;
        this.discountPolicy = discountPolicy;
    }

    // Movie가 Screening 참조
    public Money calculateFee(Screening screening) {
        return fee.minus(discountPolicy.calculateDiscount(screening));
    }

    public Money getFee() {
        return fee;
    }
}

public class Screening {
    // Screening이 Movie 참조
    private Movie movie;
    private int sequence;
    private LocalDateTime whenScreened;

    public Screening(Movie movie, int sequence, LocalDateTime whenScreened) {
        this.movie = movie;
        this.sequence = sequence;
        this.whenScreened = whenScreened;
    }

    public Reservation reserve(Customer customer, int audienceCount) {
        Money fee = movie.calculateFee(this).times(audienceCount);
        return new Reservation(customer, this, audienceCount, fee);
    }

    public Money getFixedFee() {
        return movie.getFee();
    }

    public boolean isSequence(int sequence) {
        return this.sequence == sequence;
    }

    public LocalDateTime getStartTime() {
        return whenScreened;
    }
}

 

서로 참조하는 것은 당장은 아니더라도 추후에 순환참조를 만들 길을 열어 두게 되어 왠만하면 배제해야 하는 것으로 알고 있습니다.

 

여기서 질문이 있습니다!

 

  1. 예제에 사용된 Movie와 Screening도 두 객체가 서로를 참조하고 있는 것으로 볼 수 있나요?

  2. 만약 그렇다면 이 경우에도 순환참조의 가능성이 있는 것인가요?

 

감사합니다. 😃

 

답변 1

1

조영호님의 프로필 이미지
조영호
지식공유자

OR님 안녕하세요.

강의가 재미있다니 다행이네요. :)

먼저 질문 주신 내용에 대한 답변을 드리면 예제는 순환 참조에 해당하지 않습니다.

순환 참조를 설명드리기 전에 먼저 순환 의존성의 개념을 설명 드리는게 좋을것 같네요.

어떤 클래스에서 시작해서 의존성 그래프를 따라 이동할 때 다시 자기 자신으로 돌아오는 경우 순환 의존성(circula dependency 또는 cyclic dependency)이 존재한다고 합니다.

예제 코드의 경우 Screening에서 의존성 그래프를 따라 이동하면 다시 Screening으로 돌아오기 때문에 순환 의존성이 존재합니다.

순환 의존성을 구성하는 의존성은 직접적일 수도 있고 간접적일 수도 있습니다.

 

질문에 적어주신 Team과 Member 사이의 관계는 일반적으로 양방향 연관관계(bi-directional assoication)라고 부릅니다.

양방향 참조(bi-directional reference)라고 표현하는 경우도 종종 있는데 대부분의 경우에는 양방향 연관관계라는 용어를 사용합니다.

양방향 연관관계는 두 개의 객체 참조를 동기화시키는 방식으로 구현합니다.

 

자료를 찾다보면 순환 참조(cyclic reference)라는 용어를 보게되는데 직접적인 객체 참조로만 구현된 순환 의존성의 특별한 경우를 가리키는 개념으로 사용됩니다.

책이나 자료를 살펴보면 일반적으로 순환 참조를 의미하는 경우에도 순환 의존성이라는 용어를 많이 사용하지만 객체 참조로 구성된 순환 의존성이라는 개념을 강조하고 싶은 경우에는 순환 참조라는 용어를 사용하는 것으로 보입니다.

스프링에서도 빈 초기화에 실패한 경우의 예외 메시지에서도 순환 참조라는 용어를 사용합니다.

양방향 의존관계는 2개의 클래스 사이에서 발생하는 순환 참조로 정의할 수 있을것 같아요.

결과적으로 예제에서 Screening과 Movie 사이의 의존성은 객체 참조로 구현되어 있지만 그 이후에는 파라미터로 구현되어 있기 때문에 순환 의존성은 존재하지만 순환 참조는 존재하지 않습니다.

정의에서 알 수 있는 것처럼 객체 참조가 아닌 의존성을 포함하는 순환 의존성보다는 순환 참조의 결합도가 높습니다. 

 

전체적으로 설계의 품질을 급격하게 낮추지 않는 선에서 복잡성을 낮출 수 있는 경우나 객체 그룹의 경계를 명확하게 관리할 경우에는 양방향 의존성을 포함해서 순환 의존성을 유지하는게 더 나은 경우도 있습니다.

테스트나 유지보수성의 관점에서 불필요한 순환 의존성을 제거하는 것이 좋지만 현재 설계에서는 순환 의존성을 제거할 경우 불필요하게 복잡성이 높아지고 강의에서 전달하는 내용이 다소 불분명해지기 때문에 현재 상태를 그대로 유지하는 것으로 정리했습니다.

강의 예제와 관련해서 자세한 논의는 아래 질문들을 참고해 주시면 감사하겠습니다.

- DiscountPolicy 구현 및 설계에 대해 궁금한 점이 있습니다.
- 영화 예매 예제에서 결합도 문의드립니다.

 

답변이 되었는지 모르겠네요. :)

OR님의 프로필 이미지
OR
질문자

상세한 답변 감사 드립니다. 😀

덕분에 순환 참조와 순환 의존성이 엄밀히 말하면 다른 것이라는 것을 이해하게 되었습니다.

생각해보니 "참조"와 "의존성"이 같은 의미는 아니네요.

Movie와 Screening의 순환 의존성을 그대로 유지하느냐 없애느냐는 여러 트레이드오프를 고려해서 강의에서는 그대로 유지한다는 것으로 이해했습니다.

OR님의 프로필 이미지
OR

작성한 질문수

질문하기