Effective Kotlin - Reusability 2

코틀린의 재사용성을 활용하는 방법을 정리한다.

  • 일반적인 알고리즘을 구현할 때, 제네릭을 사용해라
  • 타입 파라미터의 shadowing 을 피해라
  • 공통 모듈을 추출해서 여러 플랫폼에서 재사용해라

일반적인 알고리즘을 구현할 때, 제네릭을 사용해라

type parameter 를 가지는 함수를 generic function 이라고 한다.
예를 들어, type parameter T 를 가지는 filter function 이 있다.

1
2
3
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}

타입 파라미터는 구체적인 타입의 서브 타입만 사용하도록 타입을 제한할 수 있다.
타입에 제한이 걸려서, 내부에서 해당 타입이 제공하는 메서드를 사용할 수 있다.

아래를 보자.
Number 를 타입 파라미터 T 의 상한으로 지정한다.
그리고, 내부에서 Number 클래스에 정의되 메서드를 호출한다.

1
2
3
4
5
fun <T: Number> oneHalf(value: T): Double {
return value.toDouble() / 2.0
}

oneHalf(3) // 실제 타입 인자인 Int 가 Number 를 확장하기 때문에 가능

타입 파라미터의 shadowing 을 피해라

지역 파라미터가 외부 scope 에 있는 프로퍼티를 가리는 것을 shadowing 이라고 한다.
예를 들어,

Read more

Effective Kotlin - Reusability 1

코틀린의 재사용성을 활용하는 방법을 정리한다.

  • knowledge 를 반복해서 사용하지 마라
  • 일반적인 알고리즘을 반복해서 구현하지 마라
  • 일반적인 프로퍼티 패턴은 프로퍼티 위임으로 만들어라

knowledge 를 반복해서 사용하지 마라

knowledge

knowledge 를 반복해서 사용하지 말라는 의미는, 아래와 같이도 표현할 수 있다.

  • DRY 규칙: Don’t Repeat Yourself
  • WET 안티 패턴: We Enjoy Typing, Waste Everyone’s Time, Write Everything Twice

knowledge 는 코드 또는 데이터로 표현할 수 있는, 의도적인 정보이다.
프로그램에서 중요한 두 가지 knowledge 는,

  1. 비즈니스 로직: 프로그램이 어떤 식으로 동작하는지 (시간에 따라 계속해서 변한다)
  2. 공통 알고리즘: 원하는 동작을 하기 위한 알고리즘 (한 번 정의되면 쉽게 변하지 않는다)

둘의 가장 큰 차이는 “변화” 이다.

모든 건은 변화한다

Read more

Bean Validation

Bean Validation 은, JSR 303 에서 처음으로 제안된 어노테이션 기반의 Java Bean 검증 명세이다.
대표적인 구현체로 Hibernate Validator 가 있다.
spring-boot-starter-validation 의존성을 명시하면, hibernate-validator 의존성이 추가된다.

Bean Validation

Bean Validation 은 JSR 303 에서 처음으로 제안되었다.
최초에 제안된 명세의 내용은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
Validating data is a common task that is copied in many different layers of an application, from the presentation tier to the persistence layer.
Many times the exact same validations will have to be implemented in each separate validation framework, proving time consuming and error-prone.
To prevent having to re-implement these validations at each layer, many developers will bundle validations directly into their classes, cluttering them with copied validation code that is, in fact, meta-data about the class itself.

This JSR will define a meta-data model and API for JavaBean validation.
The default meta-data source will be annotations, with the ability to override and extend the meta-data through the use of XML validation descriptors.
It is expected that the common cases will be easily accomplished using the annotations, while more complex validations or context-aware validation configuration will be available in the XML validation descriptors.
The validation API developed by this JSR will not be specific to any one tier or programming model.
It will specifically not be tied to either the web tier or the persistence tier, and will be available for both server-side application programming, as well as rich client Swing application developers.
This API is seen as a general extension to the JavaBeans object model, and as such is expected to be used as a core component in other specifications, such as JSF, JPA, and Bean Binding.

