엘레강트 오브젝트 책은 우테코 FE & AN 스터디를 진행하며 읽었습니다.
이 글은 1장을 읽으며 자문자답한 글입니다.
과거에는 코드는 사람이 아닌 컴퓨터를 위해 작성했다.
작성자의 의도가 무엇인지 알기 어려웠다.
하지만 실행 속도가 빨라야 했기 때문에 어쩔 수 없었다.
사람을 고용하는 값보다 큰 하드디스크를 구매하는 비용이 더 비쌌다.
지금은 유지보수성가 중요해졌다.
사람이 코드를 읽기 때문에 더 쉬운 코드를 작성해야 한다.
그런 측면에서 OOP 관점으로 프로그래밍 하게 되었다.
하지만 객체 지향 언어라고 하는 JAVA도 클래스를 사용할 때 완전 OOP는 아니다. 왜냐하면 C를 물려받는 부분이 여전히 존재해서 객체를 사용하지만, 여전히 명령적인 부분이 있기 때문이다.
어쨌든 객체지향과 멀어지게 프로그래밍 할수록 유지보수성이 떨어진다.
(오해하면 안되는 부분은 절차지향이 '나쁘다'는 것이 아니다. 그냥 객체지향과 페어다임이 다른 것이다.)
객체 지향은 파일을 분류하는 기법이다. 한 파일에 모든 함수를 넣으면 관리하기 힘들다. 각각 함수들을 파일 분리 해야 한다. 그리고 파일을 분리할 때 이왕이면 파일 이름을 잘 지으면 좋다. 어떻게? 현실 세계에 빗대어서. 👉 객체 (결국 객체는 파일, 클래스, 함수들의 집합이 된다.)
클래스의 이름은 객체가 노출하고 있는 기능
에 기반해서는 안된다.
클래스가 무엇을 캡슐화할 것인지를 관찰하고 이 요소들에 붙일 적합한 이름을 찾아야 한다.
무엇인지
로 짓는다 👍무엇을 하는지 === '-er'
로 짓는다 ❌'-er'
로 짓게 된다면, 클래스는 객체
가 아니라 어떤 데이터를 다루는 절차들의 집합
이라고 하게 되는 것이다. 클래스는 연결 장치
가 아니다! 흔히 클래스를 '붕어빵 틀'이라고 표현하는데, 아니다. 좀 더 능동적 관점에서 봐야 한다.
클래스는 유기체이고, 유치체로서 이름이 아니라 er
로 짓는 것은 클래스를 존중하지 않는 것이다. 예를 들어, 나한테 이름을 안 부르고 '밥을 축내는 것'이라고 부르는 느낌이다. 행동보다 '이름'이라는 무엇인지에 초점을 맞춰야 한다.
아래처럼 숫자 목록(origin
)과 소수 목록(primes
)이라는 두 개의 데이터 조각을 이어주는 연결장치처럼
작성하면 안된다!
static을 클래스 안에서 쓰는 이유 → 모듈 export 관점에서 그냥 호출하기 편해서 (일관성만 있으면 된다. 어떤 건 함수고 어떤 건 클래스 안에 있으면 헷갈린다.) = 상태를 다루지 않고 그럼 뺼 수 있으니까 역할에서 벗어날 수 있는 거 아니야? → 보관용이다. 클래스는 아니다.
er
이름을 가지고 있는 Controller는 어떻게 생각해야 할까? 미션을 진행하면서 MVC 패턴을 썼다면 거의 100% 이 이름으로 Controller를 지었다.
이 책에 따르면
그리고 MVC 패턴을 쓴다면, C를 꼭 Controller라고 지을 필요없다.
생성자는 오버로딩으로 구현한다. (JavaScript는 불가능하지만)
생성자를 많이 만드는 이유는 다음과 같다.
단일 책임의 원칙은 객체만을 위한 것은 아니다. 모듈, 함수, 컴포넌트 뭐가 됐던 적용된다. 크고 복잡한 문제를 해결할 때, 결국 작은 문제로 쪼개는 것이기 때문이다. (분할정복)
그리고 단일 책임 원칙을 한 마디로 정의하면 '수정할 이유가 한 가지만 있다'고 할 수 있다.
문제가 터졌는데 일부분만 수정하고 있으면 위반이다. 왜냐하면 나머지 부분은 분리해도 됐었기 때문이다.
모든 입력에 대응하려고 부 생성자를 여러 개 만들었을 때, 잘 쓰지 않는 부생성자가 보일 수도 있다. 그래서 불필요한 코드라고 생각돼서 빼고 싶을 수도 있다.
하지만 우리는 전지적 작가(만든 사람)이다. 클라이언트가 어떻게 쓸 지 모른다. 클라이언트는 요구사항에서 벗어나는 값을 입력할 수도 있다.
내가 클라이언트가 아니다. 내 시야에서 보이는 걸로 함부로 코드를 빼면 안된다. 그리고 내 서비스에서 쓰지 않는 코드라도 모듈화 돼서 여러 개발자가 쓴다면 다른 서비스에서는 필요한 코드일 수 있다.
이것은 주 생성자에 코드를 넣지 말라는 것과 일맥상통한다. 주 생성자에서 코드를 실행할 필요가 없기 때문이다. 객체를 통해서 나중에 실행해도 되니까!
아래처럼 코드를 작성하면 안된다
다시 말해서, 객체를 초기화하는 시점에 텍스트를 숫자로 변환하면 안된다.
아래처럼 코드를 작성해야 한다.
Cach 클래스의 객체를 실제로 사용할 때까지 변환 작업을 연기할 수 있다.
신기하다. 확실히 사용하기 전까지 값의 타입을 변환할 필요는 없는 것 같다.
인자를 전달된 상태 그대로 캡슐화하고, 나중에 요청에 따라 파싱한다면, 파싱 시점을 결정할 수 있는 자유를 얻게 될 것입니다.
그리고 타입을 바꾸는 부분을 새로운 객체로 생성함으로서 단일 책임 원칙을 더욱 철저히 할 수 있게 된다.
더 작은 객체들을 조합한다는 게 무슨 말인지 이해가 가는 부분이다.
진정한 객체 지향에서 인스턴스화는 더 작은 객체들을 조합해서 더 큰 객체를 만들 때 일어납니다. 조합이 필요한 단 하나의 이유는 새로운 계약을 준수하는 새로운 엔티티가 필요하기 때문입니다.
아래는 오버로딩이 불가능한 언어에서 주 생성자, 부 생성자를 오버로딩으로 구현한 코드이다.
근데 아래처럼 코드가 있으면 Validation
은 어디서 해야 할까?
책에서는 주 생성자에서만 Validation
을 하면 된다고 했지만,
부 생성자를 진행하면서 에러가 발생하면 원하지 않은 오류 메시지가 반환될 수 있다.
그림에 표시한 것처럼 Validation
은 생성자의 가장 상위에서 진행되야 할까? 🤔 (아직 모르겠음)
해결: 주 생성자에서만 Validation을 진행하면 된다. 그리고 부 생성자에서 의도치 않게 오류가 발생한다면 그건 Try Catch로 오류를 따로 처리해주면 된다.
생성한 시점에 얼마나 검증할 지는 개발자 판단에 따라 다르다. 예를 들면 다음과 같다.
-1
같은 비 논리적 값 → 생성한 시점에서 검증한다. (개발자가 제어할 수 없음)kaori@naver.com
유효할 수도 있는 도메인 (현재는 유효하지 않아도 나중에 유효할 수도 있고, 일단 양식은 잘 맞는 도메인) → 나중에 사용할 때 해도 된다. (비즈니스 오류)