Study/object

[엘레강트 오브젝트] 3-5장 절대 getter와 setter를 사용하지 마세요

에디개발자 2021. 9. 25. 07:00
반응형

나를 닮았다고 한다...

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

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

 

목차

  1. 객체 대 자료구조
  2. 좋은 의도, 나쁜 결과
  3. 접두사에 관한 모든 것

1. 객체 대 자료구조

먼저 getter와 setter를 사용하지 말아야하는 이유를 알아보기 전에 객체와 자료구조에 대해서 짚고 넘어가겠습니다. 

자료구조

class Cash(
    var dollars: Int
)

// use
val cash = Cash(20)

// get
val dollars = cash.dollars

// set
cash.dollars = 30

// print
println("dollars:: ${cash.dollars}")

자료구조는 단순한 Data Bag일 뿐입니다. 자료구조는 struct를 가지고 아무일도 하지 않고 아무런 의사소통도 하지 않습니다. 

 

객체

class Cash(
    private val dollars: Int
) {
    fun dollars() {
        println("dollars:: ${this.dollars}")
    }
}

// use
val cash = Cash(20)

// print
cash.print()

객체는 내부 속성값에 접근하는 것과 노출하는 것을 허용하지 않습니다. ( dollars를 접근, 노출 X ) Cash 클래스의 print()가 어떻게 동작하는지도 알 수 없습니다. 이것을 바로 캡슐화라고하며, OOP가 지향하는 중요한 설계 원칙입니다.

 

자료구조 vs 객체

자료구조 객체
투명 ( Glass Box ) 불투명 ( Black Box )
수동적 능동적
죽어있다. 살아있다.

 

객체를 사용해야하는 이유

모든 프로그래밍 스타일의 핵심 목표는 가시성의 범위를 축소해서 사물을 단순화시키는 것 입니다. 이해해야 하는 범위가 작을수록, 소프트웨어의 유지보수성이 향상되고 이해하고 수정하기 쉬워집니다.

 

OOP는 코드를 수동적인 존재로 만들었지만, 데이터는 능동적인 존재로 만들었습니다. 하지만 데이터는 객체 안에 캡슐화되어 있고 객체는 살아있습니다. 객체들은 서로 연결되고, 역할을 수행할 때 메시지를 전송해서 작업을 실행합니다.

 

OOP에서는 코드가 데이터를 지배하지 않습니다. 단, 필요한 시점에 객체가 자신의 코드를 실행시킵니다. 코드는 더 이상 핵심이 아닙니다. 객체가 일급 시민이며, 생성자를 통한 객체 초기화가 곧 소프트웨어입니다. 

 

2. 좋은 의도, 나쁜 결과

getter와 setter는 클래스를 자료구조로 바꾸기 위해 도입됐습니다. 코드로 살펴보겠습니다.

class Cash(
    var dollars: Int
)

위 클래스는 Java 프로그래밍의 기본 규칙을 위반하고 있습니다. 이런 행위를 방지하기 위해 프로퍼티를 private로 변경하고 모든 프로퍼티에 getter와 setter를 추가하기로 하였습니다.

 

class Cash(
    private var dollars: Int
) {
    fun getDollars() = this.dollars
    fun setDollars(dollars: Int) {
        this.dollars = dollars
    }
}

변경 된 클래스에서 getDollars, setDollars는 메서드처럼 보이지만 실제로는 데이터에 직접 접근하고 있습니다. 즉 데이터가 무방비로 노출되고 있는 것과 같습니다. 다시 말해서 행동이 아닌 데이터를 표현할 뿐인 클래스입니다.

 

3. 접두사에 관한 모든 것

getter/setter 안티 패턴에서 유해한 부분은 get/set 입니다. 이 접두사는 객체가 객체가 아니고 자료구조라는 사실을 명확하게 전달합니다. 객체는 대화를 원하지 않고 데이터를 get/set 하기만을 원할 뿐입니다.

 

어떤 데이터를 반환하는 메서드를 포함하는 경우는 괜찮습니다.

class Cash(
    private val value: Int
) {
    fun dollars() = this.value
}

 

하지만 아래와 같이 작성한다면 적절하지 않습니다.

class Cash(
    private val value: Int
) {
    fun getDollars() = this.value
}

 

getDollars()는 데이터 중에 dollars를 찾고 반환하라고 이야기하고 있습니다. 하지만 dollars()는 "얼마나 많은 달러가 필요한가요?" 라고 묻는 것과 같습니다. 객체를 데이터 저장소 취급하지 않고 존중한다는 의미입니다. 

반응형