로컬에서는 잘 되는데 ☘️

[구조 패턴] 플라이웨이트 패턴

by youngjun._.

1. 플라이웨이트 패턴(Flyweight Pattern)이란? 

객체를 가볍게 만들어 메모리 사용을 줄이는 패턴

공통으로 사용하는 클래스(Flyweight)를 생성하는 팩토리 클래스(FlyweightFactory)를 만들어, 인스턴스를 최초 1개만 생성하고 공유하여 재사용할 수 있도록 하는 구조 패턴이다.

 

자주 변하는 속성(= 외적인 속성, extrinsit)과 변하지 않는 속성(= 내적인 속성, intrinsit)을 분리하고 재사용하여 메모리 사용을 줄인다.

1-1. 구조

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

1-2. 언제 활용될까

  • 공통적인 인스턴스를 많이 생성하는 로직이 포함된 경우
  • 자주 변하지 않는 속성을 재사용할 수 있는 경우

 

2. 코드로 알아보는 플라이웨이트 패턴 필요한 상황

클라이언트 요구사항은 다음과 같다.

1. 편집기에 글을 쓰고 싶어요.
2. 편집기니까 한 글자마다 폰트와 글씨 색, 크기를 바꿀 수 있으면 좋겠어요.

2-1. 편집기에 글을 쓰고 싶어요

글을 쓰게 되면 각 글자 하나마다 문자, 색상, 폰트이름, 폰트사이즈 4개의 속성이 필요하다고 도메인을 정해보자.

해당 속성을 갖고 있는 클래스(Character)를 다음과 같이 작성할 수 있다.

 

Character 클래스

  • 글자 하나를 표현한 도메인
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;
    }
}

 

Client

  • 편집기에 글을 입력하는 클라이언트
  • 글자 하나마다 Character 인스턴스를 생성한다.
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);
    }
}

 

편집기에 글을 쓰면 한글자 한글자 모두 new 생성자를 통해 객체를 생성하게 되는데, 메모리를 아끼기 위해 자주 변하는 속성(공유불가), 자주 변하지 않는 속성(공유)을 분류해보자.
  1. 문자 : 자주 변하는 속성
    1. 은 ENUM 타입으로 선언해주어도 좋을 것 같다.
  2. 글자 폰트크기자주 변하지 않는 속성

 

Font 라는 클래스(Flyweight)로 만들어서 개선해보자.

 

2-2. 플라이웨이트 패턴 적용

Character 인스턴스가 모든 데이터를 따로 저장하지 않고, 공유할 수 있는 부분을 공유해서 메모리를 아껴보자.

 

자주 변하지 않아서 공유할 수 있는 속성으로 분류했던 글자 폰트글자 크기 속성을 Font 클래스로 선언했다.

 

Font

public final class Font {

    final String family;

    final int size;

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

    public String getFamily() {
        return family;
    }

    public int getSize() {
        return size;
    }
}
  • 공유하는 객체이기 때문에 불변으로 선언 (final)
  • 생성자로만 선언 + getter 접근

 

Character

Font를 field로 갖도록 수정했다.
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;
    }
}

 

FontFactory

Font 생성을 관리해주는 팩토리는 다음과 같다.
public class FontFactory {

    private Map<String, Font> cache = new HashMap<>();

    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 인스턴스가 있다면, 새로 생성하지 않고 기존 인스턴스를 반환해준다.

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"));
    }
  • 공유된 Font를 가져올 수 있다.

 

3. 장점과 단점

장점

  • 많은 객체를 만들때 성능을 향상시킬수 있다.
  • 많은 객체를 만들때 메모리를 줄일수 있다.

단점

  • 특정 인스턴스를 다르게 처리하는게 힘들다.

 

4. 싱글톤 패턴이랑 차이가 뭘까

싱글톤 패턴은 클래스 자체가 오직 1개의 인스턴스만 허용

플라이웨이트 패턴은 싱글톤이 아닌 클래스를 팩토리에서 제어

 

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

 

5. Java에서 사용된 플라이웨이트 패턴

도메인이나 로직에서 하나의 객체를 계속 사용될 때 활용되는 경우가 많아서 Java나 Spring 코드에서 많이 찾아볼 수는 없다.

 

5-1. Java의 Integer

-128 ~ 127 까지의 값은 캐싱을 해두고 있기 때문에 == 비교는 이외 데이터에서 다르다고 뜬다.

Integer i1 = Integer.valueOf(12000);
Integer i2 = Integer.valueOf(12000);
System.out.println(i1 == i2); // false
System.out.println(i1.equals(i2)); // true

 

 

임베디드와 같이 메모리를 최소한으로 사용해야하는 경우에 활용될 것 같다. 플라이웨이트 패턴의 내부에서 싱글톤 패턴으로 객체를 생성해서 관리한다는 점을 이해하면 된다.

블로그의 정보

개발하는만두

youngjun._.

활동하기