본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Effective Kotlin] Item 33 : Consider factory functions instead of constructors

by 돼지왕 왕돼지 2022. 5. 5.
반응형

이 글은 Effective Java 를 완독하고, Kotlin 을 상용으로 사용하는 개발자 입장에서
Effective Kotlin 글 중 새로운 내용, remind 할 필요 있는 부분, 핵심 내용 등만 추려 정리한 내용입니다.

 

#
Factory function 의 장점은 아래와 같다.

  1. Constructor 와 다르게 이름을 가질 수 있다.
  2. Constructor 와 다르게 subtype 을 return 할 수 있다.
  3. Constructor 와 다르게 매번 새로운 object 를 만들지 않아도 된다. 기존에 만든 것을 줄 수도 있고, null 을 return 할 수도 있다.
  4. 아직 존재하지 않는 object 를 제공할 수 있다. 이는 annotation processing 기반으로 객체가 생성되는 경우를 이야기한다.
  5. 해당 객체 바깥쪽에 정의하는 경우 same file, same module 등의 visibility control 도 가능하다.
  6. Factory function 은 inline 될 수 있고, 따라서 type parameter 가 reified 될 수 있다.
  7. 복잡하게 생성해야 하는 것들을 간단하게 생성하도록 할 수 있다.
  8. Constructor 는 superclass 의 constructor 를 즉각적으로 불러야 하지만, factory 를 사용하면 이를 미룰 수 있다.

 

#
Factory function 은 주로 secondary constructor 들과 비슷한 계위이고, primary constructor 의 계위는 아니다. 그리고 또 다른 비슷한 factory function 와 같은 계위라고 볼 수 있다.

 

#
Kotlin 에는 다음의 factory function 들이 있다.

  1. Companion object factory function
  2. Extension factory function
  3. Top-level factory function
  4. Fake constructors
  5. Methods on a factory class

 

 

Companion Object Factory Function

#
해당 class 에 속해 있어 결속력이 좋고, java 를 비롯한 다른 언어에도 친숙한 패턴이다.
Java convention 에 따라 from, of, valueOf, getInstance, createInstance, newInstance, getType, newType 등의 이름이 많이 사용된다.

 

#
Companion object 가 interface 를 구현하거나, class 를 확장할 수 있다는 점에서 강력함을 보여준다.
예를 들면 companion object : ActivityFactory() { ... } 와 같이 사용할 수 있다.

 

 

Extension factory functions

#
기 존재하는 파일을 수정하지 않으면서 혹은 수정하지 못하는 환경에서 사용되는 방법이다.
단, 기 존재하는 파일에 empty 이더라도 companion object 가 정의되어 있어야 한다.

fun Tool.Companion.createBigTool( /.../) : BigTool{ /*... */}

 

 

Top-level functions

#
List, Map 과 같이 작으면서 자주 사용되는 object 생성에 좋다.
public top-level function 은 단점들이 있기 때문에 남용을 피하는 것이 좋다.
어디서든 접근 가능해서 IDE 가 tip 으로 추천한다던지 하는 단점이다. top-level function 이 접근하는 class 의 함수 이름처럼 되어 있을 때 혼란을 가져올 수 있어 치명적일 수 있다. 그래서 사용한다면 이름이 잘 지어져야 한다.

 

 

Fake constructors

#

val reference: () -> A = ::A


이런 식의 construct 도 가능하다.

 

#
convention 으로 class 는 대문자로 시작하고, function 은 소문자로 시작한다.
이런 점을 활용하여 다음과 같이 fake constructor 를 만들 수 있다.

public inline fun List(
	size:Int,
	init: (index: Int) -> T
) : List = MutableList(size, init)

convention 때문에 constructor 처럼 보이지만 사실은 function 이다.

 

#
실제 constructor 대신 fake constructor 를 만드는 2가지 이유는..

  1. interface 를 위한 constructor 를 갖기 위해
  2. reified type argument 를 갖기 위해

 

#
companion object 와 invoke operator 를 사용한 fake constructor 도 있다.

class Tree{
	companion object{
		operator fun invoke(size:Int, generator: (Int)->T): Tree{
			// ...
		}
	}
}

Tree(10){ "$it" }

 

이 방법은 다음과 같은 혼란을 가져온다.

val f: ()->Tree = ::Tree // constructor
val f: ()->Tree = ::Tree // fake constructor
val f: ()->Tree = Tree.Companion::invoke // detail 하게 기술한 fake constructor

그래서 정말 특별한 이유가 있는 것이 아니면 top-level constructor 형태가 추천된다.

 

 

Methods on factory class

#
factory 관련된 창의적 패턴이 많이 있다. 예를 들면 abstract factory 나 prototype 이 그것이다.

 

#
Factory class 는 factory function 대비 상태를 가질 수 있다는 장점을 가지고 있다.
상태를 이용하여 cache, 기존 객체 copy 를 통한 create 등으로 최적화도 할 수 있다.

 

 

Summary

#
Factory function 은 fake constructor, top level factory method, extension factory function 등이 있는데, 가장 추천되는 것은 Companion Object 에 정의한 factory function 이다.

 

 

 

반응형

댓글