본문 바로가기

분류 전체보기

(62)
[Effective Kotlin] Item No.36 상속보다는 컴포지션을 사용하라 상속은 굉장히 강력한 기능으로 "is-a" 관계의 객체 계층 구조를 만들기 위해 설계됐다. 상속은 관계가 불명확할 때 쓰면 여러 문제가 발생할 수 있다. 따라서 단순하게 코드 추출 또는 재사용을 위해 상속을 하려고 한다면 좀 더 신중하게 생각해야 한다. 일반적으로 이런 경우에는 상속보다 컴포지션을 쓰는 게 좋다. 간단한 행위 재사용 간단한 코드부터 확인한다. 프로그레스 바를 어떤 로직 처리 전에 출력하고, 처리 후 숨기는 유사한 동작을 하는 2개의 클래스가 있다고 가정한다. 하나 이상의 클래스를 상속할 수 없다. 위 코드에서 추가 Alert기능이 필요하다면 상속의 경우 두 기능을 하나의 슈퍼 클래스에 배치해야한다. 컴포지션의 경우 멤버변수로 Alert클래스를 추가하면 된다. 이런 코드는 간단한 경우 문제..
[Effective Kotlin] Item No.35 복잡한 객체를 생성하기 위한 DSL을 정의하라 코틀린을 활용하면 DSL(Domain Specific Language)을 직접 만들 수 있다. DSL은 복잡한 객체, 계층 구조를 가진 객체들을 정의할 때 유용하다. DSL을 만드는 건 약간 힘든 일이지만 한 번 만들고 나면 보일러플레이트와 복잡성을 숨기면서 개발자의 의도를 명확하게 표현할 수 있다. 예를 들어 코틀린 DSL은 아래와 같은 형태로 HTML을 표현할 수 있다. 이는 고전적인 HTML, 리액트 HTML 모두에서 활용할 수 있다. 다른 플랫폼의 뷰도 이런 형태로 DSL을 써서 만들 수 있다. DSL은 자료 또는 설정을 표현할 때도 활용될 수 있다. 아래 코드는 Ktor를 활용해 만든 API 정의 예시다. 마찬가지로 DSL을 썼다. 다음은 코틀린 테스트를 활용해서 테스트 케이스를 정의한 것이다...
[Effective Kotlin] Item No.34 기본 생성자에 이름 있는 옵션 아규먼트를 사용하라 객체를 정의하고 생성하는 방법 중 가장 기본적인 방법은 기본 생성자를 사용하는 것이다. 일반적으로 이를 활용해서 객체를 만드는 것이 좋다. 기본 생성자로 객체를 만들 때는 객체의 초기 상태를 나타내는 아규먼트를 전달한다. 일단 데이터를 표현하는 가장 기본적인 데이터는 아래와 같다. 점층적 생성자 패턴 점층적 생성자 패턴은 프로퍼티별 디폴트 값을 가질 경우 파라미터별로 생성자를 각각 만드는 패턴이다. 코틀린에서는 디폴트 아규먼트를 지정할 수 있기 때문에 해당 코드는 무의미하다. 이름 있는 아규먼트를 활용해서 명시적으로 이름을 붙여주면, 의미가 훨씬 명확하다. 디폴트 아규먼트를 사용할경우, 장점이 생기는데 파타미터들의 값을 원하는대로 지정가능 아규먼트를 원하는 순서로 지정 가능 명시적으로 이름을 붙일수 있다..
[Effective Kotlin] Item No. 33 생성자 대신 팩토리 함수를 사용하라 클라이언트가 클래스의 인스턴스를 만들게 하는 가장 일반적인 방법은 기본 생성자(Primary constructor)를 사용하는 방법이다. 하지만 생성자가 객체를 만들수 있는 유일한 방법은 아니다. 헬퍼 클래스를 생각해보자. 생성자 대신 팩토리 함수를 사용하면 생기는 장점들이다. 생성자와 다르게 함수에 이름을 붙일 수 있다. 예를들어 ArrayList(3) 일때, 무엇을 의미하는지 알수 없는데 ArrayList.withSize(3)이라는 이름이 붙어있다면 훨씬 유용할 것이다. 함수가 원하는 형태의 타입을 리턴할 수 있다. 인터페이스 뒤에 실체 객체 구현을 숨길때 유용하게 사용할 수 있다. listOf는 List 인터페이스를 리턴하는데, 플랫폼에 따라 다른 빌트인 컬렉션으로 만들어진다. 또한 인터페이스를 리..
[Effective Kotlin] Item No.32 추상화 규약을 지켜라 규약은 개발자들의 단순한 합의다. 따라서 한쪽에서 규약을 위반할 수도 있다. 기술적으로 모든 부분에서 이런 규약 위반이 발생할 수 있다. 예를 들어 아래처럼 리플렉션을 활용하면 원하는 걸 열고 사용할 수 있다. 뭔가를 할 수 있다는 게 그걸 해도 괜찮다는 의미는 아니다. 현재 코드는 private 프로퍼티와 private 함수명과 같은 세부적인 정보에 매우 크게 의존하고 있다. 이런 이름은 규약이라고 할 수 없기 때문에 언제든 변경될 수 있다. 따라서 이런 코드를 프로젝트에서 쓴다면 프로젝트 안에 시한 폭탄을 설치한 것과 같다. 규약은 보증과 같다. 규약을 위반하면 코드가 작동을 멈췄을 때 문제가 된다. 상속된 규약 클래스를 상속하거나 다른 라이브러리의 인터페이스를 구현할 때는 규약을 반드시 지켜야 한다..
[Effective Kotlin] Item No.31 문서로 규약을 정의하라 메시지 출력 방법을 자유롭게 바꿀 수 있게 함수로 추출했다. 하지만 문서화가 잘 돼 있지 않다. 다른 개발자는 이 코드를 읽고 당연히 토스트를 출력할 거라 생각할 수 있다. 하지만 showMessage라는 이름은 토스트가 아닌 다른 타입으로도 메시지를 출력할 수 있게 하고자 붙인 이름 이다. 따라서 이 함수가 뭘 하는지 명확하게 설명하고 싶다면 KDoc 주석을 붙이는 게 좋다. 일반적으로 대부분의 함수, 클래스는 이름만으로 예측할 수 없는 세부사항들을 갖고 있다. 예를 들어 아래 코드에서 powerset()의 powerset은 멱집합이라는 명확한 수학적 개념이지만 멱집합이 뭔지 모르는 사람이 있을 수 있으므로 추가적인 설명이 필요하다. 이런 설명만으론 멱집합이 어떤 순서인지 알 수 없다. 따라서 이런 함..
[Effective Kotlin] Item No.30 요소의 가시성을 최소화하라 class CounterSet( private val innerSet: MutableSEt = setOf() ) : MutableSet by innerSet { var elementsAdded: Int = 0 private set } 가시성과 관련된 제한은 한번 정해지면 변경하기 어렵다. 클래스의 상태를 나타내는 프로퍼티를 외부에서 변경할 수 있다면 클래스는 자신의 상태를 보장할 수 없다. 위처럼 세터만 private으로 만드는 코드를 많이 사용한다. 코틀린에서는 구체 접근자의 가시성을 제한해 모든 프로퍼티를 캡슐화하는것이 좋다. 가시성이 제한될수록 클래스의 변경을 쉽게 추적할 수 있으며, 프로퍼티의 상태를 더 쉽게 이해할 수 있다. 또한 상태 변경은 동시성을 처리할 때 중요하며 이는 병렬 프로그래밍에서..
[Effective Kotlin] Item No.29 외부 API를 랩(wrap)해서 사용하라 API 설계자가 안전하지 않다고 하거나 API 설계자가 안전하다고 해도 우리가 그걸 신뢰할 수 없다면 그 API는 불안정한 것이다. 이런 불안정한 API를 과도하게 사용하는 것은 굉장히 위험하다. 어쩔 수 없이 이런 API를 써야 한다면 최대한 로직과 직접 결합시키지 않는 게 좋다. 그래서 많은 프로젝트가 잠재적으로 불안정하다고 판단되는 외부 라이브러리 API를 랩해서 사용한다. 랩해서 사용하면 아래와 같은 자유, 안정성을 얻을 수 있다. - 문제가 있다면 wrapper만 바꾸면 되므로 API 변경에 쉽게 대응 가능하다. - 프로젝트 스타일에 맞춰서 API 형태를 조정할 수 있다. - 특정 라이브러리에서 문제가 발생하면 wrapper를 수정해서 다른 라이브러리를 쓰도록 코드를 쉽게 바꿀 수 있다. - 필..