본문 바로가기
SW Engineering/Design Patterns

(Design Pattern) 1. 생성 패턴

by jaesungLeee 2021. 9. 19.

출처 : https://korecm.github.io/DesignPatternIntro

 

1. GoF 디자인 패턴

GoF 디자인 패턴은 소프트웨어 공학에서 가장 많이 사용되는 디자인 패턴이다.

유형에 따라 생성 패턴 (Creational Pattern), 구조 패턴 (Structural Pattern), 행위 패턴 (Behavioral Pattern)으로 나뉘어 진다.

또 이 패턴들을 범위에 따라 분류할 수도 있는데, 각 패턴을 주로 클래스에 적용하는지, 객체에 적용하는지 구분한다. 클래스 패턴은 클래스와 서브 클래스 간의 관련성에 대해 다룬다. 주로, 상속을 통해 관련되며 컴파일 타임에 결정되는 정적인 성격을 지닌다. 객체 패턴은 각 객체 간의 관련성을 다루고, 런타임 내에 변경되는 동적인 성격을 지닌다.

아래 그림은 각 패턴들의 종류이다.

출처 : https://realzero0.github.io/study/2017/06/12/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%95%EB%A6%AC.html


2. 생성 패턴 (Creational Pattern)

객체를 생성하는데 관련된 패턴들이다. 또한, 참조 과정을 캡슐화하여 객체가 생성되거나 변경되어도 프로그램의 구조에 영향을 받지 않도록 하여 프로그램에 유연성을 더해준다.

생성 클래스 패턴은 객체를 생성하는 일부를 서브클래스에서 담당하도록 하고, 생성 객체 패턴은 객체 생성을 다른 객체에게 위임한다.

 

2-1. 추상 팩토리 (Abstract Factory)

구체적인 클래스에 의존하지 않고 Interface를 통해 서로 연관 및 의존하는 객체들의 그룹으로 생성하여 추상적으로 표현한다. 연관된 서브 클래스를 묶어 한 번에 교체하는 것이 가능하다. 

 

예시.)

interface Plant

class OrangePlant : Plant

class ApplePlant : Plant

abstract class PlantFactory {
    abstract fun makePlant(): Plant

    companion object {
        inline fun <reified T : Plant> createFactory(): PlantFactory = when (T::class) {
            OrangePlant::class -> OrangeFactory()
            ApplePlant::class  -> AppleFactory()
            else               -> throw IllegalArgumentException()
        }
    }
}

class AppleFactory : PlantFactory() {
    override fun makePlant(): Plant = ApplePlant()
}

class OrangeFactory : PlantFactory() {
    override fun makePlant(): Plant = OrangePlant()
}

 

Plant 인터페이스를 구현하는 OrangePlant, ApplePlant 클래스가 있고, 추상 클래스인 PlantFactory가 있다. PlantFactory안에는 추상 메소드로 makePlant( )를 가지며 Inline Function으로 createFactory( )를 갖는다. 이 Inline Function은 Generic으로 OrangePlant 또는 ApplePlant를 받으며 각각 다른 Factory를 리턴하고 있다. 또한 각각의 Factory에서는 makePlant( )를 구현하고 있다.

아래와 같이 사용할 수 있다.

 

val plantFactory = PlantFactory.createFactory<OrangePlant>()
val plant = plantFactory.makePlant()
println("Created plant: $plant")

// Create Plant : OrangePlant@4f023edb

첫 번째 코드라인에서 createFactory의 Generic으로 OrangePlant가 들어가면 OrangeFactory가 리턴된다. 결과적으로 OrangeFactory에서 makePlant( )를 호출하면 OrangePlant( )가 생성된다.

 

2-2. 빌더 (Builder)

동일한 순서로 생성되거나 특정한 알고리즘을 사용하여 생성되어야 하는 복잡한 객체를 만드는 데 사용한다. 객체의 생성 과정과 표현 방법을 분리하고 있어, 동일한 객체 생성에서도 서로 다른 결과를 만들어 낼 수 있는 것이 특징이다.

 

2-3. 팩토리 메소드 (Factory Method)

객체 생성을 서브 클래스에서 처리하도록 분리하여 캡슐화한 패턴이다. 즉, 상위 클래스에서는 인터페이스만 정의하고 실제 생성은 서브 클래스가 담당한다. 

 

