- Published on
6장. 객체와 자료 구조
6장. 객체와 자료 구조
변수를 private
으로 선언하는 잉는 남들이 변수에 의존하지 않게 만들고 싶어서입니다.
그런데 아이러니 하게 getter
, setter
를 당연하게 제공하며 비공개 변수를 외부에 노출합니다.
왜 그럴까요?
자료 추상화
Getter
, Setter
로 무분별하게 구현을 노출시키는 것이 가장 나쁜 방식입니다.
그러기보다는 구현은 감추고 추상적인 개념으로 표현하는 것이 좋습니다.
필드를 감추고 Getter
, Setter
를 만든다고 캡슐화가 되는것이 아닙니다.
Getter
,Setter
를 일단 모두 넣어 놓은그 모든 곳이 잘못
되었다는 것입니다.
자료/객체 비대칭
객체
는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개자료 구조
는 자료를 그대로 공개하며 별다른 함수는 제공하지 않음
두 정의는 본질적으로 정반대입니다.
절차적인 코드
는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽지만 새로운 자료 구조를 추가하기 어렵습니다.
객체 지향 코드
는 기존 함수를 변경하지 않으면 새 자료구조를 추가하기 쉽지만 새로운 함수를 추가하기 어렵습니다.
이렇기에 모든 것이 객체라는 생각은 미신입니다.
떄로는 단순한 자료 구조와 절차적인 코드가 가장 적합한 상황도 있습니다.
디미터 법칙
디미터 법칙은 잘 알려진 휴리스틱
으로, 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙입니디ㅏ.
디미터 법칙은 클래스 C
의 메서드 f
는 다음과 같은 객체의 메서드만 호출해야 한다고 주장합니다.
- 클래스 C
- f가 생성한 객체
- f 인수로 넘어온 객체
- C 인스턴스 변수에 저장된 객체
위 객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출하면 안됩니다.
즉 낯선 사람은 겨예하고 친구랑만 놀라는 의미입니다.
아래는 디미터 법칙을 위반하는 것 처럼 보입니다.
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
호출한 메서드에서 반환하는 객체의 함수를 또 호출하기 때문입니다.
기차 충돌
흔히 위와 같은 코드
를 기차 충돌이라 부릅니다.
일반적으로 조잡하다 여겨지는 방식이므로 피하는 편이 좋습니다.
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
위 코드처럼 작성하는 것이 좋습니다.
방식의 차이는 제쳐두고 두 방식 모두 디미터 법칙
을 위반하는 것처럼 보입니다.
위반 여부는 ctxt
, Options
, ScratchhDir
이 객체인지 자료 구조인지에 따라 달라집니다.
자료 구조라면 디미터 법칙이 적용되지 않기 때문입니다.
하지만 Getter
함수를 통해 호출함으로써 객체인지 자료 구조인지 헷갈립니다.
final String outputDir = ctx.options.scratchDir.absolutePath;
위 처럼 작성하면 문제는 간단하지만, 단순한 자료 구조에도 Getter
, Setter
를 모두 적용하라 요구하는 프레임워크와 표준(예, Bean)이 존재합니다.
잡종 구조
이런 혼란으로 반은 객체
, 반은 자료 구조
인 잡종 구조
가 나옵니다.
잡종 구조
는 객체와 자료 구조의 단점만 모아 놓은 구조입니다.
프로그래머가 함수나 타입을 보호할지 공개할지 확신하지 못해 어중간하게 내놓은 설계에 불과하므로 피하는것이 좋습니다.
구조체 감추기
ctxt
, options
, scratchDir
이 정말 객체라면 임시 디렉터리의 절대 경로는 어떻게 얻어야 할까요?
우선 디렉터리의 절대 경로를 얻는 이유부터 알아야 합니다.
String ouputFile = outputDir + "/" classNae.replace(',', '/') + ".class";
FileOutputStream fout = new FileOutputStream(ouputFile);
BufferedOutputStream fout = new BufferedOutputStream(fout);
위 로직을 보면 임시 파일을 생성하기 위함이라는 것을 알 수 있습니다.
그럼 ctxt
객체에게 생성하라고 시키는 것이 더 좋습니다.
BufferedOuputStream bos = ctxt.createScratchFileStream(classFileName);
이처럼 작성하면 모듈은 자신이 모르는 여러 객체를 탐색할 필요가 없으므로
디미터 법칙
을 위반하지 않습니다.
자료 전달 객체
자료 구조체의
전형적인 형태는 공개 변수만 있고 함수가 없는 클래스 입니다.
흔히 알고 있는 DTO
가 이것입니다.
좀 더 일반적인 형태는 Bean 구조
입니다.
Bean
은 변수를 Getter
, Setter
를 사용하여 조작합니다.
일종의
사이비 캡슐화
로 별다른 이익을 제공하지는 않습니다.
활성 레코드
활성 레코드
는 DTO의 특수한 형태입니다.
DB 테이블
이나 다른 소스
에서 자료를 직접 변환한 결과
입니다. (Entity
)
여기에 비즈니스 로직을 추가하면 자료 구조도 아니고 객체도 아닌 잡종 구조가 나오게 되므로 옳지 않습니다.
활성 레코드
는 자료 구조로 취급
하고 비즈니스 규칙을 담으면서 내부 자료를 숨기는 객체는 따로 생성하는 것이 옳습니다.