게시글
질문&답변
2024.11.28
안녕하세요. 기존의 추상화된 역할에 대해서 새로운 협력자가 필요하게 되는 경우는 어떻게 설계해야할까요??
윤철님 안녕하세요.좋은 질문 남겨주셔서 감사합니다. 🙂추상화의 역할은 '현재 알고 있는' 변경을 캡슐화해서 코드 수정으로 인해 받을 수 있는 영향을 최소화하는 것입니다.여기에서 '현재 알고 있는' 변경이라는 점이 중요한데 어떤 부분이 변경될지 모르는 상황에서 예상에 기반해서 설계할 경우 불필요한 추상화를 도입하게 되고 결과적으로 코드를 수정하게 되기 때문입니다.현재 알고 있는 변경은 Screening의 데이터에 따라 할인 여부가 달라진다는 사실이기 때문에 Screening에 의존하고 있지만 그 시점에 어떤 데이터가 필요할지 알지 못하기 때문에, 그리고 Screening이 아닌 다른 요소들에 기반해서 할인 여부를 판단하도록 요구사항이 변경될지 알지 못하기 때문에 현재와 같은 클래스 구조를 유지했다고 생각하시면 될 것 같아요.실제로 Screening 이외의 데이터를 필요로하지 않는다면 현재 코드는 그 사실을 명확하게 보여줍니다.실제로 코드가 변경되고 새로운 추상화가 필요하다면 그 사실을 코드에 반영하면 됩니다.말씀하신 경우에는 객체가 아닌 가격 계산에 필요한 항목들을 포함하는 새로운 객체를 추가해서 해결할 수 있을 것 같습니다.public class PriceFactors { private Screening screening; private Customer customer; public PriceFactors(Screening screening, Customer customer) { this.screening = screening; this.customer = customer; } public Money getFixedFee() { return screening.getFixedFee(); } public boolean isSequence(int sequence) { return screening.isSequence(sequence); } public LocalDate getBirthdate() { return customer.getBirthdate(); } }그리고 Movie, DiscountPolicy, DiscountCondition의 오퍼레이션이 PriceFactors를 전달받도록 수정합니다.public interface DiscountCondition { boolean isSatisfiedBy(PriceFactors factors); }이제 BirthdateCondition을 다음과 같이 구현할 수 있습니다.public interface BirthdateCondition implements DiscountCondition { @Override public boolean isSatisfiedBy(PriceFactors factors) { return factors.getBirthdate().equals(LocalDate.now()); } }새로운 요구사항으로 인해 많은 클래스를 수정해야 하기 때문에 변경 전의 코드는 Screening 이외의 다른 요소를 이용해서 할인 여부 확인에 대해서는 취약한 설계라고 할 수 있습니다. 여기에서 긴장은 PriceFactors를 프로젝트 처음부터 추가할 수 없었느냐인데 새로운 할인 대상이 추가된다는 사실이 확정이 아닌 상태에서 기존 코드에 PriceFactors를 추가했다면 코드가 불필요하게 복잡해 보일것입니다. 다른 관점에서 Screening에 대해 모든 클래스가 의존하기 때문에 PriceFactors를 미리 추가하는 것이 도움이 된다고 생각할 수 있습니다. 이 경우 PriceFactors를 미리 추가하는 것이 더 합리적이라고 볼 수 있습니다.결국 PriceFactors와 같은 새로운 요소를 미리 도입할지 여부는 해당 변경에 대한 발생가능성과 발생했을 때 코드를 수정하기가 얼마나 어려운지에 따라 달라지게 됩니다.이제 PriceFactors를 이용해서 리팩터링했기 때문에 현재의 설계는 이런 유형의 변경을 안정적으로 추가할 수 있게 됐습니다. 하지만 현재 예상할 수 없는 새로운 유형이 요구사항이 추가된다면 리팩터링된 코드 역시 다시 수정될 수 밖에 없습니다.여기에서 눈여겨보실 부분은 Screening, Movie, DiscountPolicy, DiscountCondition의 역할과 협력 방식은 변경되지 않았다는 점입니다. 현재의 설계는 인터페이스는 변경되지만 객체의 역할과 책임 관점에서는 안정적이라고 할 수 있습니다.다음과 같이 정리할 수 있을것 같아요.예상하지 못했던 새로운 유형의 요구사항이 추가됐을 때 변경하지 않고 해당 요구사항을 수용할 수 없습니다. 현재의 요구사항에 적합한 추상화를 선택하세요.요구사항이 변경된다면 변경된 요구사항에 적합한 추상화를 이용해서 코드를 수정하세요. 하지만 예측에 기반해서 추상화를 도입하지 말고 실제로 변경이 일어날 때까지 기다렸다가 적합한 추상화를 도입하세요.요구사항이 너무 복잡해져서 현재의 추상화에 적합하지 않다면 전체적인 역할, 책임, 협력을 수정해야할 수도 있습니다. 현재의 추상화를 버리고 변경된 요구사항에 적합한 새로운 역할, 책임, 협력을 찾으세요.질문 중에 테스트가 매번 깨지는 상황이 발생할 수 있다고 하신 부분이 있는데 이건 아마도 테스트가 실패한다는 의미가 아니라 테스트 코드를 수정해야 하는 상황을 말씀하신 것 같아요. PriceFactors를 도입한 후에는 Customer와 Screening과 같은 요소에 기반해서 할인 정책이 변경되는 경우에는 테스트 코드를 수정할 필요가 없을 거에요. 하지만 기존에 알지 못했던 새로운 유형의 요구사항이 추가된다면 당연히 테스트 케이스는 수정될 수 밖에 없을 것입니다.정말로 코드 수정에 의해 테스트가 매번 수정된다면 3번과 같이 현재의 역할, 책임, 협력을 다시 고민해야 하는 좋은 기회로 삼을 수 있을것 같아요.요약하면 추상화와 캡슐화는 현재 알고 있는 변경을 드러낼 수 있는 것이면 됩니다. 미래의 요구사항은 알 수 없기 때문에 예상에 기반해서 불필요한 추상화를 미리 도입해서는 안됩니다. 대부분의 경우에 역할과 책임을 납득할만하게 할당하고 클래스와 메서드를 작게 유지한다면 생각보다 코드를 수정하기가 어렵지 않을거에요.답변이 되었는지 모르겠네요. 😄
- 1
- 2
- 34
질문&답변
2024.11.17
도메인 추출 방법
JMJ님 안녕하세요. 좋은 질문 남겨 주셔서 감사합니다.애플리케이션 개발에 적합한 도메인 개념을 찾는 일은 저를 포함해서 모든 사람들이 어렵게 생각하는 작업이기 때문에 창피해 하실 일이 전혀 아닙니다.도메인 개념을 찾기 위한 다양한 패턴이나 카탈로그들이 제시되어 왔지만 모든 경우에 적용할 수 있는 최적화된 해법은 존재하지 않는 것으로 보입니다.애플리케이션마다 가정하고 있는 제약조건이 다르고 요구사항의 변화 양상이 다르기 때문에 어떤 도메인에 적합한 도메인 개념은 이것이다라고 말하기 어려운 것 같아요.다양한 자료를 보시고 많은 프로젝트 경험이 쌓일수록 조금 더 수월해지기는 하지만 아직도 여전히 어려운걸 보면 경험이 모든 것을 해결해 주지는 못하더라구요.제가 가장 쉽고 직관적이리고 생각하는 가이드는 다음과 같습니다.현재 시점에서 가장 단순하게 생각되는 개념들을 기반으로 우선 코드를 구현해봅니다.처음부터 모든 개념을 정확하게 추출하려고 하지말고 우선 생각나는 가장 간단한 개념들을 기반으로 코드를 작성하면서 고민했던 개념들이 현재 요구사항에 적합한지 최대한 빠르게 피드백을 받는 것이 좋습니다. 구현 시에 클래스가 응집도나 결합도 관점에서 너무 커지거나 복잡해진다면 새로운 개념을 추가한 후 일부 책임을 새로운 개념에 맡깁니다.이렇게 코드의 퀄리티를 높여야 하는 시점이 되면 도메인에 대해 좀 더 깊게 고민하게 되고 자연스럽게 새로운 도메인 개념이 도출되게 됩니다.요구사항이 변경되면 현재의 코드가 요구사항을 수용하기에 적합한지 판단하고 필요하면 새로운 도메인 개념을 기반으로 코드를 리팩터링합니다.요구사항이 변경되는 시점이야말로 새로운 도메인 개념을 추출할 수 있는 적절한 타이밍입니다. 지금까지 적합하지 않아 보이던 도메인 개념이 위치를 찾고, 적합해 보이던 도메인 개념이 여러 개의 더 작은 도메인 개념으로 나뉘기도 합니다. 리팩토링을 통해 새롭게 얻은 도메인 지식을 코드에 반영해야겠죠.도메인 개념이 고정된 것이 아니고 코드를 구현하거나 요구사항이 변경됨에 따라 지속적으로 변경된다는 사실을 의도적으로 인식하고 언제라고 리팩토링하신다면 적절한 시기에 적합한 도메인 개념들을 식별하실 수 있으실거에요.여기에 아래 인프런 AI가 제시하는 가이드가 나와있는 다양한 자료를 학습하시고 실제 프로젝트에서의 경험치를 쌓으신다면 좀 더 수월해지실 거라고 생각합니다. JMJ님뿐만 아니라 모든 분들이 어려워하시는 영역이고 한번에 적절한 도메인 개념들을 모두 식별하는 것은 어려운 작업이라서 지속적으로 리팩토링을 통해 적절한 도메인 개념을 식별한다는 마음으로 접근하시면 부담이 적어지실 거에요. 답변 중에 좀 더 세부적인 내용이 궁금하시면 질문해 주세요. 🙂 즐거운 주말 보내세요!
- 1
- 2
- 63
질문&답변
2024.11.04
5-4- 예제. 애플리케이션 객체 추가하기 강의내용 관련 질문이 있습니다. (DAO)
모아무개님 안녕하세요.좋은 질문 주셔서 감사합니다. 🙂 정확하게는 인터페이스와 구현의 분리(separation of interface and implementation) 원칙에 해당되며 흔히 구현이 아닌 인터페이스에 대해 프로그램하라(program to an interface, not implementation)라는 가이드로 설명되고는 합니다. 예제에서 서비스가 DAO 구현 클래스가 아닌 인터페이스에 의존하는 이유는 불필요한 변경에 서비스 레이어가 영향을 받지 않게 만들고 싶기 때문입니다.구현 방식과 독립적으로 인터페이스(역할의 개념으로 생각하셔도 됩니다)와 오퍼레이션(책임의 개념으로 생각하셔도 됩니다)을 정의하고 클라이언트가 인터페이스에 의존하도록 만들면 https://www.inflearn.com/community/questions/1355997에서 설명한 것처럼 구현을 교체하거나 구현의 변경에 영향을 받지 않도록 좀 더 안정적인 코드 구조를 설계할 수 있게 됩니다. 질문 주신 내용은 결합도와 캡슐화와 연관이 깊은데요이 주제는 6-3. 결합도와 6-4. 캡슐화에서 자세히 다루고 있습니다. 답변이 되었는지 모르겠네요. 🙂
- 1
- 2
- 77
질문&답변
2024.11.04
ReservationService 구현에서 SRP에 대해서 질문이 있습니다.
fzwoongbin닌 좋은 질문 주셔서 감사합니다. 🙂 1. reserveScreening과 SRP일반적으로 SRP라고 할 때는 주로 클래스를 대상으로 이야기하는 경우가 많지만 말씀하신 것처럼 메서드 역시 SRP의 대상이 될 수 있습니다.강의에서도 말씀드렸던 것처럼 사실 SRP는 응집도와 관련된 이야기입니다.하나의 변경 이유만 가지도록 클래스나 메서드를 구현한다면 응집도가 높아지기 때문에 결과적으로 SRP를 만족하게 됩니다.reserveScreening 메서드는 예약을 하기 위해 필요한 객체를 읽고, 도메인 로직에 요청을 위임하고, 수정된 결과를 저장하는 로직만 포함합니다.데이터를 읽거나 저장하고 상영이 예매되는 구체적인 로직은 별도의 객체들 안에 구현되어 있고 reserveScreening은 이들을 조합해서 상영을 예매하기 위한 전체 플로우를 완성시킵니다.HTTP 요청 파라미터가 변경되거나, 요청 데이터 검증 로직이 변경되거나, 반환값이 변경되거나, Screening과 Movie 사이의 협력이 변경되거나, SQL문이 변경될 경우에는 변경되지 않습니다.결과적으로 reserveScreening 메서드를 살펴보면 예약을 하기 위해 필요한 플로우가 변경될 때만 코드를 수정하게 됩니다.다른 기능은 다루지 않고 오직 예약을 위한 플로우만 담당하고 있습니다.따라서 reserveScreening은 응집도가 높다고 할 수 있으며 SRP를 만족한다고 할 수 있습니다. 2. Controller -> Repository 호출개인적으로 Service를 두는 쪽을 선호합니다.Controller에서 Repository를 직접 조회할 경우 비즈니스 로직이 Controller로 누수되는 경우도 많아지고 이후에 조회할 데이터가 늘경우 Controller가 불필요하게 복잡해지는 경향이 있게 됩니다.Service에 메서드를 두면 메서드 이름을 통해 조회하려는 의도를 전달할 수도 있구요.Controller는 프리젠테이션 로직만 담당하게 하고 데이터를 조회하고 변환하는 로직은 별도의 Service에 두는 것이 유지보수 측면에서 더 좋은 선택이라고 생각합니다. 3. 서비스 레이어의 역할앞에서 설명드린 것처럼 서비스 레이어는 플로우를 조율하는 역할을 담당합니다.플로우에서 실행되는 구체적인 로직은 Repository 구현체나 Screening과 같은 별도의 객체에서 수행되고 서비스 레이어는 이 객체들을 호출해서 전체적인 플로우를 완성합니다.간단히 말해서 서비스 레이어는 기술적인 요소와 도메인 객체 사이의 흐름(구체적인 구현이 아니라 호출만 합니다)을 조율하는데 필요한 책임을 담당합니다.비즈니스 관점에서 연산을 수행하는데 필요한 도메인 로직의 호출, 트랜잭션이 관리, 데이터베이스 조회/저장, 메시징을 통한 통지 등의 흐름 관리가 서비스 레이어의 주역할입니다. 답변이 되었는지 모르겠네요. 🙂
- 1
- 2
- 38
질문&답변
2024.10.25
객체지향 설계 관련하여 궁금한것이 있습니다.
모아무개님 안녕하세요.좋은 질문 해주셔서 감사합니다. 강의의 앞부분에서는 도메인에 포함되는 개념과 관계를 코드로 옮기는 방법을 중심으로 객체지향 설계 방법을 설명하고 있습니다.이렇게 구현된 클래스들은 레이어 아키텍처에서 일반적으로 이야기하는 도메인 레이어에 위치하게 되며 자바에서는 패키지를 이용해서 레이어를 구분하게 됩니다.이 패키지의 이름은 말씀하신 것처럼 domain으로 짓는 것이 일반적입니다. 하지만 애플레케이션이 실제로 동작하기 위해서는 도메인 객체만으로는 충분하지 않습니다.도메인과 무관한 객체들도 필요한데 Spring을 사용하는 환경에서 이 객체들은 아래 인프런 AI 인턴이 설명하는 것처럼 repository, service, controller와 같은 이름이 붙은 패키지 아래 배치가 될것입니다.이 부분에 대해선 "5-4. 애플리케이션 객체 추가하기"에서 좀 더 자세히 설명하고 있습니다. 강의를 들으시는 분들 중에 JPA를 모르시는 분들도 계시기 때문에 강의에서는 JPA 대신 Spring의 JdbcClient를 기반으로 네이티브 쿼리를 사용하고, 퍼시스턴스 메커니즘을 담당하는 객체의 이름에 repository 대신 dao라는 이름을 붙이고 있지만 JPA를 사용하더라도 기본적인 구조는 동일하게 가져갈 수 있습니다.이론적인 부분에 대해서는 "5-4. 애플리케이션 객체 추가하기"를 참고해 주시고, 코드의 예는 "5-4-예제. 애플리케이션 객체 추가하기" 강좌를 참고해 주시면 감사하겠습니다. 보시고 궁금한 부분이 있으면 다시 문의해 주시면 답변 드리도록 하겠습니다.남은 강의도 재미있게 보시고 꼭 완강하시길 바랄게요. 🙂
- 1
- 2
- 133
질문&답변
2024.10.14
2-4-예제, 절차에서 객체로 코드 오타 제보드립니다.
류재준님 안녕하세요. 🙂 제보해 주신 부분에 else 문이 들어가야 하지만 else문이 생략되고 if 문만 사용된 것을 확인했습니다.확인해보니 다행히 코드를 설명하는 동영상과 배포된 pdf에는 else 문이 정상적으로 사용되어 있어서 큰 이슈는 없지만 말씀해 주신 실습 동영상과 github에는 else문이 누락되어 있습니다.실행 결과에는 영향을 주지 않지만 표현력과 일관성 측면에서 오해를 불러일으킬 수 있어 github의 코드를수정했습니다.다만 동영상 자료는 세밀한 수정이 필요해서 일단 현재 상태를 유지하고 시간이 날 때 다시 업로드하도록 하겠습니다.강의 자세히 들어주시고 세세한 부분까지 확인해 주셔서 정말 감사합니다!
- 1
- 2
- 64
질문&답변
2024.10.12
다형성을 이용한 역할 디자인과 그에 대응되는 영속성 저장소에서의 모델 디자인의 괴리
지호선님 안녕하세요.먼저 좋은 질문 남겨주셔서 감사합니다. :) 개인적으로 특별한 경우가 아니라면 ORM의 엔티티를 도메인 클래스로 사용하는 쪽을 선호하는데 jpa를 사용하는 경우 퍼시스턴스 처리에 대해에서 간단히 설명드렸으니 해당 질문을 참고해 주시면 감사하겠습니다.이렇게 ORM 엔테티를 도메인 클래스로 사용하는 경우에는 (정규화 원칙에 다소 위배되더라도) 데이터베이스 테이블을 도메인 클래스에 매핑하기 쉬운 방식으로 설계하는 편입니다. 하지만 클래스 설계와 테이블 설계는 기반을 두고 있는 패러다임 자체가 다르기 때문에(임피던스 불일치) 완전히 동일한 형태로 설계할 수는 없습니다.데이터베이스 설계는 데이터의 중복을 최소화하는 관점에서 접근하기 때문에 입도가 크지만 클래스는 행동 관점에서 접근하기 때문에 입도가 작습니다.따라서 JPA의 Embeddable을 이용해서 엔티티와 값 객체를 하나의 테이블에 함께 저장하는 경우처럼 테이블을 클래스에 매핑할 경우에는 하나의 테이블에 여러 개의 클래스를 매핑하는 것이 일반적입니다.특히 다형성을 활용하기 위해 상속을 사용하는 경우에는 유지보수와 쿼리의 편의성을 위해 단일 테이블 방식으로 매핑하는 것이 일반적인데 이 경우 상속 계층에 속하는 다수의 클래스를 하나의 테이블에 매핑시킵니다. 결론적으로 데이터베이스 설계와 객체 설계 각각의 지향점은 다르기 때문에 다를 수 밖에 없습니다.ORM이 모든 매핑 이슈를 해결해 주지는 않기 때문에 데이터베이스 설계와 객체지향 설계 관점에서 어느정도 타협을 할 수 밖에 없고, 결과적으로 각각의 설계 관점에서 최적은 아닐지라도 양쪽 모델을 직관적으로 매핑할 수 있는 구조를 선택하는 것이 현명하다고 생각합니다. 답변이 되었는지 모르겠네요. 🙂
- 2
- 2
- 101
질문&답변
2024.10.08
Movie 객체와 Screening 객체의 순환참조?
OR님 안녕하세요.강의가 재미있다니 다행이네요. :)먼저 질문 주신 내용에 대한 답변을 드리면 예제는 순환 참조에 해당하지 않습니다.순환 참조를 설명드리기 전에 먼저 순환 의존성의 개념을 설명 드리는게 좋을것 같네요.어떤 클래스에서 시작해서 의존성 그래프를 따라 이동할 때 다시 자기 자신으로 돌아오는 경우 순환 의존성(circula dependency 또는 cyclic dependency)이 존재한다고 합니다.예제 코드의 경우 Screening에서 의존성 그래프를 따라 이동하면 다시 Screening으로 돌아오기 때문에 순환 의존성이 존재합니다.순환 의존성을 구성하는 의존성은 직접적일 수도 있고 간접적일 수도 있습니다. 질문에 적어주신 Team과 Member 사이의 관계는 일반적으로 양방향 연관관계(bi-directional assoication)라고 부릅니다.양방향 참조(bi-directional reference)라고 표현하는 경우도 종종 있는데 대부분의 경우에는 양방향 연관관계라는 용어를 사용합니다.양방향 연관관계는 두 개의 객체 참조를 동기화시키는 방식으로 구현합니다. 자료를 찾다보면 순환 참조(cyclic reference)라는 용어를 보게되는데 직접적인 객체 참조로만 구현된 순환 의존성의 특별한 경우를 가리키는 개념으로 사용됩니다.책이나 자료를 살펴보면 일반적으로 순환 참조를 의미하는 경우에도 순환 의존성이라는 용어를 많이 사용하지만 객체 참조로 구성된 순환 의존성이라는 개념을 강조하고 싶은 경우에는 순환 참조라는 용어를 사용하는 것으로 보입니다.스프링에서도 빈 초기화에 실패한 경우의 예외 메시지에서도 순환 참조라는 용어를 사용합니다.양방향 의존관계는 2개의 클래스 사이에서 발생하는 순환 참조로 정의할 수 있을것 같아요.결과적으로 예제에서 Screening과 Movie 사이의 의존성은 객체 참조로 구현되어 있지만 그 이후에는 파라미터로 구현되어 있기 때문에 순환 의존성은 존재하지만 순환 참조는 존재하지 않습니다.정의에서 알 수 있는 것처럼 객체 참조가 아닌 의존성을 포함하는 순환 의존성보다는 순환 참조의 결합도가 높습니다. 전체적으로 설계의 품질을 급격하게 낮추지 않는 선에서 복잡성을 낮출 수 있는 경우나 객체 그룹의 경계를 명확하게 관리할 경우에는 양방향 의존성을 포함해서 순환 의존성을 유지하는게 더 나은 경우도 있습니다.테스트나 유지보수성의 관점에서 불필요한 순환 의존성을 제거하는 것이 좋지만 현재 설계에서는 순환 의존성을 제거할 경우 불필요하게 복잡성이 높아지고 강의에서 전달하는 내용이 다소 불분명해지기 때문에 현재 상태를 그대로 유지하는 것으로 정리했습니다.강의 예제와 관련해서 자세한 논의는 아래 질문들을 참고해 주시면 감사하겠습니다.- DiscountPolicy 구현 및 설계에 대해 궁금한 점이 있습니다.- 영화 예매 예제에서 결합도 문의드립니다. 답변이 되었는지 모르겠네요. :)
- 1
- 1
- 101
질문&답변
2024.10.06
2-2 변경과 의존성 영상 편집 오류 제보 드립니다.
류재준님 안녕하세요 제보해주신 부분에 편집 오류가 있는 것이 맞네요.수정해서 다시 올렸습니다. 강의 수강하시는데 번거롭게 해드려 정말 죄송하고 오류가 있는 부분 제보해 주신 점 감사드립니다.혹시라도 보시면서 불편한 부분 있으면 또 말씀해 주세요. 감사합니다!
- 1
- 2
- 69
질문&답변
2024.10.03
영화 예매 예제에서 결합도 문의드립니다.
하하님 안녕하세요.먼저 좋은 질문 남겨주셔서 감사합니다.강의에서 설명드린 것처럼 결합도는 변경이될 때 함께 변경될 수 있는 가능성을 의미합니다.그리고 의존성은 자주 변경되는 요소에서 상대적으로 덜 자주 변경되는 요소쪽으로 향하도록 구성해야 합니다.예제에는 Screening에 비해 DiscountCondition들이 더 자주 변경된다고 전제하고 있다는 사실을 기반으로 설명을 진행하도록 하겠습니다. 🙂1번 질문 답변결론부터 말씀드린 다면 잠재적으로 많은 코드들이 Screening을 참조하고 있고 Screening이 변경될 경우 영향을 받는 코드가 많아질 수 있기 때문에 맥락을 고려하지 않고 순순하게 코드만 본다면 결합도가 높다고 할 수 있습니다.다만 코드에서의 의존성은 징후를 나타낼 뿐 실제로 중요한 것은 얼마나 자주, 그리고 어떤 부분에서 변경이 발생하는 지 살펴보는 것입니다.실제로 Screening이 자주 변경되고 이에 따라 의존하는 코드들이 함께 변경된다면 결합도가 높다고 할 수 있고, Screening이 자주 변경되지 않는다면 결합도가 낮다고 할 수 있습니다.코드를 자세히 보시면 대부분은 Screening의 클래스 이름에 의존하고 있다는 사실을 알 수 있을 거에요.SequenceCondition과 PeriodCondition에서 참조하는 메서드들은 단순한 getter가 아니라 협력을 하면서 실제로 필요하다고 판단된 메시지에 기반하고 있습니다.결론적으로 Screening이 안정적이고 클래스의 명칭이 변경될 확률이 높지 않다면 결합도는 예상보다 낮다고 말할 수 있습니다그렇더라도 결합도를 낮추도록 현재의 코드를 개선하는 것은 항상 바람직한 일입니다.다만 코드가 불필요하게 복잡하지 않도록 개선 전과 후를 트레이드오프 하시는게 좋겠습니다. 코드 사이즈가 크지 않고 앞으로 어떤 부분에서 변경이 발생할지 예상하기 어렵다면 현재의 코드를 유지하고 실제로 변경이 발생할 때까지 기다리는게 좋습니다(현재 코드는 이런 가정에 기반하고 복잡성을 추가하지 않았습니다).2번 질문 답변예제에서는 Movie가 DiscountPolicy에 의존하고 있기 때문에 Movie가 DiscountPolicy에 의존하고 있다고 가정하겠습니다. 이 경우 다음 처럼 DiscountPolicy 안에서 if/switch 등의 분기문을 이용해서 타입 기반으로 어떤 할인 정책을 이용할 지 결정하도록 코드를 구현할 수 있습니다.class DiscountPolicy { DiscountPolicyType type; public Money calculateDiscountAmount() { switch(type) { case AMOUNT_POLICY: ... case PERCENT_POLICY: ... } } }이 경우 Movie는 DiscountPolicy의 calculateDiscountAmount 메시지에만 의존하고 있습니다.따라서 이 메시지가 변하지 않는다면 DiscountPolicy의 변경에 Movie가 영향을 받지 않기 때문에 결합도 측면에서는 그렇게 큰 차이가 발생하지는 않습니다(다만 DiscountPolicy가 구체 클래스라면 DiscountPolicy를 수정할 일이 더 많아질 수 있고 그 결과 Movie가 영향을 받을 확률이 높아지기 때문에 이 문제가 발생한다면 결합도를 낮추는게 좋겠죠).여기에서 결합도를 낮추게 만드는 동기는 결합도 자차게 아니라 DisocuntPolicy의 응집도를 높이려는 시도입니다.DiscountPolicy의 내부 응집도는 낮기 때문에 응집도를 높이는 쪽으로 설계를 개선해야 하고 결과적으로 DiscountPolicy를 추상 클래스나 인터페이스로 구현하게 되기 때문에 자연스럽게 Movie와 DiscountPolicy 사이의 결합도는 낮아지게 됩니다.질문에서 "의존성의 개수가 아닌 Ripple effect의 가능성"이라고 말씀해 주셨는데 이 경우 아래와 같은 코드를 생각하신 걸까요? class DiscountPolicy { DiscountPolicyType type; public Money calculateDiscountAmountWithAmountPlicy() { // 금액 할인 정책에 따라 계산 } public Money calculateDiscountAmountWithPercentPlicy() { // 비율 할인 정책에 따라 계산 } }위 코드는 앞의 코드와 달리 결합도가 높은데 DiscountPolicy의 종류가 변경될 때마다(현재 예제에서는 실제 이런 변경이 자주 발생한다고 가정하고 있습니다) Movie도 영향을 받기 때문에 결합도가 높습니다.결과적으로 의존성의 개수가 많으면 변경으로 인해 영향을 받을 확률도 높아지게 되기 때문에 두 가지는 밀접한 관련이 있습니다.GRASP의 LOW COUPLING 패턴을 다시 보시면 결합도를 낮추기 위해 객체 사이의 의존성 개수를 줄이는데 초점을 맞추고 있다는 사실을 알 수 잇습니다.대답이 되었는지 모르겠네요. 🙂
- 2
- 2
- 99