클린코드(CleanCode)를 읽고 간략하게 정리한 글입니다.
6장. 객체와 자료 구조
- 변수를 Private(비공개)로 정의하는 이유는 남들이 변수에 의존하지 않게 만들고 싶어서이다.
- 충동이든 변덕이든, 변수 타입이나 구현을 맘대로 바꾸고 싶어서다.
- 대부분 프로그래머들은 조회(get), 설정(set) 함수를 당연하게 Public(공개)으로 공개한다.
1. 자료 추상화
public class Point1 {
public double x;
public double y;
}
public class Point2 {
double getX();
double getY();
void setCartesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
- 구현을 감추기 위해서는 추상화가 필요하다.
- 변수를 Private로 선언하더라도 각 값마다 조회(get) 함수와 설정(set) 함수를 제공한다면 구현을 외부로 노출이 된다.
- 변수 사이에 함수라는 계층을 넣는다고 구현이 저절로 감춰지지 않는다.
- 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스(Class)이다.
2. 자료/객체 비대칭
- 객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개한다.
- 자료 구조는 자료를 그대로 공개하며 별다른 함수는 제공하지 않는다.
절차적인 코드는 기본 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다.
객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.
절차적인 코드는 새로운 자료구조를 추가하기 어렵다. 모든 함수를 고쳐야 한다.
객체 지향 코드는 새로운 함수를 추가하기 어렵다. 모든 클래스를 고쳐야 한다.
- 다시 말해, 객체 지향 코드에서 어려운 변경은 절차적인 코드에서 쉬우며, 절차적인 코드에서 어려운 변경은 객체 지향 코드에서 쉽다.
- 때로는 단순한 자료구조와 절차적인 코드가 가장 적합한 상황도 있다.
3. 디미터 법칙
- 디미터 법칙은 잘 알려진 휴리스틱으로, 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다.
- 객체는 자료를 숨기고 함수를 공개한다.
- 객체는 조회 함수로 내부 구조를 공개하면 안 된다는 의미이다.
3-1. 기차 충돌
- 나쁜 코드1
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
- 위와 같은 코드를 기차 충돌이라 한다.
- 여러 객체가 한 줄로 이어진 기차처럼 보이기 때문이다.
- 나쁜 코드2
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
- 위 코드가 디미터 법칙을 위반하는지는 ctxt, Options, ScratchDir 이 객체인지 자료구조인지에 달렸다.
- 객체라면 내부 구조를 숨겨야 하므로 확실히 디미터 법칙을 위반한 것이고, 자료 구조라면 당연히 내부 구조를 노출하므로 디미터 법칙이 적용되지 않는다.
- 좋은 코드
final String outputDir = ctxt.options.scratchDir.absolutePath;
- 자료구조는 무조건 함수 없이 공개 변수만 포함하고 객체는 비공개 변수와 공개 함수를 포함한다면 문제는 훨씬 간단해진다.
3-2. 잡종 구조
- 이러한 혼란으로 말미암아 때때로 절반은 객체, 절반은 자료구조인 잡종 구조가 나온다.
- 잡종 구조는 중요한 기능을 수행하는 함수도 있고, 공개 변수나 공개 조회/설정 함수도 있다.
- 이러한 잡종 구조는 새로운 함수는 물론이고 새로운 자료구조도 추가하기 어렵다.
3-3. 구조체 감추기
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
- ctxt, options, scratchDir 이 객체라면 함수 내에서 줄줄이 사탕으로 호출해서는 안된다.
// ctxt 객체에 공개해야 하는 메서드가 너무 많다.
ctxt.getAbsolutePathOfScratchDirectoryOption();
// ctxt가 객체라면 뭔가를 하라고 해야 하는데
// getScratchDirectoryOption()에서 속을 드러내라고 말하는 느낌이다.
ctxt.getScratchDirectoryOption().getAbsolutePath()
// ctxt 객체에게 임시 파일을 생성하라고 시킨다.
// ctxt는 내부 구조를 드러내지 않으며, 모듈에서 해당 함수는 여러 객체를 탐색할 필요가 없다.
// 디미터 법칙 충족
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);
4. 자료 전달 객체
- 자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스이다.
- 이런 자료 구조체를 자료 전달 객체(Data Transfer Object, DTO)라 한다.
- DTO는 여러모로 유용한 구조체이다.
- 데이터베이스와 통신하거나 소켓에서 받은 메시지의 구문을 분석할 때 유용하다.
- 가공되지 않은 정보를 애플리케이션 코드에서 사용할 객체로 변환하는 일련의 단계에서 처음 사용하는 구조체
4-1. 활성 레코드
- 활성 레코드는 DTO의 특수한 형태이다.
- 공개 변수가 있거나 비공개 변수에 조회/설정 함수가 있는 자료 구조이지만 대개 save나 find와 같은 탐색 함수도 제공한다.
- 종종 활성 레코드에 비즈니스 규칙 메서드를 추가해 객체로 취급하는 경우가 있는데 이는 바람직하지 않다. -> 잡종 구조
- 비즈니스 규칙을 담는 객체는 따로 생성하는 것이 옳다.
- 내부 자료는 활성 레코드의 인스턴스 일 가능성이 높다.
5. 결론
- 객체는 동작을 공개하고 자료를 숨긴다. 기본 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다.
- 자료 구조는 별다른 동작 없이 자료를 노출한다. 기존 자료 구조에 새 동작을 추가하기는 쉬우나, 기존 함수에 새 자료구조를 추가하기는 어렵다.
- 어떤 시스템을 구현할 때, 새로운 자료 타입을 추가하는 유연성이 필요하면 객체가 더 적합하다.
- 다른 경우로 새로운 동작을 추가하는 유연성이 필요하면 자료 구조와 절차적인 코드가 더 적합하다.
- 중요한 것은 편견 없이 직면한 문제에 최적인 해결책을 선택해야 하는 것이다.
'Study > CleanCode' 카테고리의 다른 글
[클린코드] 9장. 단위 테스트 (2) | 2023.11.05 |
---|---|
[클린코드] 8장. 경계 (1) | 2023.11.04 |
[클린코드] 5장. 형식 맞추기 (0) | 2023.10.22 |
[클린코드] 4장. 주석 (0) | 2023.10.15 |
[클린코드] 3장. 함수 (1) | 2023.10.14 |