오늘도 더 나은 코드를 작성하였습니까?

Prototype Pattern(feat. kotlin) 본문

디자인패턴/생성패턴

Prototype Pattern(feat. kotlin)

hik14 2024. 6. 10. 01:12

정의

 생성할 객체들의 타입이 prototype(프로토타입)인 인스턴스로부터 결정되도록 하며, 인스턴스는 새 객체를 만들기 위해 자신을 복제(clone)하게 한다. 즉, 객체로 부터 객체를 생성해야 될 경우 사용하면 좋다. 복제하려는 객체의 concrete class에 의존하지 않아도 된다.

  • 추상 팩토리 패턴과는 반대로, 클라이언트 응용 프로그램 코드 내에서 객체를 생성하는 것(creator)를 서브클래스(subclass)에서 하지 않는다.
  • 새로운 객체는 일반적인 방법(예를 들어, JAVA에서 new를 사용)을 통하여 객체를 생성(create)할때, 고유의 비용이 주어진 응용 프로그램 상황에 있어서 불가피하게 매우 클 때, 이 비용을 감내하지 않을 수 있게 해준다.
  • 복제하려는 객체가 private한 속성이 있거나, 또는 복제하려는 객체가 상위 클래스로 부터 상속된 속성을 가지고 있을때 일반적인 생성자로 생성이 불가능 할 때 사용한다.

구현

 

예를 들어 hong's company member 클래스의 객체를 복사해보자!

위와 같이 member1의 각 속성을 직접적으로 복사하여 새로운 객체를 생성한다면, memer2는 member1과 HongCompanyMember Class에 많은 의존성을 갖게 된다. 또한, HongCompanyMember에 private 속성이 있거나 이를 상속하는 클래스의 객체의 복제는 불가능하다. 이러한 문제점을 해결해 보자!

 

1.  clone 이라는 단일 메소드를 가진 SAM(Single Abstract Method)을 생성한다.

interface PrototypeHongCompanyMember {
    fun clone(): HongCompanyMember
}

 

2.  HongCompanyMember를 추상 클래스로 바꾸고, PrototypeHongCompanyMember를 구현한다.

추가적으로 중요한 개인정보인 주소와 연봉 private 접근자로 변경해준다.

그리고 자신과 동일한 클래스의 객체를 Parmeter로 받는 생성자를 만들어 준다.

 

abstract class HongCompanyMember(
    val name: String,
    val gender: String,
    val age: Int,
    private val address: String,
    private val salary: Int
) : PrototypeHongCompanyMember {

    protected constructor(member: HongCompanyMember) : this(
        member.name,
        member.gender,
        member.age,
        member.address,
        member.salary
    )
}

 

3. HongCompanyMember를 상속 받으며 각각의 새로운 추가된 속성과 함께 SubClass인 Executives(임원), Employee(직원)을 구현하자! 

-  자신과 동일한 클래스의 객체를 Parmeter로 받는 생성자를 만들어 준다.

- 만약 primitive type이 아닌 속성이 있다면,  deep copy와 shallow copy 에 주의 하면서 생성자를 구현한다.

-  clone() 내부에서 위의 생성자를 호출한다.

class Executives(
    name: String,
    gender: String,
    age: Int,
    private val address: String,
    private val salary: Int,
) : HongCompanyMember(name, gender, age, address, salary) {

    private var delegatedAuthority: String = "CTO"

    private constructor(executives: Executives) : this(
        executives.name,
        executives.gender,
        executives.age,
        executives.address,
        executives.salary
    ) {
        this.delegatedAuthority = executives.delegatedAuthority
    }

    override fun clone(): HongCompanyMember {
        return Executives(this)
    }
}
class Employee(
    name: String,
    gender: String,
    age: Int,
    private val address: String,
    private val salary: Int,
) : HongCompanyMember(name, gender, age, address, salary) {

    private var position: String = "developer"

    private constructor(employee: Employee) : this(
        employee.name,
        employee.gender,
        employee.age,
        employee.address,
        employee.salary
    ) {
        this.position = employee.position
    }

    override fun clone(): HongCompanyMember {
        return Employee(this)
    }
}

 

실행결과

 

Prototype Registry (optional)

 - 많이, 자주 사용하는 Prototype에 대해서 Catalog를 생성하여, 한 곳에 모아 두자!

 - 단순한 simple factory 라고 보면된다.

class MemberCache {

    private val cache = mutableMapOf<String, HongCompanyMember>()

    init {
        val newRecruits = Employee(
            name = "홍길동",
            gender = "남자",
            age = 28,
            address = "서울시",
            salary = 100
        )

        val newExecutive = Executives(
            name = "김철수",
            gender = "남자",
            age = 50,
            address = "서울시",
            salary = 200
        )
        cache["newRecruits"] = newRecruits
        cache["newExecutive"] = newExecutive
    }

    fun get(type: String) = cache.get(type)?.clone()
}