클린코드

[클린코드] 6. 객체와 자료 구조

beekei 2023. 7. 30. 19:51
반응형

자료 추상화

변수를 비공개(private)로 정의하는 이유는 코드들이 변수에 의존하지 않게 만들고 싶어서다.

그렇다면 어째서 get, set 함수는 당연하게 외부에 공개(public)하는가?

알다시피 변수를 private로 설정한들 조회(get), 설정(set) 함수를 public으로 제공한다면 의미가 없는 샘이다.

변수 사이에 함수를 넣는다고 구현이 저절로 감춰지지는 않는다. 구현을 감추려면 추상화가 필요하다!

추상 인터페이스를 제공해 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스이다.

 

만약 휴대폰 배터리가 15% 이하일 때 절전모드를 작동한다고 해보자.

public interface Phone {
    Double getBatteryPercent();
}
if (phone.getBatteryPercent() <= 15) {
    phone.notice("절전모드를 시작합니다.");
}

위 코드는 절전모드에 조건을 로직에서 들어내고 있다. 

자료를 세세하게 공개하기보다는 아래 코드처럼 좀 더 추상적인 개념으로 표현하는 편이 좋다.

public interface Phone {
    Double isSavingModeBatteryPercent();
}
if (isSavingModeBatteryPercent()) {
    phone.notice("절전모드를 시작합니다.");
}

 

개발자는 객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다.

아무 생각 없이 get, set 함수를 추가하는 방법이 가장 나쁘다.

 

자료/객체 비대칭

가끔 코딩을 하다 보면 절차적인 코드와 객체지향 모두 필요한 경우가 있다. 두 가지 유형 중 어떤 것이 100% 옳다고 말하기는 어렵다.

두 가지 유형에는 상호 보완적인 특징이 있다. 사실상 반대라고 할 수 있다. 그래서 객체와 자료구조는 근본적으로 양분된다.

  • 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다.
  • 객체지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.
  • 절차적인 코드는 새로운 자료 구조를 추가하기 어렵다. 그러려면 모든 함수를 고쳐야 한다.
  • 객체지향 코드는 새로운 함수를 추가하기 어렵다. 그러려면 모든 클래스를 고쳐야 한다.

상황에 따라 때로는 단순한 자료구조와 절차적인 코드가 가장 적합한 상황도 있다. 모든 코드에는 정답은 없다 오답만 있을 뿐

 

디미터 법칙

디미터 법칙은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다.

객체는 자료를 숨기고 함수를 공개한다. 즉, 객체는 조회 함수로 내부 구조를 공개하면 안 된다는 의미다.

좀 더 정확하게 표현하자면 디미터 법칙은 "클래스 C의 메서드 f는 다음과 같은 객체의 메서드만 호출해야 한다."라고 주장한다.

  • 클래스 C
  • f가 생성한 객체
  • f 인수로 넘어온 객체
  • C 인스턴스 변수에 저장된 객체

하지만 위 객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출하면 안 된다.

다시 말해, 낯선 사람은 경계하고 친구랑만 놀라는 의미다.

 

아래 코드를 참고해 보자.

String name = myHobby.getBookReading().getBooks().getFavorite().getName();

위 코드는 내 취미인 독서한 책중 가장 좋아하는 책의 이름을 반환하는 코드이다. 

함수 하나가 아는 지식이 굉장히 많고 너무 많은 객체를 탐색할 수 있다.

흔히 이 런 코드를 기차 충돌(train wreck)이라 부른다. 일반적으로 조잡하다 여겨지는 방식이므로 피하는 편이 좋다.

 

위 예제는 조회 함수를 사용하는 바람에 혼란을 일으킨다. 코드를 다음과 같이 구현했다면 디미터 법칙을 거론할 필요가 없다.

String name = myHobby.bookReading.books.favorite.name;

 

자료 구조는 무조건 함수 없이 공개 변수만 포함하고 객체는 비공개 변수와 공개 함수를 포함하다면 문제는 훨씬 간단하리라.

하지만 단순한 자료 구조에도 조회 함수와 설정 함수를 정의하라 요구하는 프레임워크와 표준이 존재하다.

 

이러한 혼란 때문에 때때로 절반은 객체, 절반은 자료구조인 잡종 구조가 나온다.

이런 잡종 구조는 양쪽 구조에서 단점만 모아놓은 구조이기 때문에 새로운 함수는 물론이고 새로운 자료 구조도 추가하기 어렵다.

그러므로 잡종 구조는 되도록 피하는 편이 좋다. 프로그래머가 보호할지 공개할지 확신하지 못해 어중간하게 내놓은 설계에 불과하다.

 

자료 전달 객체

자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스다. 이런 자료 구조체를 때로는 자료 전달 객체(DTO)라고 한다.

DTO는 굉장히 유용한 구조체다. 특히 데이터베이스와 통신하거나 소켓에서 받은 메시지의 구문을 분석할 때 유용하다.

하지만 DTO에 비즈니스 로직을 추가해 이런 자료 구조를 객체로 취급하는 개발자가 너무나 흔하다.

이는 자료구조도, 객체도 아닌 잡종 구조가 나오기 때문에 바람직하지 않다.

 

결론

객체는 동작을 공개하고 자료를 숨긴다.

그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다.

자료 구조는 별다른 동작 없이 자료를 노출한다.

그래서 기존 자료 구조에 새 동작을 추가하기는 쉬우나, 기존 함수에 새 자료 구조를 추가하기는 어렵다.

어떤 시스템을 구현할 때, 새로운 자료 타입을 추가하는 유연성이 필요하다면 객체가 더 적합하고, 새로운 동작을 추가하는 유연성이 필요하다면 자료 구조와 절차적인 코드가 더 적합하다.

우수한 소프트웨어 개발자는 편견 없이 이 사실을 이해해 직면한 문제에 최적인 해결책을 선택해야 한다.

반응형