Flyweight Pattern

한 법무 법인의 시스템 관리자 크리스토펴 매튼 & 파트(Cristopher, Matton & Pharts)는 보고 시스템이 메모리를 많이 소모해서 가비지 컬렉터가 끊임없이 시스템을 몇 초 동안 멈추게 한다는 것을 발견했다. 그것을 고쳐야 한다.

페드로: 저도 전에 이런 문제를 본 적이 있어요.

이브: 무엇이 잘못된 거죠?

페드로: 많은 점(point) 데이터를 사용하는 실시간 차트 프로그램 때문이예요. 메모리를 많이 소모하거든요. 그래서 가비지 컬렉터가 시스템을 멈추게 하는 거고요.

이브: 음, 그럼 무엇을 해야 하죠?

페드로: 달리 할 수 있는 일이 별로 없어요. 캐싱도 별 도움이 안되고…​

이브:잠깐만요!

페드로: 무슨 일이죠?

이브: 점들은 나이 데이터로 이루어져 있네요. 일반적인 나이(예를 들면, 나이 [0, 100])의 경우, 왜 미리 계산해 놓지 않는 거죠?

페드로: 플라이웨이트 패턴을 사용하라는 말씀인가요?

이브: 제 말은 객체를 재사용하자는 겁니다.

class Point {
  int x;
  int y;

  /* some other properties*/

  // precompute 10000 point values at class loading time
  private static Point[][] CACHED;
  static {
    CACHED = new Point[100][];
    for (int i = 0; i < 100; i++) {
      CACHED[i] = new Point[100];
      for (int j = 0; j < 100; j++) {
        CACHED[i][j] = new Point(i, j);
      }
    }
  }

  Point(int x, int y) {
    this.x = x;
    this.y = y;
  }

  static Point makePoint(int x, int y) {
    if (x >= 0 && x < 100 &&
        y >= 0 && y < 100) {
      return CACHED[x][y];
    } else {
      return new Point(x, y);
    }
  }
}

페드로: 이 패턴의 경우에는 두 가지가 필요해요. 프로그램 시작 시점에 대부분의 점 데이터를 미리 계산하는 것과, 생성자 대신에 정적 팩토리 메소드를 이용해서 캐쉬된 객체를 반환하는 것이죠.

이브: 테스트해 보았나요?

페드로: 물론이죠. 시스템은 아주 정확히 동작했습니다.

이브: 좋아요, 다음은 클로저 버전이예요.

(defn make-point [x y]
  [x y {:some "Important Properties"}])

(def CACHE
  (let [cache-keys (for [i (range 100) j (range 100)] [i j])]
      (zipmap cache-keys (map #(apply make-point %) cache-keys))))

(defn make-point-cached [x y]
  (let [result (get CACHE [x y])]
    (if result
      result
      (make-point x y))))

이브: 이차원 배열 대신에, [x y] 좌표를 키로 갖는 일차원 맵을 만듭니다.

페드로: 자바의 경우와 마찬가지네요.

이브; 아니예요, 훨씬 더 유연해요. 세 개의 점이나 정수가 아닌 값을 캐시해야 할 필요가 있을 때에는 이차원 배열을 사용할 수 없으니까요.

페드로: 아, 이해했어요.

이브: 더 좋은 것은, 클로저에서는 memoize 함수를 이용하면 팩토리 함수 make-point를 호출한 결과를 캐싱할 수 있어요.

(def make-point-memoize (memoize make-point))

이브: 그래서 최초의 호출할 때를 제외하고는, 같은 인자를 사용해 함수를 호출하게 되면 캐시에 저장된 값을 반환해요.

페드로: 그것 참 멋진 기능이네요!

이브: 물론이죠. 하지만, 부수 효과(side effects)를 가진 함수인 경우에는 memoize 기능을 사용하지 않는 것이 좋다는 점도 기억해 두세요.


  • open/flyweight-pattern.txt
  • 마지막으로 수정됨: 2021/11/21 13:09
  • 저자 127.0.0.1