예시.)

enum class Country {
    UnitedStates, Spain, UK, Greece
}

interface Currency {
    val code: String
}

class Euro(override val code: String = "EUR") : Currency
class UnitedStatesDollar(override val code: String = "USD") : Currency

class CurrencyFactory {
    fun currencyForCountry(country: Country): Currency? {
        return when (country) {
            Country.Spain, Country.Greece -> Euro()
            Country.UnitedStates          -> UnitedStatesDollar()
            else                          -> null
        }
    }
}

 

Currency 인터페이스는 화폐 종류를 결정하는 code를 가지고 있다. 아래 EuroUnitedStatesDollar Class에서는 인터페이스에서 정의된 code를 override 하고 있다. CurrentFactory에서는 currencyForCountry( ) Method를 통해 들어오는 country parameter에 따라 각각 다른 화폐 Class를 리턴한다.

아래와 같이 사용할 수 있다.

 

val noCurrencyCode = "No Currency Code Available"

val greeceCode = CurrencyFactory().currencyForCountry(Country.Greece)?.code ?: noCurrencyCode
println("Greece currency: $greeceCode")

val usCode = CurrencyFactory().currencyForCountry(Country.UnitedStates)?.code ?: noCurrencyCode
println("US currency: $usCode")

val ukCode = CurrencyFactory().currencyForCountry(Country.UK)?.code ?: noCurrencyCode
println("UK currency: $ukCode")

// Greece currency: EUR
// US currency: USD
// UK currency: No Currency Code Available

 

각각의 Code 변수에서 Elvis-Expression을 이용하여 변수를 선언하고 출력하는 형식이다. 각각의 변수는 Null일 경우 noCurrencyCode를 출력한다.

 

2-4. 프로토타입 (Prototype)

원본 객체를 복제하는 방법으로 객체를 생성하는 패턴이다. 일반적인 방법으로 객체를 생성하며 비용이 큰 경우 주로 사용되는 패턴이다.

 

2-5. 싱글톤 (Singleton) 

가장 유명한 생성 패턴이다. 싱글톤 패턴은 특정 클래스가 하나의 객체만 생성하도록 보장하며 하나의 객체를 생성하면 생성된 객체는 어디서든 참조될 수 있지만, 여러 프로세스에서 동시에 참조할 수 없다. 따라서, 클래스 내에서 인스턴스가 하나뿐임을 보장하며, 불필요한 메모리 낭비를 최소화할 수 있다. 

 

예시.)

object PrinterDriver {
    init {
        println("Initializing with object: $this")
    }

    fun print() = println("Printing with object: $this")
}

 

Kotlin에서는 Object-Keyword를 이용하여 싱글톤을 생성할 수 있다. Kotlin에서 싱글톤 생성에 대한 설명은 다음 포스트에서 자세히 다룬다.

아래와 같이 사용할 수 있다.

 

println("Start")
PrinterDriver.print()
PrinterDriver.print()

// Start
// Initializing with object: PrinterDriver@6ff3c5b5
// Printing with object: PrinterDriver@6ff3c5b5
// Printing with object: PrinterDriver@6ff3c5b5

 

PrintDriver 싱글톤 인스턴스의 print( ) Method를 실행하면 항상 같은 인스턴스 데이터를 출력하는 것을 확인할 수 있다.


References

https://realzero0.github.io/study/2017/06/12/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%95%EB%A6%AC.html

 

디자인 패턴 정리

안녕하세요~ Jay의 블로그입니다.

realzero0.github.io

https://4z7l.github.io/2020/12/25/design_pattern_GoF.html

 

[Design Pattern] GoF(Gang of Four) 디자인 패턴 - HERSTORY

디자인 패턴이란 디자인 패턴은 모듈의 세분화된 역할이나 모듈들 간의 인터페이스 구현 방식을 설계할때 참조할 수 있는 전형적인 해결 방식을 말한다. 디자인 패턴을 통해 설계 문제, 해결 방

4z7l.github.io

https://github.com/dbacinski/Design-Patterns-In-Kotlin#creational

 

GitHub - dbacinski/Design-Patterns-In-Kotlin: Design Patterns implemented in Kotlin

Design Patterns implemented in Kotlin. Contribute to dbacinski/Design-Patterns-In-Kotlin development by creating an account on GitHub.

github.com