Flyweight

DongHeon·2022년 12월 5일
0

디자인 패턴

목록 보기
10/12

오늘은 Flyweight 패턴에 대해 알아보겠습니다.

제가 공부하면서 느낀 점은 캐싱과 비슷하다였습니다. 필요한 인스턴스를 만들어 저장해 재사용하는 느낌이었습니다.

Flyweight?

Flyweight 패턴은 자주 변하는 속성과 변하지 않은 속성을 분리하고 재사용하여 메모리 사용을 줄일 수 있습니다.
(변하지 않는 속성을 재사용합니다.)

여기서 주의할 점은 변하는 속성과 변하지 않는 속성은 개발자 주관적이기 때문에 잘 분석하고 선택해야 합니다.

Flyweight을 설계할 때 주의할 점은 Flyweight 객체는 immutable 해야 합니다.

코드

우리가 만약 메모장을 앱을 만든다면 무수히 많은 Text 객체를 만들어야 할 수 있습니다. Text객체에는 text, font, fontColor, size 프로퍼티를 가지고 있습니다.

패턴 적용 전

  • Text
class Text {
    var text: String
    var font: String
    var fontColor: String
    var size: Int

    init(text: String, font: String, fontColor: String, size: Int) {
        self.text = text
        self.font = font
        self.fontColor = fontColor
        self.size = size
    }
}
  • Client
class App {
    func makeText() {
        let text1 = Text(text: "안녕", font: "유성매직", fontColor: "black", size: 13)
        let text2 = Text(text: "hello", font: "궁서체", fontColor: "black", size: 15)
    }
}

만약 동일한 font와 size를 가지는 text를 만들고 싶어도 재사용이 불가능해 하나하나 인스턴스를 생성해야 합니다.

패턴 적용

  • Text (변하는 속성)
class Text {
    var text: String
    var fontColor: String
    var flyweight: Flyweight
    
    init(text: String, fontColor: String, flyweight: Flyweight) {
        self.text = text
        self.fontColor = fontColor
        self.flyweight = flyweight
    }
}
  • Flyweight (변하지 않는 속성)
final class Flyweight {
    private var font: String
    private var size: Int
    
    init(font: String, size: Int) {
        self.font = font
        self.size = size
    }
    
    func getFont() -> String {
        return self.font
    }
    
    func getFontSize() -> Int {
        return self.size
    }
}

Flyweight는 불변해야 하기 때문에 final 키워드를 이용해 상속을 못 하게 막아주고 각각 프로퍼티에는 Private 처리해 주었습니다.

  • FlyweightFactory
class FlyweightFactory {
    var cache = [String: Flyweight]()
    
    func getFlyweight(name: String) -> Flyweight {
        let font = name.split(separator: ":").map { String($0) }
        
        if let flyweight = cache[font[0]] {
            return flyweight
        } else {
            let newFlyweight = Flyweight(font: name, size: Int(font[1])!)
            cache.updateValue(newFlyweight, forKey: font[0])
            return newFlyweight
        }
    }
}

생성한 Flyweight를 재사용하기 위한 객체입니다.

현재 저는 Dictionary 형태로 구현했지만 구현 방법은 자유입니다. 만약 더 이상 사용하지 않는 Flyweight 객체를 제거하는 방법도 고려할 수 있을 것 같습니다.

  • Client
class MyApp {
    private let flyweightFactory: FlyweightFactory
    
    init(flyweightFactory: FlyweightFactory) {
        self.flyweightFactory = flyweightFactory
    }
    
    func makeText() {
        let text1 = Text(text: "안녕", fontColor: "black", flyweight: flyweightFactory.getFlyweight(name: "유성매직:13"))
        let text2 = Text(text: "hello", fontColor: "white", flyweight: flyweightFactory.getFlyweight(name: "궁서체:13"))
        let text3 = Text(text: "hi", fontColor: "red", flyweight: flyweightFactory.getFlyweight(name: "유성매직:13"))
    }
}

text3 같은 경우 다른 객체를 생성하지 않고 Factory에 저장된 객체를 재사용합니다.

장단점

  • 장점
    1. App에서 사용하는 메모리를 줄일 수 있습니다.

  • 단점
    1. 코드가 복잡합니다.

해당 글은 인프런의 코딩으로 학습하는 GoF 디자인 패턴 강의를 참고해 작성했습니다.

⭐️ 부족하거나 잘못된 부분이 있다면 댓글은 언제나 환영입니다!! ⭐️

0개의 댓글