Hanbit the Developer

Effective Kotlin | 3장. 재사용성 본문

Mobile/Kotlin

Effective Kotlin | 3장. 재사용성

hanbikan 2023. 6. 13. 18:52

아이템 19: knowledge를 반복하여 사용하지 말라

프로그래밍의 가장 큰 규칙은 다음과 같다:

프로젝트에서 이미 있던 코드를 복사해서 붙여넣고 있다면, 무언가가 잘못된 것이다.

knowledge

의도적인 정보를 뜻하며 코드나 데이터로 표현할 수 있다. 상속 시 오버라이드가 안 되게 강제한다는 것은 ‘해당 메서드가 슈퍼클래스와 동일하게 동작하기 원한다’는 knowledge를 함유한다. 알고리즘 작동 방식, UI 형태, 원하는 결과 등이 모두 의도적인 정보이다.

가장 중요한 knowledge 두 가지는 다음과 같다:

  1. 로직: 프로그램이 어떤 식으로 동작하는지, 프로그램이 어떻게 보이는지
  2. 공통 알고리즘: 원하는 동작을 하기 위한 알고리즘

둘의 차이점은, 전자의 경우 계속해서 변하지만 후자는 그렇지 않다는 점이다.(변경이 있다고 해도 효율 개선 정도이다.) 이에 따라 후자를 자세하게 다룰 예정이며, 먼저 전자를 살펴보자.

모든 것은 변화한다

기술, 언어 등 모든 것이 변화한다. 따라서 이를 대비해야 한다.

변화 시 가장 큰 적은 knowledge가 반복되어 있는 부분이다. 전부 replace하는 방식은 위험하고 시간이 소요되며 확장성을 막는다. 다행히도 이를 줄일 수 있는 도구 및 기능들을 활용할 수 있다.

언제 코드를 반복해도 될까?

knowledge 반복을 줄여선 안 되는 경우가 있다. 얼핏보면 knowledge 반복처럼 보이지만 실제로는 다른 knowledge를 나타내는 경우 추출해선 안 된다.

여러 코드가 같은 knowledge인지 아닌지를 “함께 변경될 가능성이 높은가? 따로 변경될 가능성이 높은가”라는 질문으로 결정할 수 있다.

한 가지 휴리스틱으로, 비즈니스 규칙이 다른 소스에서 왔는지 확인하는 방법이 있다. 다른 곳에서 왔다면 독립적으로 변경될 가능성이 높다. SRP 원칙 또한 잘못된 코드 추출을 피하게 하는 규칙이다.

단일 책임 원칙

pitfalls: Student class has qualifiesForScholarship() and also isPassing()

→ StudentQualifiesForScholarshipValidator, StudentIsPassingValidator라는 두 클래스로 분리하거나, 서로 다른 모듈에서 확장 함수로 정의하여 해결한다.

시사점은 다음과 같다:

  • 서로 다른 곳(부서)에서 사용하는 knowledge는 독립적으로 변경할 가능성이 많으므로 서로 다른 knowledge로 취급한다.
  • 다른 knowledge는 분리해야 한다.

아이템 20: 일반적인 알고리즘을 반복해서 구현하지 말라

이미 있는 standard libraries를 활용함으로써, 코드 작성 속도를 높이고, 코드 가독성이 높아지며, 직접 구현 시 발생하는 실수를 제거하고, 최적화 혜택을 받을 수 있다.

표준 라이브러리 살펴보기

stdlib라는 표준 라이브러리는 수많은 알고리즘을 가지고 있다.

나만의 유틸리티 구현하기

모든 숫자의 곱을 계산하는 함수처럼 널리 알려져 있으나 표준 라이브러리에 없는 경우에는 이를 universal utility function으로 정의하는 것이 좋다. 처음 본 개발자가 함수 내용을 곧바로 알아차리기 쉽고 나중에 이런 기능이 재사용될 수 있기 때문에 여러 번 사용되지 않더라도 이렇게 구현해두는 것이 좋다.

동일 함수를 여러 번 구현하는 것은 잘못된 일이기 때문에 중복 구현을 피하기 위해 기존에 관련 코드가 있는지 탐색해야 한다.

확장 함수, 톱레벨 함수, 프로퍼티 위임, 클래스 등으로 알고리즘을 추출할 수 있으며, 특히 확장 함수의 장점은 다음과 같다:

  • 행위를 나타내기에 좋다. side effect가 없는 경우에는 더 좋다.
  • 구체적인 타입이 있는 객체에만 사용할 수 있는 제한이 있다.
  • 확장 리시버로 사용하는 것이 가독성이 좋다.
  • 함수 검색이 편하다.

아이템 21: 일반적인 프로퍼티 패턴은 프로퍼티 위임으로 만들어라

lazy, Delegates.observable(+ Delegates.vetoable, Delegates.notNull) 등을 통해 간단하고 type-safe하게 다양한 패턴을 구현할 수 있다.

예시로 값을 get, set할 때 해당 값을 프린트하는 행위가 빈번하다고 가정해보자. 이는 다음과 같이 구현할 수 있다:

private class LoggingProperty<T>(var value: T) {
	operator fun getValue(...): T {
		print(value)
		return value
	}
}

var token: String? by LoggingProperty(null)

아이템 22: 일반적인 알고리즘을 구현할 때 제네릭을 사용하라

아이템 23: 타입 파라미터의 섀도잉을 피하라

프로퍼티와 파라미터가 같은 이름을 갖는 경우를 섀도잉이라고 한다:

class Forest(val name: String) {
	fun addTree(name: String) { // ... }
}

아이템 24: 제네릭 타입과 variance 한정자를 활용하라

관련성을 원할 때 in, out 키워드를 활용한다.

고차함수에서 모든 파라미터 타입은 contravariant, 모든 리턴 타입은 covariant이다.

아이템 25: 공통 모듈을 추출해서 여러 플랫폼에서 재사용하라

코틀린 멀티플랫폼 기능을 활용하면 로직 구현을 한 번만 하고 두 플랫폼에서 재사용할 수 있다. 공통 모듈을 만들고 여기에 다양한 비즈니스 로직을 구현하면 된다. 이때 프레임워크, 플랫폼에 종속되지 않아야 한다.(클린 아키텍처)