이 글은 엘레강트 오브젝트 새로운 관점에서 바라본 객체지향 도서를 보며 스터디한 글입니다.
책에서 주장하는 내용을 정리하였으며 예제들은 모두 코틀린 코드로 변환하여 작성하였습니다.
목차
- 빌더는 명사다
- 조정자는 동사다
- 빌더와 조정자 혼합하기
- Boolean 값을 결과로 반환하는 경우
빌더와 조정자
Type | Description | Example |
빌더 ( Builder ) | 뭔가를 만들고 새로운 객체를 반환하는 메서드 반환타임은 절대 void가 될 수 없음 |
fun pow(base: Int, power: Int): Int fun speed(): Float fun employee(id: Int): Employee fun parsedCell(x: Int, y: Int) |
조정자 ( Manipulator ) | 객체로 추상화한 실세계 엔티티를 수정하는 메서드 반환타입은 void |
fun save(content: String) fun put(key: String, value: Float) fun remote(emp: Employee) fun quicklyPrint(id: Int) |
잘못된 예
// 저장된 전체 바이트 반환
fun save(content: String): Int
// map이 변경된 경우 true를 반환
put(key: String, value: Float): Boolean
// speed를 저장한 후 이전 갑을 반환
fun speed(val: Float): Float
올바른 예
// 저장된 전체 바이트 반환
// fun save(content: String): Int
fun save(content: String) // 조정자
fun bytedSaved(content: String): Int // 빌더
// map이 변경된 경우 true를 반환
//put(key: String, value: Float): Boolean
fun put(key: String, value: Float) // 조정자
// speed를 저장한 후 이전 갑을 반환
//fun speed(val: Float): Float
fun speed(val: Float) // 조정자
1. 빌더는 명사다
어떤 것을 반환하는 메서드의 이름을 동사로 짓지 말라. 객체지향적인 사고방식에 어긋납니다.
제과점에서...
[잘못된 표현]
"브라우니를 요리해 주세요"
"커피 한 잔 끓여 주세요"
[올바른 표현]
"브라우니 주세요"
"커피 한잔 주세요"
코드로 표현한다면 아래와 같을 것 입니다.
class Bakery {
fun cookBrownie(): Food {
// 브라우니를 요리한다.
// 반환한다.
}
fun brewCupOfCoffee(flavor: String): Drink {
// 커피를 끓인다.
// 반환한다.
}
}
이런 표현은 객체지향적인 접근방법이 아니고 절차적인 접근법입니다.
객체지향적인 접근 방법인 코드는 무엇일까??
cookBrownie ->cookingBrowniebrownie
brewCupOfCoffee ->boilCupOfCoffeecoffee
이 내용은 책에 나와있지 않은 내용입니다. 메서드 명이 이상하다고 느끼거나 잘못되었다고 생각하신다면 댓글 부탁드립니다! :)
Examples
잘못된 예 | 올바른 예 |
fun load(url: URL): InputStream | fun stream(url: URL): InputStream |
fun read(file: File): String | fun content(file: File): String |
fun add(x: Int, y: Int): Int | fun sum(x: Int, y: Int): Int |
정리
객체로부터 뭔가를 얻습니다. 다시 말해서 뭔가를 만들라고 객체에게 요청합니다.
2. 조정자는 동사다
조정사는 반환을 하지 않습니다.
class Pixel(
val x: Int, val y: Int
) {
fun paint(color: Color) {
}
}
Pixel 클래스의 paint() 메서드는 리턴이 존재하지 않습니다. 아래와 같이 사용할 수 있습니다.
class UseTest {
fun use() {
val center = Pixel(50, 50)
val red = 2598732
center.paint(Color(red)) // 무엇인가 만들어질(build) 것이라고 기대하지 않는다.
}
}
핵심적인 원칙만 준수한다면 규칙을 완화할 수 있다.
class Book {
fun withAuthor(author: String): Book // bookWithAuthor의 줄임말
fun withTitle(title: String): Book // bookWithTitle의 줄임말
fun withPage(page: Page): Book // bookWithPage의 줄임말
}
Book 클래스안의 메서드 명에 접두사 book을 반복하지 않기 위해 간단히 with 접수사를 사용하였습니다. 하지만 이 메서드들은 빌더이고 이름은 명사입니다. 빌더 패턴은 유지보수성이 낮고 응집도가 떨어집니다. 이 책에서는 빌더 패턴을 사용하지 않는 것을 권장합니다.
정리
객체로부터 리턴값이 존재하지 않고 세계에 변화를 줍니다.
3. 빌더와 조정자 혼합하기
현실적인 리팩토링 예제를 살펴보겠습니다. 파일 내용을 저장하고 저장된 바이트 수를 반환하고 트랜잭션 처리시간을 반환하는 메서드가 있다고 가정합니다.
class Document {
fun write(content: InputStream): Int {
// file 내용 저장
// 저장된 바이트 수를 반환
return 0
}
}
문제점
write의 반환 타입이 void가 아니고 Int를 리턴하고 있습니다. 하지만 메서드는 파일을 저장하고 바이트를 반환해야합니다. 여기서 문제는 하나의 메서드에서 하나의 목적이 아니고 N개의 목적성을 가지고 있다는 것이 문제입니다.
올바르게 수정
하나의 메서드에서 하나의 기능만 하도록 분리합니다.
class Document {
fun output(): OutputPipe {}
}
class OutputPipe {
// file 저장
fun write(content: InputStream) {}
// 저장된 파일의 byte 반환
fun bytes(): Int {}
// 저장 트랜잭션 시간 반환
fun time(): Long {}
}
정리
메서드 명을 작명하기 어렵다면 그 메서드에서 목표하는 작업이 하나가 아닐 가능성이 높습니다. 하나의 메서드에서는 하나의 목표를 가지고 작동해야합니다. 그렇다면 메서드 명을 명확히 작명할 수 있을 것 입니다.
Boolean 값을 결과로 반환하는 경우
Boolean 값을 반환하는 메서드는 가독성 측명에서 형용사로 지어야합니다.
예시
- fun empty(): Boolean
- fun readable(): Boolean
- fun negative(): Boolean
요약
- 메서드는 반드시 빌더나 조정자 둘 중 하나여야한다.
- 빌더 = 명사, 조정자 = 동사
- Boolean 값을 반환하는 빌더는 예외. 이 경우엔 형용사
'Study > object' 카테고리의 다른 글
[엘레강트 오브젝트] 2-6장 불변 객체로 만드세요 (2) | 2021.08.31 |
---|---|
[엘레강트 오브젝트] 2-5장 퍼블릭 상수(Public Constant)를 사용하지 마세요 (0) | 2021.08.21 |
[엘레강트 오브젝트] 2-3장 항상 인터페이스를 사용하세요 (0) | 2021.08.17 |
[엘레강트 오브젝트] 2-2장 최소한 뭔가는 캡슐화하세요 (0) | 2021.08.16 |
[엘레강트 오브젝트] 2-1장 가능하면 적게 캡슐화하세요. (0) | 2021.08.12 |