위 내용을 정리하면 다음과 같다.

  • 데이터 검증은, 어플리케이션의 서로 다른 레이어에서 (from the presentation tier to the persistence layer) 반복적으로 수행되는 작업이다.
  • 각 계층에서 이러한 데이터 검증이 재구현되는 것을 피하기 위해, 많은 개발자들은 검증 코드를 클래스 안에 포함시킨다.
  • 이 검증 코드는 해당 클래스의 meta-data (데이터에 대한 정보를 제공하는 데이터) 이다.
  • JSR 303은, JavaBean validation 을 위한 meta-data 모델과 API 를 정의한다.
  • 기본적인 meta-data 는 오버라이드 및 확장이 가능한 어노테이션 기반이다.

Hibernate Validator

Bean Validation 스팩은 현재 2.0 까지 제안되었다.

  1. JSR 303 (Bean Validation)
  2. JSR 349 (Bean Validation 1.1)
  3. JSR 380 (Bean Validation 2.0)

그런데 위 JSR 은 스팩일 뿐, 구현체는 아니다.
검증된 구현체로는 Hibernate Validator 가 있다.
각 JSR 에 따라, Hibernate Validator 의 버젼은 다음과 같다.

Read more

Effective Kotlin - Readability 2

코틀린을 가독성이 좋게 작성하는 방법을 정리한다.

  • 리시버를 명시적으로 참조해라
  • 프로퍼티는 동작이 아니라, 상태를 나타내야한다
  • named arguments 를 사용해라
  • 코딩 컨벤션을 지켜라

리시버를 명시적으로 참조해라

짧게 적을 수 있다는 이유만으로, 리시버를 제거하지 말자.
여러 개의 리시버가 있으면, 리시버를 명시적으로 적어줘야한다.
그러면, 어떤 리시버의 함수인지를 명확하게 알 수 있어서 가독성이 향상된다.

예를 들어 apply, with, run 함수를 사용할 때가 있다.

1
2
3
4
5
6
7
8
9
10
class Node(val name: String) {
fun makeChild(childName: String) =
create("$name.$childName")
.also { print("Created ${it?.name}")} // Created parent.child

fun create(name: String): Node? = Node(name)
}

val node = Node("parent")
node.makeChild("child")

프로퍼티는 동작이 아니라, 상태를 나타내야한다

프로퍼티는 개념적으로,

  • val 의 경우에 getter
  • var 의 경우에 getter 와 setter 를 나타낸다.

그래서 프로퍼티를 정의하여 오버라이드 가능하다.

Read more

Effective Kotlin - Readability 1

코틀린을 가독성이 좋게 작성하는 방법을 정리한다.

  • 가독성을 목표로 설계해라
  • 연산자 오버로드할 때는 의미에 맞게 사용해라
  • Unit? 을 리턴하지 마라
  • 변수 타입이 명확하지 않으면 확실하게 지정해라

가독성을 목표로 설계해라

로버트 마틴의 클린코드에서 다음과 같은 내용이 있다.
개발자가 코드 작성하는데 1분 걸리지만, 읽는데는 10분 걸린다

프로그래밍은 쓰기보다 읽기가 중요하다는 의미이다.
그래서, 가독성을 생각하면서 코드를 작성해야한다.

인지 부하 감소

인지 부하를 줄이는 방향으로 코드를 작성하자.
자주 사용되는 패턴을 활용하면, 뇌가 프로그램의 작동 방식을 이해하는 과정을 더 짧게 만들 수 있다.

다음 두 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
// A
if (person != null && person.isAdult) {
view.showPerson(person)
} else {
view.showError()
}

// B
person?.takeIf { it.isAdult}
?.let(view.showPerson)
?: view.showError()

어느 코드가 더 좋을까 ? A 가 훨씬 가독성이 좋은 코드이다.
가독성이란, 코드를 읽고 얼마나 빠르게 이해할 수 있는지를 의미힌다.
구현 A 가 더 좋은 코드인 이유는,

Read more