Kotlin

생성자 역할을 하는 Factory 함수

kakaroo 2022. 4. 30. 12:50
반응형

객체를 만드는 일반적인 방법은 생성자를 사용하는 방법이지만, 팩토리 패턴을 이용한 객체를 생성할 수도 있습니다.

 

article logo

 

팩토리 매소드 패턴?
생성자로 직접 객체를 생성하는 것이 아니라, 메소드를 통해 객체를 생성하는 코딩 패턴
fun makeListView(config: Config) : ListView {
	val items =
    	return ListView(config)	// <-- 이 때, 생성자가 호출됨
}

 

 

 

팩토리 패턴이 무엇인지 비교해 보겠습니다.

먼저, 팩토리 패턴이 미 적용된 예시입니다.

라이더생성(타입)
    if (타입 == "정규직")
        정규직라이더 라이더 = new 정규직라이더("김찬정")
    else if (타입 == "파트타임")
        파트타임라이더 라이더 = new 파트타임라이더("김찬정")
    else if (타입 == "야간타임")
        if (현재시각 > 오후9시)
            야간타임 라이더 = new 야간타임("김찬정")
        else
            println("야간타임 라이더 생성불가")
    ...

다양한 타입의 라이더객체를 생성할 경우 분기 처리가 필요하며
야간타임 라이더 객체는 오후 9시 이후만 생성 할 수 있습니다.(객체성성 조건 로직)

 

 

다음은 팩토리 메소드 패턴을 적용한 예시입니다.

라이더생성(타입)
    라이더 라이더 = 라이더팩토리.라이더생성(타입)

분기처리 및 객체 생성 조건 로직을 팩토리객체로 위임하고 일관성있는 방법으로 객체를 생성합니다.

다른 종류의 라이더클래스 추가 및 생성 로직의 변경에 의한 소스코드 변경이 필요 없습니다.

 

Class 정의

interface Rider {
    fun delivery(): String
    fun repairVehicle(): String
}

class FullTimeRider : Rider {
    override fun delivery() = "오토바이배달"
    override fun repairVehicle() = "오토바이수리"
}

class PartTimeRider : Rider {
    override fun delivery() = "자전거배달"
    override fun repairVehicle() = "자전거수리"
}

 

Factory method

class RiderFactory {
    fun createRider(className: String): Rider {
        return when (className) {
            "fulltime" -> FullTimeRider()
            "parttime" -> PartTimeRider()
            "nighttime" ->
                if (LocalDateTime.now().hour > 21)
                    NightTimeRider()
                else
                    throw IllegalArgumentException("오후 9시 이후만 생성 가능합니다")
            else -> throw IllegalArgumentException("생성할 수 있는 객체명이 아닙니다.")
        }
    }
}

 

Client

class DeliveryService {
    private var riderFactory = RiderFactory()

    fun assignDelivery(deliveryId: String) {
        val rider = riderFactory.createRider("fulltime")
        rider.delivery(deliveryId)
    }
}

 

 

 

* 팩토리 클래스의 메소드

data class Student (val id: Int, val name: String)

class StudentFactory {
	val nextId = 0
    fun next(name: String) = Student(nextId++, name)
}

main() {
	val factory = StudentFactory()
    val s1 = factory.next("James")
    val s2 = factory.next(Tom")
}​

 

 

 


 

팩토리 함수(패턴)의 특징

생성자와 다르게 함수에 원하는 이름을 붙일 수 있다.
인터페이스의 뒤에서 실제 객체의 구현을 숨긴 원하는 타입을 반환할 수 있다.
매번 새로운 객체를 반환하는게 아니라 싱글턴처럼 단일객체를 반환하거나 캐싱 메커니즘을 적용하는 것이 가능하다.
아직 존재하지 않는 객체를 반환할 수도 있다.
팩토리 함수는 인라인함수로 만들 수 있으며, 그 파라미터들을 reified로 만들 수 있다.
생성자로 만들기 복잡한 객체를 단순하게 만들어 낼 수 있다.


팩토리 함수를 활용한 객체 생성방식


1. Companion 팩토리 함수: companion 객체를 사용하는 일반적인 방식.

class MyLinkedList<T> (val head: T, val tail: MyLinkedList<T>?) {
	companion object {
    	fun <T> of(vararg elements: T) : MyLinkedList<T> {
        	/*   */
        }
    }
}

val list = MyLinkedList.of(1, 2)


2. 확장 팩토리 함수: companion 객체를 직접 수정할 수 없는 경우 확장 함수를 이용한 팩토리 함수 선언.

이미 companion 객체가 존재할 때, companion 객체를 직접 수정할 수는 없고 다른 파일에 함수를 만들어야 한다면 확장 함수를 활용합니다.

interface Tool {
	companion object {	//비어 있는 companion 정의
    }
}

//확장 함수
fun Tool.Companion.createBigTool( /*...*/ ) : BigTool {
	// ...
}
​
// 사용
Tool.createBigTool()

3. top-level 팩토리 함수: top-level에 선언하는 팩토리 함수.

예) listOf, setOf, mapOf


4. 가짜 생성자: top-level 함수처럼 보이고 생성자처럼 사용하는 함수를 가짜생성자라고 합니다.
다음과 같은 이유로 가짜생성자를 사용합니다.

 

아래는 생성자처럼 보이나, top-level 함수입니다. 그래서 가짜 생성자라고 부릅니다.

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


– 인터페이스를 위한 생성자를 만들 때
– reified 타입 아규먼트를 갖게 하고 싶을 때

기본생성자를 만들 수 없는 상황 또는 생성자가 제공하지 않는 기능(reified 타입 파라미터)으로 생성자를 만들어야 하는 상황에서만 가짜 생성자를 사용하는 것이 좋습니다.
팩토리 클래스의 함수: 팩토리 클래스는 상태를 가질 수 있다는 특징을 갖으며, 이를 활용하여 다양한 기능 및 전략을 도입할 수 있습니다.

반응형

'Kotlin' 카테고리의 다른 글

전개 연산자 *  (0) 2022.05.05
생성자 (constructor)  (0) 2022.04.30
FrameLayout size/margin 동적변경  (0) 2022.04.24
SubsamplingScaleImageView  (0) 2022.04.23
Animation  (0) 2022.04.18