플라이급 패턴
Flyweight pattern![]() |

컴퓨터 프로그래밍에서 플라이급 소프트웨어 설계 패턴은 자신의 데이터의 일부를 다른 유사한 개체와 공유함으로써 메모리 사용을 최소화하는 개체를 말한다.플라이급 패턴은 잘 알려진 23개의 GoF 디자인 패턴 중 하나이다.[1]이러한 패턴은 구현, 변경, 테스트 및 재사용하기 쉬운 유연한 객체 지향 소프트웨어 설계를 촉진한다.
다른 맥락에서, 데이터 구조를 공유하는 것을 해시 컨센싱이라고 부른다.
이 용어는 WYSIWYG 문서 편집기에서 글리프 정보를 효율적으로 처리하기 위해 1990년[2] 폴 칼더와 마크 린튼에 의해 처음 만들어졌으며, 이 아이디어를 광범위하게 탐구했다.[3]그러나, 비슷한 기법이 1988년 초에 이미 다른 시스템에서도 사용되었다.[4]
개요
플라이웨이트 패턴은 개별 저장 시 많은 양의 메모리를 사용할 수 있는 단순 반복 요소를 사용하여 많은 수의 객체를 처리할 때 유용하다.외부 데이터 구조에서 공유 데이터를 보관했다가 사용할 때 일시적으로 객체에 전달하는 것이 일반적이다.
대표적인 예로 워드프로세서에서 문자를 나타내는 데이터 구조를 들 수 있다.따라서, 문서의 각 문자는 글꼴 개요, 글꼴 메트릭 및 기타 형식 데이터를 포함하는 글리프 객체를 가질 수 있다.그러나, 이것은 각 문자마다 수백 또는 수천 바이트의 메모리를 사용할 것이다.대신, 각 문자는 문서에서 동일한 문자의 모든 인스턴스가 공유하는 글리프 객체에 대한 참조를 가질 수 있다.이렇게 하면 각 캐릭터의 위치만 내부에 저장하면 된다.
그 결과, 플라이급 객체는 다음을 수행할 수 있다.[5]
- 일정하지 않고, 상황에 따라 독립적이며 공유할 수 있는 내장 상태 저장(예: 주어진 문자 집합에 문자 'A'의 코드)
- 변형되고 상황에 따라 달라지며 공유할 수 없는 외부 상태로 전달하기 위한 인터페이스를 제공한다(예: 텍스트 문서에서 문자 'A'의 위치).
클라이언트를 재사용할 수 있음Flyweight
물체 및 필요에 따라 외부 상태로 전달하여 물리적으로 생성된 물체의 수를 줄인다.
구조
- 그
Client
flyweight 패턴을 사용하는 class - 그
FlyweightFactory
개체를 만들고 공유하는 클래스 - 그
Flyweight
외부 상태를 취하고 작업을 수행하는 인터페이스 - 그
Flyweight1
구현하는 클래스Flyweight
그리고 내재가 되는 상태를 저장한다.
시퀀스 다이어그램에는 다음과 같은 런타임 상호작용이 표시된다.
- 그
Client
반대 전화getFlyweight(key)
에서FlyweightFactory
, 그러면 a를 반환한다.Flyweight1
이의를 제기하다 - 전화후
operation(extrinsicState)
돌아오는 대로Flyweight1
목적어Client
다시 전화하다.getFlyweight(key)
에서FlyweightFactory
. - 그
FlyweightFactory
이미 있는 것을 돌려주다.Flyweight1
이의를 제기하다
이행내역
플라이급 패턴을 구현하는 방법은 여러 가지가 있다.한 가지 예는 변이성이다: 외측 플라이웨이트 상태를 저장하는 물체가 변할 수 있는지 여부.
불변의 객체는 쉽게 공유되지만, 상태가 변할 때마다 새로운 외부 객체를 생성해야 한다.이와는 대조적으로, 변이 가능한 개체는 상태를 공유할 수 있다.돌연변이를 통해 오래된 미사용 객체의 캐싱 및 재초기화를 통해 객체를 보다 효율적으로 재사용할 수 있다.공유는 상태가 매우 가변적일 때 일반적으로 지속할 수 없다.
다른 주요 관심사로는 검색(종료 클라이언트가 플라이급에 액세스하는 방법), 캐싱 및 동시성 등이 있다.
검색
플라이급 객체를 생성하거나 재사용하기 위한 공장 인터페이스는 복잡한 기본 시스템의 전면이다.예를 들어, 공장 인터페이스는 일반적으로 플라이급 생성을 위한 글로벌 액세스를 제공하기 위해 싱글톤으로 구현된다.
일반적으로 말하면, 검색 알고리즘은 공장 인터페이스를 통한 새로운 물체에 대한 요청으로 시작한다.
요청은 일반적으로 어떤 종류의 객체인지에 따라 적절한 캐시로 전달된다.캐시의 객체에 의해 요청이 이행되는 경우, 재초기화하여 반환할 수 있다.그렇지 않으면 새로운 물체가 인스턴스화된다.개체가 여러 개의 외부 하위 구성 요소로 분할된 경우 개체가 반환되기 전에 개체가 함께 압착된다.
캐싱
플라이급 객체를 캐시하는 방법에는 유지된 캐시와 유지되지 않은 캐시의 두 가지가 있다.
상태가 매우 가변적인 개체는 FIFO 구조로 캐시할 수 있다.이 구조는 캐시를 검색할 필요 없이 사용하지 않는 객체를 캐시에 보관한다.
대조적으로, 유지 관리되지 않은 캐시는 초기 오버헤드가 적다. 캐시의 개체는 컴파일 시간이나 시작 시에 대량으로 초기화된다.일단 객체가 캐시를 채우면, 객체 검색 알고리즘은 유지된 캐시의 푸시/팝 작업보다 더 많은 오버헤드를 가질 수 있다.
불변의 상태를 가진 외적 객체를 검색할 때는 캐쉬에서 원하는 상태를 가진 객체를 검색해야 한다.이러한 개체를 찾을 수 없는 경우 해당 상태의 개체를 초기화해야 한다.돌연변이가 가능한 상태의 외부 객체를 검색할 때는 사용하지 않는 객체를 검색하여 사용한 객체가 없는 경우 다시 초기화해야 한다.사용하지 않는 객체가 없는 경우, 새 객체를 인스턴스화하여 캐시에 추가해야 한다.
각각의 고유한 외부 물체 하위 등급에 별도의 캐시를 사용할 수 있다.고유한 검색 알고리즘을 각 캐시와 연결하면서 여러 캐시를 개별적으로 최적화할 수 있다.이 객체 캐싱 시스템은 책임 패턴 체인으로 캡슐화될 수 있으며, 이는 구성요소들 간의 느슨한 결합을 촉진한다.
동시성
다중 스레드에 플라이급 객체가 생성되는 경우를 특별히 고려해야 한다.값 리스트가 유한하고 미리 알려진 경우, 플라이급은 미리 인스턴스화하여 여러 개의 나사산에 있는 컨테이너에서 경합 없이 검색할 수 있다.여러 스레드에서 플라이웨이트를 인스턴스화하는 경우 다음 두 가지 옵션이 있다.
- 플라이급 인스턴스화를 단일 스레드로 만들어 경합을 도입하고 값당 하나의 인스턴스(instance)를 확보하십시오.
- 동시 스레드가 여러 개의 플라이급 인스턴스를 만들도록 허용하여 경합을 없애고 값당 여러 인스턴스를 허용하십시오.
클라이언트와 스레드 간의 안전한 공유를 가능하게 하기 위해 플라이웨이트 객체를 불변의 값 객체로 만들 수 있으며, 여기서 값이 같을 경우 두 인스턴스가 동일한 것으로 간주된다.
C# 9의 이 예는 기록을[7] 사용하여 커피 맛을 나타내는 가치 객체를 만든다.
공중의 기록하다 커피플라버스(끈을 매다 맛을 내다);
C#의 예
이 예에서는 다음과 같이 한다.FlyweightPointer
의 모든 인스턴스에 사용되는 정적 멤버 생성MyObject
계급
// 자신을 반복하는 Flyweight 객체를 정의한다. 공중의 계급 플라이급 { 공중의 끈을 매다 회사 이름 { 얻다; 세트; } 공중의 끈을 매다 컴퍼니로케이션 { 얻다; 세트; } 공중의 끈을 매다 컴퍼니웹사이트 { 얻다; 세트; } // 부피가 큰 데이터 공중의 바이트[] 회사 로고 { 얻다; 세트; } } 공중의 정태의 계급 FlyweightPointer { 공중의 정태의 읽기 전용 플라이급 회사 = 새로운 플라이급 { 회사 이름 = "abc", 컴퍼니로케이션 = "XYZ", 컴퍼니웹사이트 = "www.example.com" // 여기에 CompanyLogo 로드 }; } 공중의 계급 마이 오브젝트 { 공중의 끈을 매다 이름 { 얻다; 세트; } 공중의 끈을 매다 회사 => FlyweightPointer.회사.회사 이름; }
Python의 예
속성은 Python의 인스턴스만 정의하는 것이 아니라 클래스 레벨에서 정의할 수 있다. 왜냐하면 클래스는 언어에서 일급 객체(다른 객체들과 동일하기 때문에 그들의 사용에 제한이 없다는 의미)이기 때문이다.새로운 유형의 클래스 인스턴스가 인스턴스 데이터를 특수 속성 사전에 저장instance.__dict__
. 기본적으로 액세스된 속성이 먼저 검색됨__dict__
그리고 나서 다음 인스턴스의 클래스 속성으로 돌아가십시오.[8]이러한 방식으로 클래스는 그 예에 대해 사실상 일종의 Flyweight 컨테이너가 될 수 있다.
Python 클래스는 기본적으로 변경할 수 있지만 클래스의 클래스를 재정의하여 불변성을 에뮬레이션할 수 있다.__setattr__
모든 Flyweight 속성에 대한 변경을 허용하지 않는 방법.
# 치즈브랜드는 플라이급이 될 것이다. 계급 치즈브랜드: 반항하다 __init___(자아의, 낙인을 찍다: 발을 동동 구르다, 비용이 들다: 둥둥 뜨다) -> 없음: 자아의.낙인을 찍다 = 낙인을 찍다 자아의.비용이 들다 = 비용이 들다 자아의.불변의 = 진실의 # 향후 귀속 불가능 반항하다 __setattr___(자아의, 이름을 붙이다, 가치를 매기다): 만일 게트레질하다(자아의, "_불가능", 거짓의): # 최초 귀속 허용 높이다 RuntimeError("이 물체는 불변의 것") 다른: 잘 하는 군요().__setattr___(이름을 붙이다, 가치를 매기다) 계급 치즈샵: 메뉴판 = {} # Flyweights에 액세스할 수 있는 공유 컨테이너 반항하다 __init___(자아의) -> 없음: 자아의.명령 = {} # 개인 속성이 있는 프로세서 단위 컨테이너 반항하다 주식_주식(자아의, 낙인을 찍다: 발을 동동 구르다, 비용이 들다: 둥둥 뜨다) -> 없음: 치즈를 치다 = 치즈브랜드(낙인을 찍다, 비용이 들다) 자아의.메뉴판[낙인을 찍다] = 치즈를 치다 # 공유플라이급 반항하다 sell_builled(자아의, 낙인을 찍다: 발을 동동 구르다, 단위: 인트로) -> 없음: 자아의.명령.기본값을 설정하다(낙인을 찍다, 0) 자아의.명령[낙인을 찍다] += 단위 # 인스턴스 속성 반항하다 total_suffer_suffer_suffer(자아의): 돌아오다 합계를 내다(자아의.명령.가치()) 반항하다 총액_소득(자아의): 수입 = 0 을 위해 낙인을 찍다, 단위 에 자아의.명령.항목들(): 수입 += 자아의.메뉴판[낙인을 찍다].비용이 들다 * 단위 돌아오다 수입 숍1 = 치즈샵() 숍2 = 치즈샵() 숍1.주식_주식("흰색", 1.25) 숍1.주식_주식("파란색", 3.75) # 이제 모든 치즈샵에는 재고에 '흰색'과 '파란색'이 있다. # 같은 '흰색'과 '파란색' 치즈브랜드 숍1.sell_builled("파란색", 3) # 둘 다 팔 수 있다. 숍2.sell_builled("파란색", 8) # 하지만 판매되는 단위는 인스턴스당 저장 주장하다 숍1.total_suffer_suffer_suffer() == 3 주장하다 숍1.총액_소득() == 3.75 * 3 주장하다 숍2.total_suffer_suffer_suffer() == 8 주장하다 숍2.총액_소득() == 3.75 * 8
C++의 예
C++ Standard Template Library는 고유한 개체를 키에 매핑할 수 있는 여러 컨테이너를 제공한다.컨테이너를 사용하면 임시 객체를 만들 필요가 없어져 메모리 사용량을 더욱 줄일 수 있다.
#include <아이오스트림> #include <지도> #include <끈> // 테넌트의 인스턴스가 Flyweights가 됨 계급 세입자 { 공중의: 세입자(경시하다 찌꺼기::끈을 매다& 이름을 붙이다 = "") : m_name(이름을 붙이다) {} 찌꺼기::끈을 매다 이름을 붙이다() 경시하다 { 돌아오다 m_name; } 사유의: 찌꺼기::끈을 매다 m_name; }; // 레지스트리가 Tenant Flyweight 객체의 공장 및 캐시 역할을 함 계급 레지스트리 { 공중의: 레지스트리() : 세입자() {} 세입자& findByName(경시하다 찌꺼기::끈을 매다& 이름을 붙이다) { 만일 (!세입자.포함하다(이름을 붙이다)) { 세입자[이름을 붙이다] = 세입자{이름을 붙이다}; } 돌아오다 세입자[이름을 붙이다]; } 사유의: 찌꺼기::지도를 그리다<찌꺼기::끈을 매다, 세입자> 세입자; }; // 아파트는 독특한 입주자를 그들의 방 번호에 매핑한다. 계급 아파트 { 공중의: 아파트() : m_dv(), m_dv() {} 공허하게 하다 addOperant(경시하다 찌꺼기::끈을 매다& 이름을 붙이다, 인트로 방) { m_dv[방] = &m_dv.findByName(이름을 붙이다); } 공허하게 하다 세입자() { 을 위해 (경시하다 자동차로 &i : m_dv) { 경시하다 인트로& 방 = i.맨 처음의; 경시하다 자동차로& 소작인 = i.둘째; 찌꺼기::뻐드렁니가 나다 << 소작인->이름을 붙이다() << "가 공간을 차지하다 " << 방 << 찌꺼기::끝을 맺다; } } 사유의: 찌꺼기::지도를 그리다<인트로, 세입자*> m_dv; 레지스트리 m_dv; }; 인트로 본래의() { 아파트 아파트; 아파트.addOperant("데이빗", 1); 아파트.addOperant("사라", 3); 아파트.addOperant("조지", 2); 아파트.addOperant("리사", 12); 아파트.addOperant("마이클", 10); 아파트.세입자(); 돌아오다 0; }
PHP의 예
<?php 계급 커피플라부어 { 사유의 정태의 배열하다 $CACHE = []; 사유의 기능을 발휘하다 ____(사유의 끈을 매다 달러명) {} 공중의 정태의 기능을 발휘하다 인턴으로 채용하다(끈을 매다 달러명): 자아의 { 자아의::$CACHE[달러명] ??= 새로운 자아의(달러명); 돌아오다 자아의::$CACHE[달러명]; } 공중의 정태의 기능을 발휘하다 향료잉카체(): 인트로 { 돌아오다 수를 세다(자아의::$CACHE); } 공중의 기능을 발휘하다 __toString(): 끈을 매다 { 돌아오다 $ this->이름을 붙이다; } } 계급 주문 { 사유의 기능을 발휘하다 ____( 사유의 커피플라부어 달러우르, 사유의 인트로 $tableNumber ) {} 공중의 정태의 기능을 발휘하다 만들다(끈을 매다 $flavourName, 인트로 $tableNumber): 자아의 { 달러우르 = 커피플라부어::인턴으로 채용하다($flavourName); 돌아오다 새로운 자아의(달러우르, $tableNumber); } 공중의 기능을 발휘하다 __toString(): 끈을 매다 { 돌아오다 "서빙"{$ this->맛을 내다}식탁에 오르다{$ this->테이블 번호}"; } } 계급 커피숍 { 사유의 배열하다 달러 주문 = []; 공중의 기능을 발휘하다 테이크오더(끈을 매다 달러우르, 인트로 $tableNumber) { $ this->명령[] = 주문::만들다(달러우르, $tableNumber); } 공중의 기능을 발휘하다 서비스() { 인쇄하다(폭동을 일으키다(PHP_EOL, $ this->명령).PHP_EOL); } } $shop = 새로운 커피숍(); $shop->테이크오더("카푸치노", 2); $shop->테이크오더("프레페", 1); $shop->테이크오더("에스프레소", 1); $shop->테이크오더("프레페", 897); $shop->테이크오더("카푸치노", 97); $shop->테이크오더("프레페", 3); $shop->테이크오더("에스프레소", 3); $shop->테이크오더("카푸치노", 3); $shop->테이크오더("에스프레소", 96); $shop->테이크오더("프레페", 552); $shop->테이크오더("카푸치노", 121); $shop->테이크오더("에스프레소", 121); $shop->서비스(); 인쇄하다("CoffeeFlavor 객체의 캐쉬: ".커피플라부어::향료잉카체().PHP_EOL);
참고 항목
참조
- ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. pp. 195ff. ISBN 978-0-201-63361-0.
{{cite book}}
: CS1 maint : 복수이름 : 작성자 목록(링크) - ^ Gamma, Erich; Richard Helm; Ralph Johnson; John Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. pp. 205–206. ISBN 978-0-201-63361-0.
- ^ Calder, Paul R.; Linton, Mark A. (October 1990). Glyphs: Flyweight Objects for User Interfaces. The 3rd Annual ACM SIGGRAPH Symposium on User Interface Software and Technology. Snowbird, Utah, United States. pp. 92–101. doi:10.1145/97924.97935. ISBN 0-89791-410-4.
- ^ Weinand, Andre; Gamma, Erich; Marty, Rudolf (1988). ET++—an object oriented application framework in C++. OOPSLA (Object-Oriented Programming Systems, Languages and Applications). San Diego, California, United States. pp. 46–57. CiteSeerX 10.1.1.471.8796. doi:10.1145/62083.62089. ISBN 0-89791-284-5.
- ^ "Implementing Flyweight Patterns in Java". Developer.com. 2019-01-28. Retrieved 2021-06-12.
- ^ "The Flyweight design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
- ^ BillWagner. "Records - C# reference". docs.microsoft.com. Retrieved 2021-06-12.
- ^ "Data Model §". The (online) Python Language Reference. Python Software Foundation. Retrieved 7 March 2017.