[Clean code] chapter 6. 객체와 자료 구조

3 minute read

Overview

우리는 변수와 관련한 의존성을 낮추기 위해서 변수는 private으로 선언한다. 하지만 getter, setter 함수는 모두 공개해서 만드는데 왜 그럴까!

자료 추상화

public class Point{
  public double x;
  public double y;
}
public interface Point{
  double getX();
  double getY();
  void setCartesian(double x, double y);

  double getR();
  double getTheta();
  void setPolar(double r, double theta);
}

위에 두 코드를 보게 되면 모두 2차원 위에 점을 표현하지만 하나는 구현을 공개했고 하나는 구현을 공개하지 않았다. 첫번째 것은 명확하게 직교좌표계를 사용한 것이고 그것뿐만 아니라 개별적 좌표값을 읽고 설정해야 한다. 하지만 두번째 코드는 접근 정책을 강제한다. 뿐만 아니라 값을 개별적으로 적용하는 것이 아닌 한꺼번에 설정한다.

변수를 private하더라도 각 값마다 getter, setter을 만드는 것은 구현을 외부로 노출하는 것과 같다.

단순히 함수를 만든다고해서 구현이 감춰지지 않는다. 인터페이스를 통해서 구현을 숨기고 자료의 핵심을 조작할 수 있어야지 구현을 숨긴 채로 사용자는 이용하는 것이다.

여기서 궁금한 것은 모든 클래스를 만들 떄 인터페이스를 만들어야 되나?

자료/객체 비대칭

객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개한다. (안에 있는 정보는 숨기고 함수만 공개)

자료구조는 자료를 그대로 공개ㅏ며 별다른 함수는 제공하지 않는다.

두 개념은 상반된 개념이다. 본질적으로 완전히 다르다.

우리는 기본적으로 절차지향으로 개발을 했을 경우 불편한 점이 많다고 이야기한다. 하지만 객체를 사용해서 구현하는 것에도 단점이 존재한다.

절차와 자료 구조만을 이용해서 구현한 경우 새로운 함수를 만드는 것에 대해서 큰 어려움이 없다. 그 이유는 각 클래스에 해당 함수를 구현할 필요가 없다.

하지만 객체를 이용해서 구현하는 경우에는 하나의 함수를 만들고 싶은 경우 모두 구현해야 되기 때문에 불편하다. 하지만 당연히 새로운 객체를 추가해서 사용하는 경우에는 편하다.

객체 지향 코드에서 어려운 변경은 절차적인 코드에서 쉬우며, 절차적인 코드에서 어려운 변경은 객체 지향 코드에서 쉽다!

디미터 법칙

모듈은 자신이 조작하는 개체의 속사정을 몰라야 한다는 법칙이다. 객체는 자료를 숨기고 함수를 공개한다. 즉, 객체는 조회 함수로 내부 구조를 공개하면 안 된다는 의미다.

객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출하면 안된다는 이야기이다. 이 과정에서 객체 내부에 대해서 공개하는 것과 같다.

밑에 있는 코드는 해당 법칙을 어긴 것 같다.

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

이 법칙을 왜 지켜야 되는거죠? 내부에 존재하는 객체? 자료를 밖으로 노출하기에 그런 것인가요?

기차 충돌

흔히 위와 같은 코드를 기차 충돌이라고 한다. 일반적으로 조잡하다 여겨지는 방식이므로 피하는 편이 좋다.

위 코드를 밑에 코드로 바꿔서 사용해는게 좋다.

Options opts = ctxt.getOptions()
File scratchDir = opts.getScratchDir()
final String outputDir = scrartchDir.getAbsolutePath();

하지만 위에 코드도 다마토 법칙을 어긴다고 말해야될지도 모른다. 그 기준은 해당 친구들이 객체라면 위반한 것이고 자료 구조라면 당연히 내부 구조를 노출하는 것이니 법칙이 적용되지 않는다.

final String outputDir = ctxt.options.cratchDir.absoulutePath;

자료 구조는 무조건 함수없이 공개 변수만 포함하고 객체는 비공개 변수와 공개 함수를 포함한다고 이야기하면 문제는 매우 쉬워진다. 하지만 그렇지 않다.

잡종 구조

반은 객체 구조를 따르고 반은 자료 구조인 잡종 구조이다. 이것은 두 개의 단점만을 모았다.

이런 구조는 새로운 함수를 추가하기도 어렵고 새로운 객체를 추가하는 것도 어렵다.

구조체 감추기

만약 ctxt라는 친구가 객체라고 한다면 내부를 공개해서는 안된다.

그러면 이 친구가 내부를 공개해서라도 얻으려고 했던 것은 무엇인가?

밑에 코드를 보니 임시 파일을 만들기 위해서 절대 주소를 얻었던 것이다. 그렇다면 이렇게 수정할 수 있을것이다.

BufferdOutputStream bos = ctxt.createScratchFileStream(classFileName);

이런 형태로 하게 되면 내부 구조를 보여주지 않고 해당 로직을 실행할 수 있게 된다.

자료 전달 객체

자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스이다. 대표적으로 DTO가 여기에 속한다. DTO는 굉장히 유용한 구조체다. 특히 데이터베이스와 통신하거나 소켓에서 받은 메시지를 구문을 분석할 때 유용하다.

데이터베이스에서 가공되지 않은 데이터를 실제로 사용하기 위한 데이터로 만들기 위해서 사용되는 자료 구조체이다.

활성 레코드

활성 레코드는 DTO의 특수한 형태다. 공개 변수가 아니라 비공개 변수에 getter, setter 함수가 있는 자료 구조이지만, 대개 save나 find와 같은 탐색 함수도 제공한다.

활성 레코드는 데이터베이스 테이블이나 다른 소스에서 자료를 직접 변환한 결과다.

이 활성 레코드에 비지니스 규칙 메소드를 추가해 이런 자료구조를 객체로 취급하는 개발자가 매우 흔하다. 이렇게 되면 자료 구조가도 아니고 객체도 아닌 잡종 구조가 된다.

해결책은 당연하다. 활성 레코드는 자료 구로조 취급한다. 비지니스 규칙을 담으면서 내부 자료를 숨기는 개체는 따로 생성한다.

결론

객체는 동작을 공개하고 자료를 숨긴다. 그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가할 수 있다. 그 대신 새로운 동작을 추가하기는 어렵다.

자료 구조는 별 다른 동작 없이 자료를 노출한다. 그래서 새동작을 추가하기는 쉬우나 새로운 자료 구조를 추가하기는 어렵다.

key point

  • 객체와 자료구조는 매우 다른 특징을 가지고 있다. 그러기 때문에 그 두 분류를 잘 생각해서 개발해야지 좋은 코드가 될 수 있다.
  • DTO에 쓰잘데기 없는 것을 넣게 되면 이도저도 안되는 친구가 된다.

궁금한 점

마무리

한번도 이런 생각을 해본적이 없었다. 객체와 자료구조는 비슷한 친구들인 줄 알았지만 이렇게 정의를 하고 사용을 하니 확실한 차이가 보이고 그것을 통한 장단점이 보이는 것 같다.

개발을 진행하면서 계속 신경쓰기 어렵겠지만 내가 어제 짠 코드에서 문제점을 발견했으니 고쳐야겠다.

Leave a comment