플라이웨이트 패턴

Groot·2023년 12월 10일
0

TIL

목록 보기
144/148
post-thumbnail

플라이웨이트 패턴

  • 공유를 통해 많은 수의 fine-grained(쪼개진 것) 객체들을 효과적으로 지원.
  • 이 패턴에서 중요한 개념은 본질적인(intrincsic), 부가적인(extrinsic) 상태
    • 본질적인 상태: 객체의 상수 데이터, 공유되는 상태
    • 부가적인 상태: 다른 객체들에 의해 '외부에서' 변경되는 객체의 나머지 상태, 가변적인 상태
  • 플라이웨이트 패턴의 유일한 목적은 메모리 사용을 최소화
    • RAM 부족으로 문제를 겪지 않는다면 이 패턴을 무시할 수 있다.

활용성

  • 대량의 객체를 사용해야 할 때
  • 객체의 수가 너무 많아져 저장 비용이 너무 높아질 때
  • 대부분의 객체 상태를 부가적인 것으로 만들 수 있을 때

구조

요소

  • Flyweight
    • Flyweight가 받아들일 수 있고, 부가적 상태에서 동작해야 하는 인터페이스 선언
  • ConcreteFlyweight
    • Flyweight 인터페이스 구현하고 내부적으로 갖고 있어야 하는 본질적 상태에 대한 저장소
  • UnshaerdConcreteFlyweight
    • 모든 플라이급 객체들이 공유될 필요는 없기 때문에 따로 분리할 수 있다. (필수요소는 아닌듯)
  • FlyweightFactory
    • 플라이급 객체를 생성하고 관리하며, 플라이급 객체가 제대로 공유되도록 보장한다.
    • 사용자가 플라이급 객체를 요청하면, 팩토리는 인스턴스를 제공.
  • Client
    • 플라이급 객체에 대한 참조자를 관리
    • 플라이급 객체의 부가적 상태를 저장

협력 방법

  • 플라이급 객체가 기능을 수행하는 데 필요한 상태가 본질적인 것인지 부가적인 것인지 구분
  • 본질적 상태는 ConcreteFlyweight에 저장
  • 부가적인 상태는 사용자가 저장하거나, 연산되어야 하는 다른 상태로 관리
  • 사용자는 연산을 호출할 때 자신에게만 필요한 부가적 상태를 플라이급 객체에 매개변수로 전달.
    • 사용자는 ConcreteFlyweight 객체를 FlyweightFactory 객체에서 얻어야 함.
    • 그래야 플라이급 객체가 공유될 수 있다.

장점

  • 공유해야 하는 인스턴스의 전체 수를 줄일 수 있다.
    • 메모리를 줄인다.
  • 객체별 본질적 상태의 양을 줄일 수 있다.
  • 부가적인 상태는 연산되거나 저장될 수 있다.

단점

  • 플라이웨이트 메서드를 호출할 때마다 데이터의 일부를 다시 계산해야 한다면 CPU가 낭비될 수 있다.
  • 코드가 복잡해짐.

예시 코드

// Flyweight
protocol GrootFlyweight {
    func iAmGroot(_ numberOfYellowBirds: Int)
}

// ConcreteFlyweight
class GrootConcreteFlyweight: GrootFlyweight {
    private let hairColor: HairColor
    
    init(hairColor: HairColor) {
        self.hairColor = hairColor
    }
    
    func iAmGroot(_ numberOfYellowBirds: Int) {
        print("I Am \(numberOfYellowBirds) birds \(hairColor) Groot!")
    }
}

enum HairColor: String {
    case red
    case blue
    case yellow
    case green
}

// 그루트 객체를 생성하고 관리하는 팩토리 클래스
class GrootFlyweightFactory {
    private var grootPool: [HairColor: GrootFlyweight] = [:]

    func groot(hairColor: HairColor) -> GrootConcreteFlyweight {
        let key = hairColor
        
        if let existingGroot = grootPool[key] {
            return existingGroot as! GrootConcreteFlyweight
        } else {
            let newGroot: GrootFlyweight = GrootConcreteFlyweight(
                hairColor: hairColor
            )
            grootPool[key] = newGroot
            return newGroot as! GrootConcreteFlyweight
        }
    }
    
    func element() {
        print(grootPool)
    }
}

// 그루트 팩토리를 이용하여 그루트 객체 생성
let grootFactory = GrootFlyweightFactory()

// 그루트 객체 생성
var groot1 = grootFactory.groot(hairColor: .green)
var groot2 = grootFactory.groot(hairColor: .blue)
var groot3 = grootFactory.groot(hairColor: .yellow)
var groot4 = grootFactory.groot(hairColor: .green) // 이미 존재하는 그루트 객체를 참조

// 생성된 그루트 객체의 헤어 색깔과 노란 새 개수 출력
groot1.iAmGroot(3)
groot2.iAmGroot(5)
groot3.iAmGroot(6)
groot4.iAmGroot(5)

// 객체 확인
grootFactory.element()

참고

profile
I Am Groot

0개의 댓글