본 글은 클린코드 6장을 읽고 난 후 개인적인 생각입니다.
자료 추상화
변수를 비공개로 정의하는 이유는 변수에 의존하지 않게 만들기 위해서입니다.
하지만 수많은 프로그래머가 getter 혹은 setter 함수를 당연하게 공개해 비공개 변수를 외부에 노출합니다.
변수를 private으로 선언하더라도 각 값마다 getter 함수와 setter 함수로 제공한다면 구현을 외부로 노출하는 셈입니다.
이렇게 자료를 세세하게 공개하기보다는 추상적인 개념의 함수를 만드는 것이 좋다고 합니다.
저는 setter에 대해서는 이 내용에 동의하지만 getter까지 전부 추상적인 개념(클래스 내부의 어떤 비공개 인스턴스 변수를 가져오는지 모르도록 추상화)으로 일일이 변경해야 한다는 부분에서는 클래스 용도마다 차이가 있을 것으로 생각합니다.
자료/객체 비대칭
절차적인 코드
기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다.
새로운 자료 구조를 추가하기 어렵다.
객체 지향 코드
기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다고 합니다.
추상화 인터페이스에 새로운 함수를 추가하기 어렵다. (모든 클래스를 고쳐야 하기 때문에)
예를 들어 다음과 같은 코드가 있다고 하면
절차적인 코드
public class Square {
public Point topLeft;
public double side;
}
public class Circle {
public Point center;
public double radius;
}
public class Geometry {
public final double PI = 3.141592653589793;
public double area(Object shape) {
if (shape instanceof Square) {
Square square = (Square) shape;
return square.side * square.side;
} else if (shape instanceOf Circle) {
Circle c = (Circle) shape;
return PI * c.radius * c.radius;
}
}
}
객체지향 코드
public interface Shape {
double area();
}
public class Square implements Shape {
public Point topLeft;
public double side;
@Override
public double area() {
return side * side;
}
}
public class Circle implements Shape {
public Point center;
public double radius;
public final double PI = 3.141592653589793;
@Override
public double area() {
return PI * radius * radius;
}
}
절차적인 코드에서는 새 함수를 추가하여도 다른 클래스들이 영향받지 않습니다.
객체 지향 코드에서는 Shape에 함수를 추가한다면 구현체인 Circle, Square 모두가 영향을 받습니다.
개인적인 생각으로는 추상화한 Shape 에 함수가 추가되어야 하는 경우는 구현체들에 모두 추가되어야 할 때인 것 같은데 딱히 단점으로 느껴지진 않고 영향을 받는 게 당연한 것 같다고 생각이 듭니다.
특정 하나의 구현체만 적용되어야 한다면 다른 방법을 사용하는 게 좋아 보이네요.
객체지향 코드는 새 클래스를 추가하기 쉽습니다. 위 코드에서 Shape를 구현하는 클래스만 추가하여 함수를 구현하면 됩니다.
디미터 법칙
"클래스 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();
자료 전달 객체
활성 레코드
활성 레코드는 데이터베이스 테이블이나 다른 소스에서 자료를 직접 반환한 결과입니다.
활성 레코드에 비즈니스 규칙 메서드를 추가해 이런 자료 구조를 객체로 취급하는 개발자가 흔하다고 합니다.
개인적인 생각으로는 상황에 따라 다를 것 같기는 합니다. 프로젝트 초반 단계에서 빠르게 속도를 내기 위해서는 3 계층 아키텍처 + JPA Entity(책에서 말하는 활성레코드로 추정)에 비즈니스 로직을 추가할 수도 있을 것 같습니다. 다만 점점 도메인 로직이 거대해진다면 활성 레코드 객체와 도메인 로직을 구현하는 객체를 별개로 두는 게 더 좋을 것 같습니다.
결론
객체는 동작을 공개하고 자료를 숨긴다.
절차적인 코드, 객체 지향 코드는 상황에 따라 활용될 수 있다.
'클린코드와 리팩토링 > 클린코드' 카테고리의 다른 글
클린코드 - 형식 맞추기 (3) | 2024.03.30 |
---|---|
클린코드 - 주석 (0) | 2023.07.23 |
클린코드 - 함수 (0) | 2023.07.23 |
클린코드 - 의미있는 이름 (0) | 2023.07.23 |