본문 바로가기
Engineering/SW Design

[디자인 패턴] 생성패턴 - 프로토타입

by 쿨쥰 2024. 3. 3.

생성패턴 - 프로토타입


이전 글 : 생성패턴 - 빌더

2023.05.25 - [Engineering/SW Design] - [디자인 패턴] 생성패턴 - 빌더

 

[디자인 패턴] 생성패턴 - 빌더

생성패턴 - 빌더 이전 글 : 생성패턴 - 추상 팩토리 2023.05.24 - [Engineering/SW Design] - [디자인 패턴] 생성패턴 - 추상 팩토리 [디자인 패턴] 생성패턴 - 추상 팩토리 생성패턴 - 추상 팩토리 이전 글 : 생

skidrow6122.tistory.com

 

 

요약

코드를 그들의 클래스에 의존시키지 않고, 기존 객체들을 복사할 수 있도록 해주는 패턴이다.

객체를 생성할때 완전 새로 new 생성자를 활용해서 인스턴스를 만들기 보다는, 이미 만들어진 인스턴스의 내용 일부를 수정하여 사용하고 싶을때가 있다.

그런 경우, clone() 메서드를 활용하여 인스턴스를 생성할 수 있는데, 이런 개념을 확장하여 아예 처음부터 일반적인 프로토타입을 만들어 두고, 그것을 복사해서 필요한 부분을 수정해서 사용하는 것이다.

이러한 패턴을 따르면 new object()로 객체를 생성하는 것보다 더 펀리하게 쓸 수 있다.

 

키워드 : clone() / 이미 생성 된 인스턴스를 복제

 

 

문제

객체가 있고 그 객체의 정확한 복사본을 만드는 상황을 가정 하자면,

  • 먼저 같은 클래스의 새 객체를 new를 통해 생성해야 하고.
  • 그런 다음 원본 객체의 모든 필드들을 살펴본 후 해당 값들을 새 객체에 복사해야 한다.

하지만, 객체의 필드들 중 일부가 비공개라면, 객체 자체의 외부에서 볼 수 없을 수 있으므로 모든 객체를 그런 식으로 복사하지 못한다.

또한 객체의 복제본을 생성하려면 객체의 클래스를 알아야 하므로, 코드가 해당 클래스에 의존하게 된다는 는 단점 또한 존재 한다.

여기에 객체 생성시마다 DB 커넥션을 맺는 기능을 포함한다면, 인스턴스를 생성할 때마다 큰 자원의 소모를 요구할 것이다.

 

 

해결책

프로토타입 패턴은 실제로 복제되는 객체들에 복제 프로세스를 위임하며, 복제의 결과물이 될 모든 객체에 대한 공통 인터페이스를 선언한다.

인터페이스를 사용하면 코드를 객체의 클래스에 결합하지 않고도 해당 객체를 복제할 수 있으며, 일반적으로 이러한 인터페이스에는 단일 clone 메서드만 포함된다.

clone 메서드는 현재 클래스의 객체를 만든 후 이전 객체의 모든 필드 값을 새 객체로 전달한다.

이때 객체들이 같은 클래스에 속한 다른 객체의 비공개 필드들에 접근(access) 할 수 있도록 하므로 비공개 필드들을 복사하는 것도 가능하다.

복제를 해줄 수 있게 하는 객체를 프로토타입이라고 하며, 사용하는 방법은 간단하다.

일반적으로 아래와 같은 스텝으로 프로토타입 패턴은 달성 된다.

  • 다양한 방식으로 설정된 객체들의 집합을 만든다.
  • 그 후 설정한 것과 비슷한 객체가 필요할 경우 처음부터 새 객체를 생성하는 대신 프로토타입을 복제한다.

 

구조

 

 

 

Hands on

  • 자전거 클래스의 객체 생성을 예로 들어본다.

<<Bike.kt>>

open class Bike : Cloneable {
    private var gears: Int = 0
    private var bikeType: String? = null
    var model: String? = null
    
    private set
    init {
        bikeType = "Standard"
        model = "general"
        gears = 4
    }

    public override fun clone(): Bike {
        return Bike()
    }
    
    fun makeAdvanced(){
        bikeType = "Advanced"
        model = "super"
        gears = 12
    }
}
  • BIke 클래스는 clone() 을 제공하는 프로토타입 클래스이다.
  • private init 메서드로서 객체 생성시 내부적으로 Standard 타입의 Bike 를 리턴한다.
  • 여기에 makeAdvanced() 을 제공하여 Advanced 타입의 Bike 를 세팅 해주기도 한다.

 

<<클라이언트 코드>>

fun makeSuper(basicBike: Bike): Bike {
    basicBike.makeAdvanced()
    return basicBike
}

fun main(args: Array<String>) {
    val bike = Bike()
    val basicBike = bike.clone()
    val advancedBike = makeSuper(basicBike)
    println("프로토타입을 통해 생성된 슈퍼 바이크" + advancedBike.model)
}
  • 클라이언트 코드에서 Bike() 클래스를 기본 인스턴스로 일단 만들어 두고,
  • makeSuper() 클래스에서 프로토타입 인터페이스 내의 makeAdvanced() 를 호출하여 슈퍼 자전거로 값을 세팅하였다.

 

 

핵심

  • 객체를 생성하는데 비용이들거나, 이미 유사한 객체가 존재하는 경우에 사용된다.
  • 프로토타입 패턴은 각자의 객체를 초기화하는 방식만 다른 자식 클래스들의 수를 줄이고 싶을 때 사용된다.
  • clone할 때 얕은 복사와 깊은 복사 둘 다 이루어진다. 
    • primitive한 타입들은 깊은 복사가 이루어지지만(값이 넘어가므로), heap에 올라가는 객체들은 얕은 복사가 이루어진다.
    • 즉, 생성 비용이 큰 경우 새로 생성하는 대신 다른 객체를 복사하는 패턴

 


프로토타입은 인스턴스 객체 상태에서의 Clone 이 핵심이다.

댓글