본문 바로가기

설계

[디자인 패턴] 플라이웨이트 패턴 (Flyweight Pattern) - python 예제 코드

플라이웨이트 패턴은 객체를 공유하여 메모리 사용을 줄이는 구조적인 디자인 패턴입니다. 이 패턴은 비용이 큰 객체를 여러 개 생성할 때 유용하며, 객체가 공유 가능한 상태와 고유한 상태를 가지고 있을 때 적용됩니다.

예를 들어, 문자열 처리를 하는 프로그램에서 동일한 문자열이 여러 번 사용된다면, 이를 하나의 객체로 생성하여 공유할 수 있습니다. 이렇게 하면 메모리 사용량이 감소하고, 성능도 향상될 수 있습니다.

 

1. 예제 코드

 

파이썬에서는 플라이웨이트 패턴을 구현하기 위해 클래스의 __new__ 메소드를 오버라이딩하고, 객체가 이미 존재하는 경우에는 이를 반환하도록 구현합니다. 이 때, 공유 가능한 객체는 클래스 변수로 선언하고, 고유한 상태는 인스턴스 변수로 처리합니다.

아래는 파이썬으로 구현한 플라이웨이트 패턴의 예제입니다.

 

class Flyweight:
    _instances = {}

    def __new__(cls, char):
        if char not in cls._instances:
            print(f"Creating new instance for {char}")
            cls._instances[char] = super().__new__(cls)
        return cls._instances[char]

    def set_position(self, row, column):
        self.row = row
        self.column = column

    def display(self):
        print(f"Character at ({self.row}, {self.column})")

characters = ['A', 'B', 'C', 'D', 'A', 'B']
positions = [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]

for char, pos in zip(characters, positions):
    flyweight = Flyweight(char)
    flyweight.set_position(*pos)
    flyweight.display()


위 코드에서는 Flyweight 클래스를 정의하고, _instances 변수를 클래스 변수로 선언합니다. __new__ 메소드를 오버라이딩하여 새로운 객체를 생성하는 것이 아니라, 이미 생성된 객체가 있는 경우 이를 반환하도록 합니다.

그리고, set_position 메소드와 display 메소드를 정의하여 고유한 상태를 관리하고, 결과를 출력합니다.

마지막으로, characters와 positions 리스트를 이용하여 Flyweight 객체를 생성하고, 각각의 객체에 대해 set_position 메소드와 display 메소드를 호출합니다. 이 때, 같은 문자가 여러 번 등장하더라도, 이미 생성된 객체를 반환하기 때문에 메모리 사용량이 감소합니다.

실행 결과는 다음과 같습니다.

 

Creating new instance for A
Character at (0, 0)
Creating new instance for B
Character at (0, 1)

 

2. 개발할 때 주의해야 할 점

1) 공유 가능한 객체를 인스턴스 변수로 사용하지 않기

인스턴스 변수로 공유 가능한 객체를 사용하면, 객체마다 별도의 상태를 가지게 되어 플라이웨이트 패턴의 이점을 잃게 됩니다. 따라서, 공유 가능한 객체는 클래스 변수로 선언하고, 인스턴스 변수는 고유한 상태를 관리하기 위해 사용해야 합니다.

 

2) 객체 생성 및 반환 로직 구현에 주의하기

__new__ 메소드를 오버라이딩하여 객체 생성 및 반환 로직을 구현할 때, 반드시 객체 생성 조건과 객체 반환 조건을 명확하게 구분해야 합니다. 이를 제대로 처리하지 않으면, 원하지 않은 결과가 발생할 수 있습니다

 

3) 멀티스레드 환경에서의 동기화 처리

플라이웨이트 패턴은 객체를 공유하여 메모리 사용량을 줄이는 것이 목적이기 때문에, 멀티스레드 환경에서는 동기화 처리가 필요합니다. 동기화 처리를 하지 않으면, 동시에 여러 스레드에서 같은 객체를 수정하거나 생성하는 경우 충돌이 발생할 수 있습니다. 따라서, 스레드 안전성을 고려하여 코드를 작성해야 합니다.

 

3. 장점

1) 메모리 사용량 감소

플라이웨이트 패턴은 객체를 공유하여 메모리 사용량을 줄입니다. 따라서, 여러 개의 객체를 생성할 필요 없이 하나의 객체를 공유하여 사용할 수 있으므로 메모리 사용량을 감소시킬 수 있습니다.

2) 객체 생성 시간 감소

객체를 생성하는 과정에서 초기화 작업이 필요한 경우, 플라이웨이트 패턴을 적용하면 초기화 작업을 반복하지 않아도 됩니다. 이는 객체 생성 시간을 줄일 수 있습니다.

3) 성능 향상

플라이웨이트 패턴을 적용하면 객체 생성 및 소멸에 따른 오버헤드가 줄어들기 때문에 성능이 향상될 수 있습니다.

 

4. 단점

1) 객체 공유에 따른 제약

객체를 공유하여 사용하므로, 객체의 상태를 변경하면 다른 객체들에게 영향을 미칩니다. 따라서, 공유 객체의 상태를 변경하지 않아야 합니다.

2) 객체 공유의 한계

공유 객체가 너무 많아지면, 객체 간 공유에 따른 오버헤드가 증가하여 오히려 성능이 저하될 수 있습니다. 이 경우에는 플라이웨이트 패턴이 적용되지 않는 경우가 있습니다.

3) 복잡성

플라이웨이트 패턴을 구현하기 위해서는 객체의 내부 상태와 외부 상태를 분리하는 작업이 필요합니다. 이는 구현의 복잡성을 증가시킬 수 있습니다.