변수를 비공개로 정의하는 이유가 있다. 남들이 변수에 의존하지 않게 만들고 싶어서다. 그렇다면 어째서 수많은 프로그래머가 조회(get) 함수와 설정(set) 함수를 당연하게 공개(public)해 비공개 변수를 외부에 노출할까?
자료 추상화
// 6-1 : 구체적인 point 클래스
public class Point {
private double x;
private double y;
}
// 6-2 : 추상적인 point 클래스
public interface Point {
double getX();
double getY();
void setCatesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
- 6-1은 구체적으로 x,y를 표시한다. 여기에 조회 함수와 설정 함수를 제동한다면 구현이 외부로 노출된다. 이는 추상화를 하지 않으면 감춰지지 않는다.
- 6-2는 6-1에 비해서 추상적이다. 직교좌표계를 사용하는 지, 극좌표계를 사용하는 지, 좌표를 설정할 때는 두 값을 한 번에 설정해야한다.
- 그저 형식적으로 조회 함수와 설정함수를 변수로 다룬다고 클래스가 되지는 않는다. 그보다는 추상 인터페이스를 제공해 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스다.
* 예시가 좀 빈약한데, 핵심은 get,set을 이용하여 private 변수를 조작하게 두어서는 안된다이다.
* 나도 동료분께 "우리는 이렇게 public한 함수로 바꿀거면 뭐하러 은닉해요? 변수 가져다가 직접 바꾸면 안되어요?" 물어본 적이 있는데, "은닉성의 장점을 찾아보세요" 하고 애매하게 끝난 적이 있다... 지금 생각해보면 나는 은닉성을 아예 몰랐고, 동료분은 은닉성을 왜 쓰는 지를 몰랐던 거다.
* 객체의 속성을 직접 변경하면 안된다는 걸 이번에 처음 알았다... 정확히는 객체를 정확히 이해못해서, 문자 그대로 넘어갔는데 이제 조금 알 거 같다.
자료구조 / 객체 비대칭
- 자료구조 : 여기서 의미하는 자료구조는 정말 자료를 담은 구조다. 메서드는 없고, 데이터에 직접 접근하여 수정 조회한다.
- 객체 : 우리가 아는 객체로 객체의 속성(데이터)에 직접 접근은 불가능하며, 메서드로 객체를 변경/조회한다.
* 자료구조가 일반적인 자료구조(스택, 큐)랑 다르길래 먼저 서술해둔다.
// 6-5 절차적인 도형 (자료구조)
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
}
}
}
// 6-6 객체지향적인 도형
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;
}
}
- 자료구조와 객체는 서로 상호보완적인 특징이 있다.
- 6-5처럼 자료 구조를 사용하는 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다. 반면, 객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.
- 6-6처럼 절차적인 코드는 새로운 자료 구조를 추가하기 어렵다. 그러려면 모든 함수를 고쳐야 한다. 객체 지향 코드는 새로운 함수를 추가하기 어렵다. 그러려면 모든 클래스를 고쳐야 한다.
분별 있는 프로그래머는 모든 것이 객체라는 생각이 미신임을 잘 안다. 때로는 단순한 자료 구조와 절차적인 코드가 가장 적합한 상황이 있다.
디미터 법칙
- 디미터 법칙은 모듈은 자신이 조작하는 객체의 속사정을 몰라야한다는 법칙이다. 다음 예시는 디미터 법칙을 위배한다.
String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
- 위 예시를 기차충돌이라 부른다. 여러 객차가 이어진 기차처럼 보이기 때문이다. 여기서 감 함수가 반환하는 것이 자료구조인지, 객체에 따라 다르다. 객체라면 내부 구조를 모두 노출함으로 틀렸고, 자료구조라면 저렇게 함수형태로 이어지지 않을 것이다. (객체라면, 해당 동작을 수행하기 위한 별도의 함수가 있는 게 맞다. 이 글을 읽는 사람들은 모르겠지만, 해당 함수의 목적은 임시 파일을 만드는 것이기 때문에 createScratchFileStream이 된다.)
// 자료구조인 경우
String outputDir = ctxt.getOptions.getScratchDir.getAbsolutePath;
// 객체인 경우
String outputDir = ctxt.createScratchFileStream(classFileName);
- 객체와 자료구조가 뒤섞여있는 잡종 구조가 나올 때도 있다. 다만, 잡종구조는 새로운 함수 추가도 새로운 자료 구조 추가도 어렵게 하는 단점만 모은 설계이다. 지양하도록 하자.
자료 전달 객체(DTO)
- 자료 구조체의 전형적인 형태로 공개 변수만 있고, 함수가 없는 형태이다.
- Bean 구조는 비공개 변수를 조회 / 설정 함수로 조작한다. 이는 사이비 캡슐화로 별다른 이익을 제공하지 않는다.
- 활성 레코드는 DTO의 특수한 형태로 공개 변수가 있거나 비공개 변수에 조회/ 설정 함수가 있는 자료구조이다. 불행히도, 활성 레코드에 비즈니스 규칙 메서드를 추가해 객체처럼 취급하는 개발자가 많다. 이는 잡종구조를 만들기 때문에 지양해야한다. 활성 레코드는 자료구조체로 사용하고, 객체를 따로 생성하자.
* DTO 부분은 내 배경지식이 부족하여 정확히 이해하지 못했다. 특히, Bean 구조나 활성 레코드라는 것은 자꾸 다른 것이 나와서 제대로 검색하지 못했다. 나중에 백엔드와 자바 지식이 보충되면 다시 공부하자
'책과 강연 > 클린 코드' 카테고리의 다른 글
| 클린 코드 8장 : 경계 (1) | 2023.12.29 |
|---|---|
| 클린코드 7장 : 오류 처리 (0) | 2023.12.28 |
| 클린 코드 5장: 형식 맞추기 (0) | 2023.12.24 |
| 클린코드 4장 : 주석 (0) | 2023.12.24 |
| 클린 코드 3장 : 함수 (1) | 2023.12.24 |