# 플라이웨이트 (Flyweight) 패턴

# 플라이웨이트 패턴이란 무엇인가요?

플라이웨이트 패턴(Flyweight pattern)는 동일하거나 유사한 객체들 사이에 가능한 많은 데이터를 서로 공유하여 사용하도록 하여 메모리 사용량을 최소화하는 소프트웨어 디자인 패턴이다.

  • 애플리케이션에서 많은 인스턴스를 만들어 메모리 사용이 많아지게 되면 메모리 부족 현상이 발생한다.
  • 따라서 자주 변하지 않는 속성을 모아서 해당 패턴을 적용하기에 적합하다.

image

  • Flyweight
    • 공유에 사용할 클래스 (또는 API)
  • FlyweightFactory
    • Flyweight 인스턴스를 생성 또는 공유 (공장 역할)
  • Client
    • Flyweight 인스턴스를 필요로 하는 클라이언트

# 어떤 상황에서 사용해야 할까요?

  • 공통적인 인스턴스를 많이 생성하는 로직이 포함된 경우
  • 자주 변하지 않는 속성을 재사용할 수 있는 경우
public class Character {
    private char value;
    private String color;
    private String fontFamily;
    private int fontSize;

    public Character(char value, String color, String fontFamily, int fontSize) {
        this.value = value;
        this.color = color;
        this.fontFamily = fontFamily;
        this.fontSize = fontSize;
    }
}
  • 한컴에서 글을 작성할 경우, 각 글자 하나마다 문자, 색상, 폰트종류, 사이즈가 필요하다.
public class Client {
    public static void main(String[] args) {
        Character c1 = new Character('h', "white", "Nanum", 12);
        Character c2 = new Character('e', "white", "Nanum", 12);
        Character c3 = new Character('l', "white", "Nanum", 12);
        Character c4 = new Character('l', "white", "Nanum", 12);
        Character c5 = new Character('o', "white", "Nanum", 12);
    }
}
  • 글자 하나마다 Character 인스턴스를 생성하게 되는데 이때 폰트 종류와 사이즈는 변경이 적다.
  • 자주 변경되지 않는 두 속성을 모아 패턴을 적용시킬 필요가 있다.

# 플라이웨이트 패턴 적용

@Getter
public final class Font {
    final String family;
    final int size;

    public Font(String family, int size) {
        this.family = family;
        this.size = size;
    }
}

@Getter
public class Character {
    private char value;
    private String color;
    private Font font;

    public Character(char value, String color, Font font) {
        this.value = value;
        this.color = color;
        this.font = font;
    }
}
import java.util.HashMap;
import java.util.Map;

public class FontFactory {
    private Map<String, Font> cache = new HashMap<>(); // cache

    public Font getFont(String font) {
        if (cache.containsKey(font)) {
            return cache.get(font);
        } else {
            String[] split = font.split(":");
            Font newFont = new Font(split[0], Integer.parseInt(split[1]));
            cache.put(font, newFont);
            return newFont;
        }
    }
}
  • FontFactory의 getFont() 메소드를 통해 Font 생성을 하게 되면, HashMap에 데이터가 있는지 먼저 검사하게 된다
  • 기존에 저장해둔 Font 인스턴스가 있다면, 새로 생성하지 않고 기존 인스턴스를 반환해준다.
public class Client {
    public static void main(String[] args) {
        FontFactory fontFactory = new FontFactory();
        Character c1 = new Character('h', "white", fontFactory.getFont("nanum:12"));
        Character c2 = new Character('e', "white", fontFactory.getFont("nanum:12"));
        Character c3 = new Character('l', "white", fontFactory.getFont("nanum:12"));
    }
}

# 플라이웨이트 패턴의 장단점

장점

  • 애플리케이션에서 사용하는 메모리를 줄일 수 있다.

단점

  • 코드의 복잡도가 증가한다

# 싱글톤 패턴과 무엇이 다른가요?

  • 싱글톤 패턴은 클래스 자체가 오직 1개의 인스턴스만 허용한다.
  • 플라이웨이트 패턴은 싱글톤이 아닌 클래스를 팩토리에서 제어한다.

인스턴스 생성의 제한을 어디서 제어하느냐에 차이이다.

# 플라이웨이트 패턴의 예시는?

  • Integer
Integer i1 = Integer.valueOf(10);
Integer i2=Integer.valueOf(10);
System.out.println(i1 == i2); // true
  • 근데 자주 사용되는 값들에 대해서만 캐싱을 하고, 아닌 것은 캐싱을 하지 않는다고 한다...

# 참고자료

  • https://ko.wikipedia.org/wiki/%ED%94%8C%EB%9D%BC%EC%9D%B4%EC%9B%A8%EC%9D%B4%ED%8A%B8_%ED%8C%A8%ED%84%B4
  • https://dev-youngjun.tistory.com/217