[디자인 패턴] 플라이웨이트(Flyweight) 패턴

❓플라이웨이트 패턴?

플라이웨이트는 각 객체의 공통된 부분들을 공유하여 사용할 수 있도록 하는 디자인 패턴입니다. 이름의 뜻처럼, fly(가볍다) weigth(무게) 즉 무게를 가볍게 하는 것으로, 공유를 통해 자원을 효율적으로 사용할 수 있습니다!

여기서 자원은 메모리를 말하는데요, 여러 객체가 동일한 데이터를 가지고 있을 때 이를 같은 참조 값으로 공유함으로써 메모리를 줄일 수 있게 되는 것이죠!

❗플라이웨이트 패턴의 예시 (JS)

// 플라이웨이트 패턴을 구현할 플라이웨이트 객체 (공유 객체)
class TextStyle {
  constructor(font, size, color) {
    this.font = font;
    this.size = size;
    this.color = color;
  }
}

// 텍스트 객체, 공유할 수 있도록 플라이웨이트를 참조
class Text {
  constructor(content, textStyle) {
    this.content = content;
    this.textStyle = textStyle;
  }

  // 텍스트를 표시하는 메서드
  display() {
    console.log("content:", this.content);
		console.log("textStype:", this.textStyle);
  }
}

const sharedStyle = new TextStyle("Arial", 12, "Black");

const text1 = new Text("Hello, World!", sharedStyle);
const text2 = new Text("This is World, Hello!", sharedStyle);

✨플라이웨이트의 불변성

플라이웨이트는 여러 객체들이 참조하기 때문에, 불변 상태를 유지해야 합니다. 즉 상태를 한 번만 초기회 해야 합니다. setter, public 필드 들을 다른 객체에 노출해서는 안됩니다.

✨플라이웨이트 팩토리

플라이웨이트 팩토리를 만들어 효율적으로 관리할 수 있습니다.

위 예제의 플라이웨이트 패턴에 팩토리를 적용해 보겠습니다.

// 플라이웨이트 패턴을 구현할 플라이웨이트 객체 (공유 객체)
class TextStyle {
  constructor(font, size, color) {
    this.font = font;
    this.size = size;
    this.color = color;
  }
}

// 플라이웨이트 팩토리
class TextStyleFactory {
  constructor() {
    this.styles = {};
  }

  getStyle(font, size, color) {
    const key = `${font}-${size}-${color}`;
    
    if (!this.styles[key]) {
      this.styles[key] = new TextStyle(font, size, color);
    }

    return this.styles[key];
  }
}

// 텍스트 객체, 공유할 수 있도록 플라이웨이트를 참조
class Text {
  constructor(content, textStyle) {
    this.content = content;
    this.textStyle = textStyle;
  }

  // 텍스트를 표시하는 메서드
  display() {
    console.log("content:", this.content);
		console.log("textStype:", this.textStyle);
  }
}

const sharedStyle = new TextStyle("Arial", 12, "Black");

const text1 = new Text("Hello, World!", sharedStyle);
const text2 = new Text("This is World, Hello!", sharedStyle);

font, size, color 값을 받아, 참조 값이 존재하지 않을 때만 새로 공유 객체를 생성하고, 존재 한다면 기존의 메모리에서 값을 참조하여 사용하게 됩니다.

이처럼 팩토리를 만들어 효율적으로 메모리를 관리할 수 있게 됩니다.

❗플라이웨이트 패턴의 장점과 단점

플라이웨이트는 공유 객체를 가짐으로써 메모리를 아낄 수 있다는 장점이 있습니다. 공유를 통해 수많은 유사 객체를 생성하여도 큰 자원 없이 생성할 수 있다는 것이죠.

하지만 공유 객체를 가진다는 것은 굉장히 위험합니다. 의존하는 객체가 많아질 수록, 사이드 이펙트에 굉장히 취약하기 때문입니다. 그러므로 불변성에 항상 신경을 쓰며, 신중하게 사용하여야 합니다!

❗결론!

이 패턴은 굉장히 유용하게 잘 쓰일 수 있을 것 같습니다. 리액트에서 사용되는 Redux, Atom과 같은상태 관리 라이브러리 역시 이와 같은 패턴에서 만들어진게 아닌가 생각이 들었습니다. 이처럼 유용하게 사용되는 패턴인 만큼 익숙해질 필요가 있을 것 같습니다!