1. 플라이웨이트 패턴이란
1.1 정의와 개념
플라이웨이트 패턴은 구조적 디자인 패턴으로, 주된 목적은 공유된 객체의 수를 최소화하여 메모리를 절약하고 성능을 향상시키는 것입니다. 플라이웨이트 패턴은 동일하거나 유사한 객체를 공유함으로써 객체의 생성과 사용을 줄이고 성능을 최적화하는 것을 특징으로 합니다.
1.2 다른 디자인 패턴과의 차이점
다른 디자인 패턴과 비교했을 때, 플라이웨이트 패턴은 주로 객체의 공유와 재사용에 초점을 맞춥니다. 이는 객체를 공유 가능한 내부 상태와 공유 불가능한 외부 상태로 분리합니다. 내부 상태를 공유함으로써 객체의 생성과 메모리 사용을 줄여 시스템의 효율성을 높입니다.
2. 플라이웨이트 패턴의 특성과 장점
플라이웨이트 패턴의 주요 특성과 장점은 다음과 같습니다:
- 메모리 사용의 최소화: 동일하거나 유사한 객체를 공유함으로써 메모리 사용을 줄입니다.
- 성능 향상: 객체의 생성과 소멸을 줄여 시스템의 작동을 가속화합니다.
- 다수의 세밀한 객체 지원: 과도한 메모리 공간을 차지하지 않고 다수의 세밀한 객체를 생성할 수 있습니다.
- 간소화된 시스템 구조: 객체의 내부와 외부 상태를 분리함으로써 시스템의 구조와 복잡성을 단순화합니다.
3. 플라이웨이트 패턴의 실무 응용 예시
플라이웨이트 패턴은 다음과 같은 상황에 적용될 수 있습니다:
- 게임의 입자 객체: 각 입자 객체의 속성을 내부 상태와 외부 상태로 분리하고, 같은 속성을 가진 입자 객체를 공유합니다.
- 네트워크 서버의 연결 객체: 연결 객체의 속성을 내부 상태와 외부 상태로 분리하고, 기존 연결 객체를 재활용하여 새로운 객체를 생성하기 전에 기존 객체를 재사용합니다.
4. 고랭(Golang)에서의 플라이웨이트 패턴 구현
4.1 UML 클래스 다이어그램
고랭(Golang)에서의 플라이웨이트 패턴의 UML 클래스 다이어그램은 다음과 같습니다:
4.2 예시 소개
이 예시에서는 플라이웨이트 패턴을 기반으로 한 그래픽 편집기를 생성하며, 각기 다른 색상의 원을 포함하고 동일한 색상의 원을 공유함으로써 메모리 사용을 줄입니다.
4.3 구현 단계
4.3.1 플라이웨이트 인터페이스와 구체 플라이웨이트 클래스 생성
먼저, 공유 객체의 작업을 정의하기 위해 Flyweight
인터페이스를 생성하고, ConcreteFlyweight
클래스를 구현하여 내부 상태를 포함하도록 합니다.
// Flyweight는 플라이웨이트 객체의 인터페이스를 정의합니다.
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight는 플라이웨이트 인터페이스를 구현한 구체적인 플라이웨이트 객체를 나타냅니다.
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation은 공유 객체의 작업 메서드를 구현합니다.
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("구체적인 플라이웨이트 객체, 내부 상태: %s, 외부 상태: %s\n", f.intrinsicState, extrinsicState)
}
4.3.2 플라이웨이트 팩토리 클래스 생성
다음으로, FlyweightFactory
클래스를 생성하여 플라이웨이트 객체를 관리하고 공유합니다. 이 팩토리 클래스는 생성된 플라이웨이트 객체를 저장하기 위한 flyweights
딕셔너리를 유지합니다.
// FlyweightFactory 클래스
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight은 팩토리로부터 플라이웨이트 객체를 검색하거나 생성합니다.
func (f *FlyweightFactory) GetFlyweight(key string) Flyweight {
if fw, ok := f.flyweights[key]; ok {
return fw
}
flyweight := &ConcreteFlyweight{
intrinsicState: key,
}
f.flyweights[key] = flyweight
return flyweight
}
4.3.3 클라이언트 호출 예시
마지막으로, 그래픽 편집기를 구현하기 위해 플라이웨이트 패턴을 사용하는 방법을 보여주기 위해 Client
클래스를 생성할 수 있습니다.
// Client 클래스
type Client struct {
flyweight Flyweight
}
// Operation은 작업을 수행합니다.
func (c *Client) Operation() {
c.flyweight.Operation("외부 상태")
}
4.4 구현 고려사항과 모범 사례
4.4.1 상태 공유와 스레드 안전성
플라이급 패턴을 사용할 때는 내부 상태의 공유와 스레드 안전성에 주의해야 합니다. 플라이급 객체는 여러 클라이언트에서 공유되기 때문에 내부 상태의 일관성을 보장해야 합니다.
4.4.2 객체 풀 관리
플라이급 객체를 더 잘 관리하고 재사용하기 위해 객체 풀을 사용하여 생성된 플라이급 객체를 저장할 수 있습니다. 객체 풀은 객체의 재사용률을 높이고 객체 생성 및 소멸의 오버헤드를 줄일 수 있습니다.
4.4.3 객체 상태의 외부 관리
플라이급 패턴은 객체의 내부 상태와 외부 상태를 분리하지만 외부 상태는 클라이언트가 관리해야 합니다. 플라이급 객체를 사용할 때 클라이언트는 외부 상태를 플라이급 객체로 전달하여 작업해야 합니다.
완전한 코드 예제
아래는 완전한 Golang 코드 예제입니다:
package main
import "fmt"
// Flyweight는 플라이급 객체의 인터페이스를 정의합니다.
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight는 특정 플라이급 객체를 나타내며 Flyweight 인터페이스를 구현합니다.
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation은 공유 객체의 작업 메서드를 구현합니다.
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("구체적인 플라이급 객체, 내부 상태: %s, 외부 상태: %s\n", f.intrinsicState, extrinsicState)
}
// FlyweightFactory 클래스
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight은 공장에서 플라이급 객체를 검색하거나 생성합니다.
func (f *FlyweightFactory) GetFlyweight(key string) Flyweight {
if fw, ok := f.flyweights[key]; ok {
return fw
}
flyweight := &ConcreteFlyweight{
intrinsicState: key,
}
f.flyweights[key] = flyweight
return flyweight
}
// 클라이언트 클래스
type Client struct {
flyweight Flyweight
}
// Operation은 작업을 수행합니다.
func (c *Client) Operation() {
c.flyweight.Operation("외부 상태")
}
func main() {
factory := &FlyweightFactory{
flyweights: make(map[string]Flyweight),
}
flyweight1 := factory.GetFlyweight("A")
flyweight1.Operation("외부 상태 1")
flyweight2 := factory.GetFlyweight("B")
flyweight2.Operation("외부 상태 2")
client := &Client{
flyweight: factory.GetFlyweight("A"),
}
client.Operation()
}