플라이웨이트 패턴

이정석·2023년 6월 20일
0

디자인패턴

목록 보기
12/23

플라이웨이트 패턴이란?

공유될 수 있는것과 공유될 수 없는 것을 구분지어 공유될 수 있는 자원을 가지는 객체를 하나만 생성해 전체 프로그램의 메모리 사용량을 줄일 수 있는 패턴


문제상황

1. 총게임의 총알을 나타내는 Bullet클래스가 있다

class Bullet {
    private String type;
    private int damage;
    private String position;

    public Bullet(String type, int damage, String position) {
        this.type = type;
        this.damage = damage;
        this.position = position;
    }
}

2. 게임중 거의 동시에 1000발의 총알을 쏜다고 할 때 1000개의 총알을 생성해야 한다

public class Game {
    public static void main(String[] args) {
        Bullet[] bullets = new Bullet[1000];
        for (int i = 0; i < bullets.length; i++) {
            bullets[i] = new Bullet("Standard Bullet", 50, "Position " + i);
        }
    }
}

위 상황에서 총알은 type, damage, position이라는 변수를 가지고 있지만 실제 이미지를 나타내는 image 데이터는 용량이 다른 변수에 비해 용량이 클 것이다. 이런 총알을 1000개가 존재한다면 이미지가 100KB라고 해도 100,000KB=약 100MB이고 더 많아진다면 메모리를 많이 잡아먹을 것이다.

총알의 개별속성인 position을 제외하고 공통부분으로 묶을 수는 없을까?


구조

  1. Flyweight: Flyweight 클래스가 구현해야 할 인터페이스를 정의한 클래스로 플라이웨이트의 메소드에 해당 객체가 사용할 외부상태를 매개변수로 넘겨주어야 한다.
  2. ConcreteFlyweight: 공유가능한 내부 상태를 가지는 Flyweight 클래스이다. Flyweight 클래스를 상속받아 연산을 구현해야 한다.
  3. UnsharedConcreteFlyweight: 공유되지 않는(위 상황에서 position) 내부 상태를 가지는 Flyweight 클래스로 UnsharedConcreteFlyweight의 인스턴스를 공유되지 않는다.
  4. FlyweightFactory: 플라이웨이트의 가장 중요한 점중 하나는 ConcreteFlyweight 인스턴스의 수를 하나만 유지하는 것이다. 싱글톤 패턴을 사용할 수 있지만 클라이언트는 어떤 ConcreteFlyweight를 생성할지 모르기 때문에 팩토리를 만들어 생성하는 책임을 위임하도록 한다.

코드(JAVA)

1. Bullet

class Bullet {
    private String type;
    private int damage;

    public Bullet(String type, int damage) {
        this.type = type;
        this.damage = damage;
    }
}

플라이웨이트 패턴의 ConcreteFlyweight에 해당하는 부분으로 총알의 공통 속성인 type과 damage를 가지고 있다.

2. BulletContext

class BulletContext {
    private Bullet bullet;
    private String position;

    public BulletContext(Bullet bullet, String position) {
        this.bullet = bullet;
        this.position = position;
    }
}

플라이웨이트 패턴의 UnsharedConcreteFlyweight에 해당하는 부분으로 날아가는 총알의 공유할 수 없는 속성인 position을 가지고 있고 공통 속성을 저장한 Bullet 클래스를 가지고 있다.

3. BulletFactory

class BulletFactory {
    private Map<String, Bullet> bulletPool = new HashMap<>();

    public Bullet getBullet(String type, int damage) {
        String key = type + damage;
        if (!bulletPool.containsKey(key)) {
            bulletPool.put(key, new Bullet(type, damage));
        }
        return bulletPool.get(key);
    }
}

ConcreteFlyweight를 생성하는 팩토리 클래스로 Bullet 인스턴스를 하나만 생성하도록 조절한다. 위 클래스는 HashMap을 사용하는 방식이며 List를 사용하는 방법도 사용가능 하지만 Index에 해당하는 값을 넘겨주어야 한다.

4. Game

public class Game {
    public static void main(String[] args) {
        BulletFactory factory = new BulletFactory();
        BulletContext[] bulletContexts = new BulletContext[1000];
        for (int i = 0; i < bulletContexts.length; i++) {
            Bullet bullet = factory.getBullet("Standard Bullet", 50);
            bulletContexts[i] = new BulletContext(bullet, "Position " + i);
        }
    }
}

문제상황과 같이 1000개의 총알을 생성하지만 필요한 메모리는 크게 줄을 것이다. 이미지파일의 크기를 100KB라고 한다면 패턴을 적용하면 1000개의 총알에 대한 이미지파일의 용량은 하나의 인스턴스만 생성되기 때문에 100KB가 된다.


플라이웨이트 패턴은 언제 사용할까?

플라이웨이트 패턴을 적용했을 때 가장 큰 이점은 역시 필요 메모리 축소이다. 그렇다면, 필요 메모리가 많은 상황에 사용하면 된다.

  • 프로그램이 많은 객체를 필요로 할 때
  • 객체 사용이 많이 저장 공간에 대한 부담이 클 때
  • Extrinsic state를 제외하면 소수의 공유 객체로 대체될 수 있을 때
  • 프로그램이 객체들을 서로 구분할 필요가 없을 때

플라이웨이트 패턴의 효율

  • 얼마나 쉽게 공유될 수 없는 상태를 찾아낼 수 있는가?

    Extrinsic state가 많을수록 플라이웨이트 패턴의 효과가 미미하다.

  • Shared object를 관리하는 방법, Client에서 더 이상 사용하지 않는 객체를 소멸시키는 방법

    Reference counting, 유일성 보장을 위한 Singleton pattern을 적용

profile
게임 개발자가 되고 싶은 한 소?년

0개의 댓글