티스토리 뷰

반응형

Head First - Design Patterns 의 템플릿 메소드 패턴 기반으로 작성하였습니다. 자세한 설명은 도서를 참고해주세요.

프로토타입 패턴에 대해서 알아보겠습니다. 이 패턴은 어떤 클래스의 인스턴스를 만드는 것이 자원/시간을 많이 필요하거나 복잡한 경우 프로토타입 패턴을 사용하면 됩니다.

 

프로토타입 패턴이란?

프로토타입 패턴을 활용해서 기존 인스턴스를 복사하기만 하면 새로운 인스턴스를 만들 수 있습니다. 자바에서는 clone() 메소드나 역직렬화로 할 수 있습니다. 복사하였기에 클라이언트 코드에서 어떤 클래스의 인스턴스를 만드는지 전혀 모르는 상태에서 새로운 인스턴스를 만들 수 있습니다.

장점
클라이언트에서는 새로운 인스턴스를 만드는 복잡한 과정을 알 수 없어도 됩니다.

단점
때때로 객체의 복사본을 만드는 일이 매우 복잡한 경우가 있습니다.

 

소스를 통한 프로토타입 알아보기

예를 들어볼까요? 롤플레이 게임에는 다양한 몬스터가 존재합니다. 다양한 몬스터는 주변 환경에 따라서 다르게 구성하고 싶습니다. 맵이 다양해지고 요구사항이 많아 질수록 생성자 param으로 몬스터를 생성한다면 소스의 양이 많아지고 보기에는 안좋을거 같습니다. 생성 과정을 따로 캡슐화 할 수 있는 방법이 없을지 고민해 봅시다.

생성과정을 캡슐화 할 때 프로토타입 패턴을 활용하시면 됩니다. 클라이언트가 현재 상황에 맞는 새로운 몬스터를 원할 때 레지스트리에서 적당한 몬스터를 찾아서 clone으로 만들어 제공하면 됩니다. 이렇게 되었을 시 클라이언트는 몬스터의 종류를 알 수 없이 원하는 몬스터를 가져올 수 있습니다.

간단한 개념정도 아는 정도로 구성한점은 참고해서 보시면 좋겠습니다.

class MainApplication {
    companion object {
        val TAG = MainApplication::class.simpleName

        @JvmStatic fun main(args : Array<String>) {
            val monsterMaker = MonsterMaker()
            for(i in 0 until 5) {
                monsterMaker.makeRandomMonster()
            }
        }
    }
}

5번 호출 할 것이고 호출 카운트에 따라서 ‘고블린’, ‘메두사' 를 호출 할 것입니다.

class MonsterMaker {
    fun makeRandomMonster() {
        val m : Monster = MonsterRegistry.getMonster()
        m.print()
    }
}

몬스터를 만들어 보겠습니다. MonsterRegistry의 getMonster() 로 인스턴스를 가져옵니다. 가져온 몬스터를 print() 하여 화면에 노출 하였습니다.

class MonsterRegistry {
    companion object {
        private var correctMonster : Monster? = null
        private var callCnt = 0
        private val goblin = Goblin()
        private val medusa = Medusa()

        fun getMonster(): Monster {
            searchMonster()
            return correctMonster?.clone() as Monster
        }

        private fun searchMonster() {
            if(callCnt++ % 2 == 0){
                correctMonster = goblin
            }else {
                correctMonster = medusa
            }
        }
    }
}

getMonster()에서 Clone()을 통해 몬스터를 clone하여 가져오는 것을 알 수 있습니다.

interface Monster: Cloneable {
    fun print()

    public override fun clone(): Any {
        return super.clone()
    }
}
class Goblin : Monster {
    override fun print(){
println("고블린 입니다.")
    }

    override fun clone(): Any {
        return Goblin()
    }
}
class Medusa : Monster {
    override fun print(){
println("메두사 입니다.")
    }

    override fun clone(): Any {
        return Medusa()
    }
}

Monster 인터페이스와 고블린, 메두사 구조는 다음과 같습니다.

참고로 kotlin 로 clone() 을 구현하여 간단히 구현하였습니다. 실무에서 사용 시 clone()은 더 복잡할 것입니다.

 

마무리

때로는 시스템에서 복잡한 클래스 계층구조에서 다양한 형식의 객체 인스턴스를 새로 만들어야하는 경우가 생길 수 있습니다. 이때 프로토 타입 패턴을 사용하는 것을 고려해도 좋을거 같습니다. 복잡한 구조의 인스턴스를 만드는 것보다 프로토타입 패턴을 활용하여 보다 간단하게 만들 수 있습니다. 상황에 맞는 디자인 패턴으로 효율적인 코딩하기를 응원합니다.

반응형

'프로그래밍 > Design Patterns' 카테고리의 다른 글

비지터(Visitor) 패턴  (0) 2022.02.14
메멘토(Memento) 패턴  (0) 2022.02.09
미디에이터(Mediator) 패턴  (0) 2022.02.08
인터프리터(Interpreter) 패턴  (0) 2022.02.02
플라이웨이트(Flyweight) 패턴  (0) 2022.01.26
댓글