프로그래밍 일반/디자인 패턴

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

지노윈 2020. 1. 28. 22:58
반응형

데이터를 공유하여 메모리를 절약할 수 있게 하는 패턴입니다.

한번 생성된 객체는 또 다시 생성하지 않고 Pool로 관리하여 사용합니다.

 

플라이웨이트는 원래 권투 용어로 몸무게가 낮은 선수들의 매치를 의미합니다.

플라이웨이트 패턴은 많은 수의 가벼운 임시 객체들을 "스마트 참조"로 사용하는 것을 말하며 그러한 객체들을 플라이웨이트라고 부릅니다.

대단히 많은 수의 매우 비슷한 객체들이 사용되어야 할 때 메모리 사용량을 절감하는 방법으로 자주 사용됩니다.

사용자 이름

수 많은 유저의 이름을 저장하는 겨우를 생각해봅시다. 유저의 이름이 많이 겹친다면 이를 일일이 모두 메모리에 저장하면 메모리를 많이 사용할 것입니다.

이름 키값을 이용하여 중복된 이름을 함께 참조한다면 메모리 사용량을 줄일 수 있습니다.

typedef uint32_t key;

struct User
{
  User(const string& first_name, const string& last_name)
    : first_name{add(first_name)}, last_name{add(last_name)}
  {  }

  const string& get_first_name() const
  {
    return names.left.find(last_name)->second;
  }

  const string& get_last_name() const
  {
    return names.left.find(last_name)->second;
  }

  friend ostream& operator<<(ostream& os, const User& obj)
  {
    return os
      << "first_name: " << obj.get_first_name()
      << " last_name: " << obj.get_last_name();
  }

protected:
  static bimap<key, string> names;
  static int seed;

  static key add(const string& s)
  {
    auto it = names.right.find(s);
    if (it == names.right.end())
    {
      key id = ++seed;
      names.insert({seed, s});
      return id;
    }
    return it->second;
  }
  key first_name, last_name;
};

key User::seed = 0;
bimap<key, string> User::names{};

void naive_flyweight()
{
  User user1{ "John", "Kim" };
  User user2{ "Jane", "Kim" };

  cout << user1 << endl;
  cout << user2 << endl;
}

Boost.Flyweight

앞서의 예제는 수작업으로 하나하나 구현하였습니다. boost::flyweight를 사용하면 이러한 수작업을 덜 수 있습니다.

이 타입을 사용하면 좀더 쉽게 저장 공간을 절약 할수 있는 플라이웨이트를 생성할 수 있습니다.

struct User
{
  flyweight<string> first_name, last_name;

  User(const string &first_name, const string &last_name)
    : first_name(first_name),
      last_name(last_name) {}

  const string& get_first_name() const
  {
    return first_name.get();
  }

  const string& get_last_name() const
  {
    return last_name.get();
  }

  friend ostream& operator<<(ostream& os, const User& obj)
  {
    return os
      << "first_name: " << obj.get_first_name()
      << " last_name: " << obj.get_last_name();
  }
};

void boost_flyweight()
{
  User user1{ "John", "Kim" };
  User user2{ "Jane", "Kim" };

  cout << user1 << endl;
  cout << user2 << endl;
}

문자열 플라이웨이트 사례

std::string::substr() 관련하여 플라이웨이트 패턴 구현을 살펴봅시다.

C++에서는 string_view를 통해 플라이웨이트를 제공합니다.

 

텍스트 에디터에서범위를 지정하여 대문자를 바꾸는 경우에 대한 사례를 설명합니다.

원본 텍스트와 같은 크기의 이진 배열을 만들어 두고 대문자로 변환 여부를 플래그로 설정합니다.

범위에 대한 시작/끝 표시만으로 요구 조건을 나타내기에 충분하지만, 텍스트의 모든 문자마다 이진 플래그를 만드는 낭비가 있다.

이러한 구현을 플라이웨이트를 적용한 사례를 설정하고 있습니다.

 

 

[독서 리뷰] - 모던 C++ 디자인 패턴

 

모던 C++ 디자인 패턴

[객체 지향 프로그래밍/디자인 패턴] - 빌더 패턴(Builder Pattern) [분류 전체보기] - 디자인 패턴 [객체 지향 프로그래밍/디자인 패턴] - 팩토리 패턴(Factory pattern) [객체 지향 프로그래밍/디자인 패턴] -..

devjino.tistory.com

코드 참고 : https://cpp-design-patterns.readthedocs.io/en/latest/
 

Welcome to C++ Design Patterns’s documentation! — C++ Design Patterns 0.0.1 documentation

© Copyright 2018, Hans-J. Schmid Revision 786c83f8.

cpp-design-patterns.readthedocs.io