자료를 세세하게 공개하기보다는 추상적인 개념으로 표현하는 편이 좋다.
아무 생각 없이 조회/설정 함수를 추가하는 방법은 가장 나쁘다.
자료 구조는 자료를 그래도 공개하며 별다른 함수는 제공하지 않음
public class Square {
public Point topLeft;
public double side;
}
public class Rectangle {
public Point topLeft;
public double height;
public double width;
}
public class Circle {
public Point center;
public double radius;
}
public class Geometry {
public final double PI = 3.141592653589793;
public double area(Object shape) throws NoSuchShapeException {
if (shape instanceof Square) {
Square s = (Square) shape;
return s.side * s.side;
} else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape;
return r.height * r.width;
} else if (shape instanceof Circle) {
Circle c = (Circle) shape;
return PI * c.radius * c.radius;
} else {
throw new NoSuchShapeException();
}
}
}
객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개
public class Square implements Shape {
private Point topLeft;
private double side;
public double area() {
return side * side;
}
}
public class Rectangle implements Shape {
private Point topLeft;
private double height;
private double width;
public double area() {
return height * width;
}
}
public class Circle implements Shape {
private Point center;
private double radius;
public final double PI = 3.141592653589793;
public double area() {
return PI * radius * radius;
}
}
상황 | 절차적인 코드 | 객체 지향 코드 |
---|---|---|
새로운 함수 추가 | 쉬움 | 어려움 |
새로운 자료 타입 추가 | 어려움 | 쉬움 |
객체 지향적인 코드와 절차적인 코드는 서로 상호 보완적인 특질이 있다.
디미터 법칙은 잘 알려진 휴리스틱으로, 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다.
final String outputDir = xtxt.getOptions().getScratchDir().getAbsolutePath();
위의 코드는 한 줄로 이어진 기차처럼 보이기 때문에 기차 충돌(train wreck) 이라 부른다.
조잡하다 여겨지는 방식이므로 피하는 게 좋으며, 아래와 같이 나누는 편이 좋다.
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
위의 예제가 디미터 법칙을 위반하는지 여부는 자료 구조인지 객체인지에 달렸다.
때때로 절반은 객체, 절반은 자료 구조인 잡종 구조가 나온다.
잡종 구조는 중요한 기능을 수행하는 함수도 있고, 공개 변수나 공개 조회/설정 함수도 있다.
공개 조회/설정 함수는 비공개 변수를 그대로 노출한다.
덕택에 절차적인 프로그래밍의 자료 구조 접근 방식처럼 비공개 변수를 사용하고픈 유혹에 빠지기 십상이다.
앞선 코드 예제가 진짜 객체라면 임시 디렉터리의 절대 경로는 어떻게 얻어야 좋을까?
// 첫번째 방법. ctxt 객체에 공개해야 하는 메서드가 너무 많아짐
ctxt.getAbsolutePathOfScratchDirectoryOption();
// 두번째 방법. 자료 구조를 반환한다고 가정하면 썩 내키지 않음
ctxt.getScratchDirectoryOption().getAbsolutePath();
ctxt 객체에게 임시 파일을 생성하라고 시키는 방법이 있다.
ctxt는 내부 구조를 드러내지 않으며, 모듈에서 해당 함수는 자신이 몰라야 하는 여러 객체를 탐색할 필요가 없어서 디미터 법칙을 위반하지 않는다.
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);
자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스다.
이런 자료구조체를 때로는 자료 전달 객체(Data Treansfer Object, DTO)라 한다.
특히 데이터베이스와 통신하거나 소켓에서 받은 메시지의 구문을 분석할 때 유용하다.
흔히 DTO는 데이터베이스에 저장된 가공되지 않는 정보를 애플리케이션 코드에서 사용할 객체로 변환하는 일련의 단계에서 가장 처음으로 사용하는 구조체다.
활성 레코드는 DTO의 특수한 형태다.
공개 변수가 있거나 비공개 변수에 조회/설정 함수가 있는 자료 구조지만, 대게 save나 find와 같은 탐색 함수도 제공한다.
객체는 동작을 공개하고 자료를 숨긴다.
그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면,
기존 객체에 새 동작을 추가하기는 어렵다.
자료 구조는 별다른 동작 없이 자료를 노출한다.
그래서 기존 함수에 새 자료 구조를 추가하기는 어려우나,
기존 자료 구조에 새 동작을 추가하기는 쉽다.
시스템을 구현할 때,
새로운 자료 타입을 추가하는 유연성이 필요하다면 객체가 더 적합하고,
새로운 동작을 추가하는 유연성이 필요하다면 자료구조와 절차적인 코드가 더 적합하다.