Study/object

[엘레강트 오브젝트] 2-4장 메서드 이름을 신중하게 선택하세요

에디개발자 2021. 8. 18. 22:54
반응형

나를 닮았다고 한다...

이 글은 엘레강트 오브젝트 새로운 관점에서 바라본 객체지향 도서를 보며 스터디한 글입니다.

책에서 주장하는 내용을 정리하였으며 예제들은 모두 코틀린 코드로 변환하여 작성하였습니다.

 

목차

  1. 빌더는 명사다
  2. 조정자는 동사다
  3. 빌더와 조정자 혼합하기
  4. 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 -> cookingBrownie brownie
brewCupOfCoffee -> boilCupOfCoffee coffee

이 내용은 책에 나와있지 않은 내용입니다. 메서드 명이 이상하다고 느끼거나 잘못되었다고 생각하신다면 댓글 부탁드립니다! :) 

 

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 값을 반환하는 빌더는 예외. 이 경우엔 형용사
반응형