목록Kotlin/이펙티브 코틀린 (52)
안드로이드 개발자 노트
규약을 한쪽에서 위반할 수도 있습니다. 규약은 보증과 같으며, 규약을 위반하면 코드가 작동을 멈췄을 때 문제가 됩니다. 상속된 규약 클래스를 상속하거나, 다른 라이브러리의 인터페이스를 구현할 때는 규약을 반드시 지켜야 합니다. 규약을 지키지 않는다면, 객체가 제대로 동작하지 않을 수도 있습니다. 예를 들어 hashCode가 제대로 구현되지 않으면, HashSet과 함께 사용할 때 제대로 동작하지 않습니다. 다음 코드를 살펴보면, 원래 세트는 중복을 허용하지 않는데, equals가 제대로 구현되지 않았으므로 중복을 허용해 버립니다. class Id(val id: Int) { override fun equals(other: Any?) = other is Id && other.id == id } val set..
'Item27. 변화로부터 코드를 보호하려면 추상화를 사용하라'에서 살펴보았던 메시지를 출력하는 함수처럼 추상화를 통해 만들어졌다면, 쉽게 이해하기 어려울 수 있습니다. 따라서 이 함수가 무엇을 하는지 명확하게 설명하고 싶다면, KDoc 주석을 붙여 주는 것이 좋습니다. KDoc 주석은 일반적으로 함수와 클래스의 이름만으로 예측할 수 없는 세부 사항을 포함하고 있습니다. 규약 어떤 행위를 설명하면 사용자는 이를 약속으로 취급하며, 이를 기반으로 스스로 자유롭게 생각하던 예측을 조정합니다. 규약을 정의해두면, 클래스를 만드는 사람과 사용하는 사람 모두 미리 정의된 규약에 따라 독립적으로 작업할 수 있습니다. 규약 정의하기 규약을 정의하는 방법은 다양합니다. 이름: 일반적인으로 이름만으로 동작을 예측할 수 ..
작은 인터페이스는 배우기 쉽고 유지하기도 쉬우며, 기능이 많은 클래스보다 적은 클래스가 유지보수가 쉽습니다. 어떤 수정을 하기 위해서는 클래스 전체를 이해해야 하는데, 요소 자체가 적다면 유지보수하고 테스트할 것이 적기 때문입니다. 변경을 가할 때는 기존의 것을 숨기는 것보다 새로운 것을 노출하는 것이 쉽습니다. 일반적으로 공개적으로 노출되어 있는 요소들은 이미 외부에서 사용되고 있을 것이며, 이런 요소들을 변경하면(특히나 가시성을 변경하면) 이 코드를 사용하는 모든 부분이 영향을 받기 때문입니다. 따라서 처음에는 작은 API로서 개발을 하도록 강제하는 것이 더 좋을 수 있으며, 접근 제어자를 잘 활용해서 외부에서의 임의 수정을 최소화 할 수 있습니다. // 외부에서 set 접근 가능한 경우 var el..
많은 프로젝트가 잠재적으로 불안정하다고 판단되는 외부 라이브러리 API를 랩(wrap)해서 사용합니다. 랩해서 사용하면, 다음과 같은 자유와 안정성을 얻습니다. 문제가 있다면 래퍼(wrapper)만 변경하면 되므로, API 변경에 쉽게 대응한다. 프로젝트의 스타일에 맞춰서 API의 형태를 조절할 수 있다. 특정 라이브러리에 문제가 발생하면, 래퍼를 수정해서 다른 라이브러리를 사용하도록 쉽게 변경할 수 있다. 쉽게 동작을 추가하거나 수정할 수 있다. 단점은 다음과 같습니다. 래퍼를 따로 정의해야 한다. 다른 개발자가 볼때, 어떤 래퍼들이 있는지 따로 확인해야 한다. 내부에서 사용하는 래퍼들이므로 외부의 도움을 받을 수 없다.
프로그래밍에서는 안정적이고 최대한 표준적인 API를 선호합니다. 주요 이유는 다음과 같습니다. API가 변경되면 이를 활용하는 다른 코드들에도 영향을 미친다. 사용자가 새로운 API를 배워야 한다. 버전 하지만 좋은 API를 한번에 설계할 수는 없으며, 이를 계속해서 개선하기 위해서 변경을 하게됩니다. API 제작자가 'API'또는 'API의 일부'가 불안정하다면, 일반적으로 버전을 활용해서 라이브러리와 모듈의 안정성을 나타냅니다. 일반적으로 시멘틸 버저닝(Semantic Versioning, SemVer)을 사용합니다. 이 시스템은 버전 번호를 세 번호(MAJOR, MINOR, PATCH)로 나누어서 구성합니다. MAJOR 버전: 호환되지 않는 수준의 API 변경 MINOR 버전: 이전 버전과 호환되는..
추상화를 통해 변화로부터 코드를 보호하는 행위가 어떤 자유를 가져오는지 살펴보겠습니다. 상수 리터럴을 상수 프로퍼티로 변경하면 해당 값에 의미있는 이름을 붙일 수 있으며, 상수의 값을 변경해야 할 때 훨씬 쉽게 변경할 수 있습니다. fun isPasswordValid(text: String): Boolean { if (text.length < 7) return false //... } 여기서 숫자 7은 사실 '비밀번호의 최소 길이'를 나타냅니다. const val MIN_PASSWORD_LENGTH = 7 fun isPasswordValid(text: String): Boolean { if (text.length < MIN_PASSWORD_LENGTH) return false //... } 이처럼 상수로..
계층이 잘 분리되면 어떤 계층에서 작업할 때 그 아래의 계층은 이미 완성되어 있으므로, 전체를 이해할 필요없이 해당 계층만 생각하면 됩니다. 추상화 레벨 계층 레벨이 높아질 수록 물리 장치와 프로세서로부터 멀어집니다. 높은 레벨일 수록 걱정해야 하는 세부적인 내용들이 적어져서 단순함을 얻지만, 제어력을 잃을 수 있습니다. 예를 들어 C언어는 메모리 관리를 직접 할 수 있지만, 자바는 가비지 컬렉터가 자동으로 메모리를 관리해 줍니다. 따라서 자바는 메모리 사용을 최적화하는 것이 힘듭니다. 추상화 레벨 통일 코드도 추상화를 계층처럼 만들어서 사용할 수 있으며, 이를 위한 기본적인 도구가 바로 함수입니다. 함수도 높은 레벨과 낮은 레벨을 구분해서 사용해야 한다는 원칙(Single Level of Abstrac..
정리 코틀린은 멀티 플랫폼을 지원하기 때문에 코틀린 코드를 서로 다른 플랫폼(웹 백엔드 & 프런트엔드, Android & IOS 등등)에서 공유가 가능하다. 공통 논리 또는 공통 알고리즘을 모듈화해서 서로 다른 플랫폼에서 재사용할 수 있다.
다음과 같은 제네릭 클래스가 있다고 합시다. class Cup 위의 코드에서 타입 파라미터 T는 variance 한정자(out 또는 in)가 없으므로, 기본적으로 invariant(불공변성)입니다. invariant(불공변성)라는 것은 제네릭 타입으로 만들어지는 타입들이 서로 관련성이 없다는 의미입니다. 예를 들어 Cup와 Cup, Cup와 Cup은 어떠한 관련성도 갖지 않습니다. fun main() { val anys: Cup = Cup() // Error: type mismatch val nothing: Cup = Cup() // Error: type mismatch } 만약에 어떤 관련성을 원한다면, out 또는 in이라는 variance 한정자를 붙입니다. out은 타입 파라미터를 covarian..
다음 코드처럼 프로퍼티와 파라미터가 같은 이름을 가질 수 있습니다. class Forest(name: String) { fun addTree(name: String) { //... } } 이렇게 되면 지역 파라미터가 외부 스코프에 있는 프로퍼티를 가리게되며, 이를 섀도잉이라고 부릅니다. 섀도잉 현상은 클래스 타입 파라미터와 함수 타입 파라미터 사이에서도 발생합니다. interface Tree class Birch : Tree class Sqruce : Tree class Forest { fun addTree(tree: T) { //... } } 이렇게 코드를 작성하면, Forest와 addTree의 타입 파라미터가 독립적으로 동작합니다. val forest = Forest() forest.addTree(..