디자인 패턴 중 Flyweight 패턴에 대해 설명합니다.
설명
메모리 사용량을 줄이기 위해 여러 객체 간 공통적으로 사용하는 상태를 분리하여 공유하는 패턴입니다. 즉, 여러 객체에서 재사용할 수 있는 부분을 캐싱하여 최적화하는 것입니다. 다음과 같은 상황에서 적용하면 유용합니다.
- 애플리케이션이 많은 양의 유사한 객체를 필요로 합니다.
- 그로 인해 메모리 사용량이 높습니다.
- 이 객체들간 중복된 상태가 존재합니다. 이를 분리하여 여러 객체에 공유할 수 있습니다.
Flyweight 패턴은 객체에서 Mutable 데이터와 Immutable 데이터로 나누어 분리하게 됩니다. Mutable 데이터는 Extrinsic state로, Immutable 데이터는 Intrinsic state라고 불립니다.
- Flyweight: repeating_state(Intrinsic state)를 저장하고 있는 객체입니다. 상황에 따라 실질적인 로직을 수행하는 메소드를 정의해도 되고 안 해도 됩니다. 메소드를 정의하는 경우 unique_state(Extrinsic state)를 파라미터로 전달받습니다. 주의할 점은 캐싱될 객체이기 때문에 최초 생성된 후 객체의 변경이 되어서는 안 됩니다.
- FlyweightFactory: Flyweight 객체 생성과 객체 Pool을 관리하는 팩토리 클래스입니다. get_flyweight 팩토리 메소드는 repeating_state(Intrinsic state)를 파라미터로 전달받아 캐싱된 객체들 중 해당 상태를 포함하는 객체를 찾아 반환합니다. 만약 캐싱된 객체가 없다면 새로 객체를 생성하고 캐싱한 후 반환합니다. 객체 식별은 repeating_state(Intrinsic state)로 합니다.
- Context: 공유 자원인 Flyweight를 필드로 참조하고, unique_state(Extrinsic state)를 저장하는 객체입니다. 상황에 따라 실질적인 로직 수행을 Flyweight 클래스에 위임해도 되고 안 해도 됩니다. 위임하는 경우 Flywegith의 메소드를 호출할 때 unique_state(Extrinsic state)를 파라미터로 전달합니다.
- Client: Context를 생성/사용/관리합니다. Context 생성자를 호출할 때 Flyweight를 파라미터로 넘겨줘야 합니다.
다음과 같은 장점이 있습니다.
- 자원을 공유하여 메모리를 아낄 수 있습니다.
적용 예시
Flyweight 패턴을 사용해 숲을 그리는 추상적인 예제입니다.
from typing import List
class Forest:
def __init__(self) -> None:
self.trees: List["Tree"] = []
def plant_tree(self, name: str, color: str, x: int, y: int):
tree_type = TreeTypeFactory.get_tree_type(name, color)
tree = Tree(x, y, tree_type)
self.trees.append(tree)
def draw(self):
for tree in self.trees:
tree.draw()
class Tree:
def __init__(self, x: int, y: int, type: "TreeType") -> None:
self.x = x
self.y = y
self.type = type
def draw(self):
return self.type.draw(self.x, self.y)
class TreeType:
def __init__(self, name: str, color: str) -> None:
self.name = name
self.color = color
self.image = self.get_image(name, color)
def get_image(self, name: str, color: str) -> bytes: ...
def draw(x: int, y: int): ...
# draw image at coordinates
class TreeTypeFactory:
tree_types: List[TreeType] = []
@classmethod
def get_tree_type(self, name: str, color: str) -> TreeType:
for tree_type in self.tree_types:
if tree_type.name == name and tree_type.color == color:
return tree_type
tree_type = TreeType(name, color)
self.tree_types.append(tree_type)
name, color, image 같은 공유자원을 TreeType(Flyweight)로 분리하여 캐싱합니다. 같은 시간대에 많은 양의 Tree 객체를 생성해도 메모리를 절약할 수 있습니다.
참조
'객체 지향 > 디자인 패턴' 카테고리의 다른 글
디자인 패턴 - Proxy (0) | 2023.10.29 |
---|---|
디자인 패턴 - Facade (0) | 2023.10.28 |
디자인 패턴 - Decorator (1) | 2023.10.25 |
디자인 패턴 - Composite (0) | 2023.10.22 |
디자인 패턴 - Bridge (0) | 2023.10.21 |