밤빵's 개발일지
[TIL]20241126 디미터의 법칙 본문
디미터의 법칙이란 객체 지향 설계에서 결합도를 낮추고, 유지보수성을 높이는 중요한 원칙이다. 객체 간의 관계를 제한함으로써 복잡성을 줄이고, 응집도를 높이는 것을 목표로 하는 이 법칙을 이해하고, 이를 실제 코드에 어떻게 적용할 수 있을지 고민하면서 학습한 내용을 정리하기로 했다.
▶디미터의 법칙
디미터의 법칙은 객체 지향 설계에서 캡슐화와 정보 은닉을 잘 지키도록 돕는 원칙이다. "친구하고만 대화하라"( 객체는 자신이 직접 참조하고 있는 객체와만 상호작용하고, 다른 객체의 내부 구조를 알지 않도록 설계해야한다는 의미)는 규칙을 통해 객체 간의 의존성을 줄이고, 각 객체가 자신의 역할에 충실하도록 설계할 수 있다. 결합도를 낮추고, 코드의 유연성을 높이는 것을 목표로 한다. 객체 간의 의존성을 줄임으로써, 코드의 변경이 발생하더라도 그 영향이 최소화되고, 유지보수성을 높이는 데 큰 도움이 된다.
디미터의 법칙은 객체 간의 직접적인 의존성을 최소화하는 데 목적이 있다. 객체가 다른 객체의 내부 구조에 접근하지 않고, 필요한 정보를 메시지를 통해 요청하는 방식으로 설계하는 것이다. 이 법칙은 객체 간의 관계가 과도하게 복잡해지는 것을 막아주고, 각 객체가 독립적으로 동작할 수 있도록 돕는다.
▶디미터의 법칙 적용 예시
▷위배된 코드
String paymentStatus = order.getPayment().getTransaction().getStatus();
→ 지금 코드는 여러 객체의 내부에 직접 접근하고 있기때문에, 객체 간의 결합도가 높아지고 유지보수에 취약하게 된다. 만약 Payment나 Transaction 클래스의 구조가 변경되면, 이를 사용하는 모든 코드에 영향을 미치게 된다.
▷디미터의 법칙 준수
String paymentStatus = order.getPaymentStatus();
→ 객체에 메시지를 보내는 방식으로 접근을 단순화하면, 코드의 의존성을 줄이고 변경에 유리한 구조를 만들 수 있다. order 객체는 내부 구조를 외부에 노출하지 않고, 필요한 정보를 제공하는 역할을 하기 때문에 코드의 유지보수성이 향상된다.
▶ 한 줄에 점(.)을 하나만 허용하는 것과의 관계
디미터의 법칙에 대한 글을 봤을때 댓글을 통해 많은 개발자들이 디미터의 법칙을 코드 한 줄에 점(.)을 하나만 찍는 것으로 오해하는 경우가 많다는걸 알게 됐다. 하지만 이 법칙의 본질은 객체 간의 의존성을 줄이는 것이지, 단순히 코드 형식에 관한 것이 아니였다. 메서드 체이닝이나 스트림 API와 같은 경우에는 점이 여러 개 찍히더라도, 객체 간의 내부 구조를 직접 노출하지 않는다면 디미터의 법칙을 위배하지 않는다고 한다.
코드 포매터에서 줄 바꿈을 강제하거나, 체이닝을 유지하는 방식은 가독성을 높이기 위한 것이고, 디미터의 법칙을 지키기 위한 것은 아니다. System.out.println()과 같은 코드에서 점이 여러 번 찍히는 것이 반드시 디미터의 법칙을 위반하는 건 아니라는 거다.
▶객체 설계 추가
이전 프로젝트에서 간단한 결제기능을 구현해봤기때문에 Order와 관련된 간단한 코드를 예시로 사용했다. 그러나, 실제 비즈니스 로직에서 객체 간의 관계는 훨씬 더 복잡할 수 있다. 만약 온라인 쇼핑 시스템에서 Order 객체와 Payment 객체 간의 관계를 Order 객체가 Payment 객체의 내부 구조를 직접 참조하지 않도록 설계하고, 필요한 정보를 메시지를 통해 요청하는 방식으로 디미터의 법칙을 준수하는 코드를 작성해본다면 이 법칙의 적용 범위와 장점을 더 명확하게 설명할 수 있다.
public class Order {
private Payment payment;
public Order(Payment payment) {
this.payment = payment;
}
// 디미터의 법칙을 준수한 메서드
public String getPaymentStatus() {
return payment.getStatus();
}
}
public class Payment {
private Transaction transaction;
public Payment(Transaction transaction) {
this.transaction = transaction;
}
public String getStatus() {
return transaction.getStatus();
}
}
public class Transaction {
private String status;
public Transaction(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
→ 지금 예시에서 Order 객체는 Payment 객체의 내부 구조를 직접 참조하지 않는다. 대신, getPaymentStatus() 메서드를 통해 Payment 객체에 메시지를 보내어 결제 상태를 요청한다. 이를 통해 객체 간의 결합도를 낮추고, 코드의 유지보수성을 높일 수 있다. 만약 Payment나 Transaction의 내부 구조가 변경되더라도, Order 클래스는 이러한 변경에 영향을 받지 않도록 되어 있다.
▶디미터의 법칙의 장단점
장점/단점 | 설명 |
캡슐화 유지 | 객체의 내부 구조를 외부에 노출하지 않음으로써, 캡슐화를 유지할 수 있다. |
결합도 감소 | 객체 간의 결합도를 낮추어, 코드의 변경에 대한 영향을 최소화할 수 있다. |
유지보수성 향상 | 객체 간의 의존성이 줄어들어, 코드의 유지보수성이 높아진다. |
복잡도 증가 가능성 | 디미터의 법칙을 지나치게 엄격히 적용하면, 필요 이상의 메서드를 추가하게 되어 복잡도가 증가할 수 있다. 특히, 단순 데이터 접근에 대해서도 엄격히 제한하면 코드의 직관성이 떨어질 수 있다. |
▶ 개발 과정에서의 고민
디미터의 법칙을 실제로 적용하려 할 때, 어디까지가 적절한지에 대한 고민이 생긴다. 예를 들어, Stream API를 사용하면서 점(.)이 여러 번 찍히는 경우가 디미터의 법칙을 위반하는지에 대한 글을 보면서 스트림 체이닝은 단순히 데이터를 처리하는 흐름을 표현하는 것이기 때문에, 객체의 내부 구조를 노출하지 않는다면 디미터의 법칙을 위반하지 않는다고 볼 수 있다.그리고 실제로 개발을 하다 보면 객체 간의 관계를 완전히 끊어내기 어려운 경우가 많다. 이럴 때는 디미터의 법칙을 엄격하게 적용하기보다는, 객체 간의 책임을 명확히 정의하고, 불필요한 의존성을 최소화하는 방향으로 설계하는 것이 중요하다. 예를 들어, 불필요한 getter 호출을 줄이고, 객체에 메시지를 보내어 원하는 작업을 수행하도록 하는 것이 디미터의 법칙을 지키는 좋은 방법이다.
디미터의 법칙은 객체 지향 설계에서 결합도를 줄이고 유지보수성을 높이는 중요한 원칙이다. 이 법칙을 준수함으로써 코드의 변경에 대한 영향을 최소화하고, 유지보수성을 높일 수 있다. 그러나, 디미터의 법칙을 지나치게 엄격히 적용하는 것은 오히려 코드의 복잡도를 증가시킬 수 있으므로, 상황에 맞게 유연하게 적용하는 것이 필요하다. 이번 개발일지를 통해 디미터의 법칙의 본질과 적용 방법을 학습하면서 코드에 적용했을 때의 장단점을 이해하게 되었다. 앞으로 개발을 진행할 때, 객체 간의 결합도를 최소화하고, 응집도를 높이는 방향으로 설계하려고 노력해야겠다.
'개발Article' 카테고리의 다른 글
[TIL]20241202 TDD 적용하고 기능 구현 하기 (0) | 2024.12.02 |
---|---|
[TIL]20241201 Redis 적용을 위한 공부... (2) | 2024.12.01 |
[TIL]20241125 @Value 충돌 문제와 @PostConstruct (0) | 2024.11.25 |
[TIL]20241124 유틸리티클래스는 악이다? (1) | 2024.11.24 |
[TIL]20241123 PUBG 프로젝트 roster 응답 구조 재작업 (1) | 2024.11.23 |