남들이 변수에 의존하지 않도록 변수는 private 으로 정의한다. 그렇다면 왜 조회(get)와 설정(set)을 제공하는 메서드는 public하게 외부에 노출하는 것일까?
- 객체는 동작을 공개하고 자료를 숨긴다. 그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다.
- 자료 구조는 별다른 동작 없이 자료를 노출한다. 그래서 기존 자료 구조에 새 동작을 추가하기는 쉬우나, 기존 함수에 새 자료 구조를 추가하기는 어렵다.
자료 추상화
변수 사이에 함수라는 계층을 넣는다고 구현이 감춰지는건 아니다. 구현을 감추기 위해서는 추상화가 필요하다.
- 변수를 private 으로 선언하더라도 각 값마다 get 함수와 set 함수를 제공한다면 구현을 외부로 노출하는 셈이다.
- 조회 함수와 설정 함수로 변수를 다룬다고 클래스가 되지는 않는다.
- 추상 인터페이스를 제공해 사용자가 “구현”을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 클래스라고 말할 수 있다.
// 구체적인 클래스 (구현을 외부로 노출시킴) public class Point { private double x; private double y; } // 추상적인 클래스 (구현을 숨김) public interface Point { double getX(); double getY(); void setCatesian(double x, double y); double getR(); double getTheta(); double setPolar(double r, double theta); } // 구체적인 클래스 (구현을 외부로 노출) public interface Vehicle { double getFuelTankCapacityInGallons(); double getGallonsOfGasoline(); } // 추상적인 클래스 (구현을 숨김) public interface Vehicle { double getPercentFuelRemaining(); }
자료를 세세하기 공개하기 보다는 추상적인 개념으로 표현해야 한다. 인터페이스나 조회/설정 메서드로는 추상화가 이뤄지지 않는다.
자료/객체 비대칭
- 객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개한다.
- 자료 구조는 자료를 그대로 공개하며 별다른 함수는 제공하지 않는다.
// 절차적인 클래스 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 double width; } public class Geometry { public final double PI = 3.141592653585793; 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 } } }
Geometry 클래스는 세 가지 도형 클래스를 다루고 있고 도형 클래스는 간단한 자료구조로 볼 수 잇다. 도형이 동작하는 방식은 Geometry 클래스에서 구현하고 있는데 만약 함수를 추가하게 된다면 도형 클래스는 영향을 받지 않는다.
새 도형을 추가하고 싶다면 Geometry 클래스에 속한 함수를 모두 고쳐야 한다.
// 객체지향적인 클래스 public class Square implements Shape { public Point topLeft; public double side; public double area() { return side*side; } } public class Rectangle implements Shape { public Point topLeft; public double height; public double width; public double area() { return height*width; } } public class Circle implements Shape { public Point center; public double radius; public double width; public double area() { return PI*radius*radius; } }
area() 메서드는 다형 메서드로 Geometry 클래스는 이제 필요가 없다.
새 도형을 추가해도 기존 함수에 아무런 영향을 미치지 않지만 새 함수를 추가하고 싶다면 모든 도형클래스를 수정해야 한다.
자료 구조를 사용하는 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽지만 객체지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉬워진다.
절차적인 코드는 새로운 자료 구조를 추가하기 어렵다 그 이유는 모든 함수를 고쳐야 하기 때문이다. 하지만 객체지향 코드는 새로운 함수를 추가하기 어렵다. 그 이유는 모든 클래스를 고쳐야 하기 때문이다.
객체 지향 코드에서 어려운 변경은 절차적인 코드에서 쉽고 그 반대는 객체지향 코드에서 쉽다 !
디미터 법칙
메서드가 반환하는 객체의 메서드를 사용하면 안된다.
- 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다. 객체는 자료를 숨기고 함수를 공개한다.
- 객체는 조회 함수로 내부를 공개하면 안된다는 뜻이다. 내부 구조를 숨기지 않고 노출하는 셈이 되버린다.
[기차 충돌]
- 아래는 여러 객체가 한 줄로 이루어진 기차처럼 보이고 조잡하다.
String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
- 메서드가 반환하는 객체의 메서드를 사용해서 디미터 법칙을 위반하게 된다.
- 아래 코드로 변환하는 것이 바람직하다.
Options opts = ctxt.getOptions(); File scratchDir = opts.getScratchDir(); String outputDir = scratchDir.getAbsolutePath();
ctxt, opts, scratchDir이 만약에 객체라면 디미터 법칙을 위반하는 행위지만 자료 구조라면 내부 구조를 노출하기 때문에 디미터 법칙이 적용되지 않는다.
final String outputDir = cxtx.opts.scratchDir.absolutePath;
- 위의 코드는 디미터 법칙을 거론할 필요가 없다는 의미이다. 자료구조는 무조건 함수 없이 공개 변수만 포함하고 객체는 비공개 변수와 공개 함수를 표현하면 된다.
[잡종 구조]
- 절반은 객체, 절반은 자료 구조로 이루어진 것을 뜻한다.
- 공개 변수, 공개 함수, 주요 함수, 게터, 세터가 모두 섞여있는 구조
- 클래스, 자료구조 양쪽에서 단점만 모아 놓은 피해야 할 구조
- 새로운 함수는 물론 새로운 자료 구조도 추가하기 어렵게 된다.
자료 전달 객체
자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스이며 이런 자료 구조를 DTO라고 보통 부른다.
- DTO : 공개 변수만 있고 함수가 없는 클래스
- BEAN : 비공개 변수와 게터 세터가 있는 클래스
- 활성 레코드 : 공개, 비공개 변수와 게터 세터 그리고 탐색 함수가 있는 클래스
결론
- 객체는 동작을 공개하고 자료를 숨긴다.
- 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉽다.
- 기존 객체에 새 동작을 추가하기는 어렵다.
- 자료 구조는 별다른 동작 없이 자료를 노출한다.
- 기존 자료구조에 새 동작을 추가하기는 쉽다.
- 기존 함수에 새 자료 구조를 추가하기는 어렵다.
- 바람직한 구조는?
- 객체 : 비공개 변수와 공개 함수만 포함하자
- 자료구조 : 함수 없이 공개 변수만 포함하자
- 적절한 쓰임은?
- 객체 : 새로운 자료 타입 추가
- 자료 구조 : 새로운 메서드 추가