
コンピュータプログラミングにおいて、フライウェイト ソフトウェア設計パターンとは、他の同様のオブジェクトとデータの一部を共有することでメモリ使用量を最小限に抑えるオブジェクトを指します。フライウェイトパターンは、よく知られている23のGoF設計パターンの1つです。[1]これらのパターンは、実装、変更、テスト、再利用が容易な柔軟なオブジェクト指向ソフトウェア設計を促進します。
他の文脈では、データ構造を共有するという考え方をハッシュコンシングと呼びます。
この用語は、1990年にポール・カルダーとマーク・リントンによって最初に造られ、そのアイデアは広く研究されました[2] 。WYSIWYGドキュメントエディタでグリフ情報を効率的に処理するためです[3]。しかし、同様の技術は1988年にはすでに他のシステムで使用されていました[4]。
概要
flyweight パターンは、個別に埋め込まれると大量のメモリを使用する単純な繰り返し要素を共有する多数のオブジェクトを処理するときに役立ちます。共有データを外部データ構造に保持し、オブジェクトが使用されるときに一時的にオブジェクトに渡すのが一般的です。
典型的な例としては、ワードプロセッサで文字を表すために使用されるデータ構造があります。単純に言えば、文書内の各文字には、フォント アウトライン、フォント メトリック、およびその他の書式設定データを含むグリフオブジェクトがあります。ただし、これでは文字ごとに数百または数千バイトのメモリが使用されます。代わりに、各文字には、文書内の同じ文字のすべてのインスタンスで共有されるグリフ オブジェクトへの参照を含めることができます。この方法により、各文字の位置のみを内部に保存する必要があります。
その結果、フライウェイトオブジェクトは次のことができるようになります。[5]
- 不変で、コンテキストに依存せず、共有可能な固有の状態を保存する(たとえば、特定の文字セット内の文字「A」のコード)
- 可変でコンテキストに依存し、共有できない外部状態を渡すためのインターフェースを提供する(たとえば、テキスト文書内の文字「A」の位置)
クライアントはFlyweightオブジェクトを再利用し、必要に応じて外部状態を渡すことができるため、物理的に作成されるオブジェクトの数を減らすことができます。
構造

Clientフライウェイトパターンを使用するクラス- オブジェクトを
FlyweightFactory作成して共有するクラスFlyweight - 外部状態を受け取り、操作を実行
Flyweightするインターフェース - 固有の状態を
Flyweight1実装して保存するクラスFlyweight
シーケンス図は、次の実行時の相互作用を示しています。
- オブジェクトはを
Client呼び出し、オブジェクトを返します。getFlyweight(key)FlyweightFactoryFlyweight1 operation(extrinsicState)返されたオブジェクトを呼び出した後Flyweight1、 はClient再度 を呼び出しgetFlyweight(key)ますFlyweightFactory。FlyweightFactory既存のオブジェクトを返しますFlyweight1。
実装の詳細
flyweight パターンを実装する方法は複数あります。1 つの例は可変性です。つまり、外部 flyweight 状態を格納するオブジェクトが変更できるかどうかです。
不変オブジェクトは簡単に共有できますが、状態が変化するたびに新しい外部オブジェクトを作成する必要があります。対照的に、可変オブジェクトは状態を共有できます。可変性により、古い未使用のオブジェクトをキャッシュして再初期化することで、オブジェクトの再利用性が向上します。状態が非常に変化する場合は、通常、共有は実行できません。
その他の主な懸念事項としては、検索 (エンドクライアントがフライウェイトにアクセスする方法)、キャッシュ、同時実行性などがあります。
検索
flyweight オブジェクトを作成または再利用するためのファクトリ インターフェイスは、多くの場合、複雑な基盤システムのファサードとなります。たとえば、ファクトリ インターフェイスは、flyweight を作成するためのグローバル アクセスを提供するために、シングルトンとして実装されるのが一般的です 。
一般的に言えば、取得アルゴリズムは、ファクトリ インターフェイスを介した新しいオブジェクトの要求から始まります。
通常、リクエストは、オブジェクトの種類に基づいて適切なキャッシュに転送されます。リクエストがキャッシュ内のオブジェクトによって満たされる場合、そのオブジェクトは再初期化されて返されます。それ以外の場合は、新しいオブジェクトがインスタンス化されます。オブジェクトが複数の外部サブコンポーネントに分割されている場合、オブジェクトが返される前に、それらのサブコンポーネントが結合されます。
キャッシング
flyweight オブジェクトをキャッシュする方法には、メンテナンスされたキャッシュとメンテナンスされていないキャッシュの 2 つがあります。
状態が非常に変化するオブジェクトは、 FIFO構造を使用してキャッシュできます。この構造では、未使用のオブジェクトがキャッシュ内に保持されるため、キャッシュを検索する必要がありません。
対照的に、メンテナンスされていないキャッシュでは、事前のオーバーヘッドが少なくなります。キャッシュのオブジェクトは、コンパイル時または起動時に一括して初期化されます。オブジェクトがキャッシュに取り込まれると、オブジェクト取得アルゴリズムに関連するオーバーヘッドは、メンテナンスされたキャッシュのプッシュ/ポップ操作よりも大きくなる可能性があります。
不変状態の外部オブジェクトを取得する場合は、必要な状態のオブジェクトをキャッシュで検索するだけです。そのようなオブジェクトが見つからない場合は、その状態のオブジェクトを初期化する必要があります。可変状態の外部オブジェクトを取得する場合は、使用済みのオブジェクトが見つからない場合は、未使用のオブジェクトをキャッシュで検索して再初期化する必要があります。未使用のオブジェクトがない場合は、新しいオブジェクトをインスタンス化してキャッシュに追加する必要があります。
外部オブジェクトの固有のサブクラスごとに個別のキャッシュを使用できます。複数のキャッシュを個別に最適化し、各キャッシュに固有の検索アルゴリズムを関連付けることができます。このオブジェクト キャッシュ システムは、コンポーネント間の疎結合を促進する Chain of Responsibilityパターンを使用してカプセル化できます。
同時実行性
flyweight オブジェクトが複数のスレッドで作成される場合は、特別な考慮が必要です。値のリストが有限で、事前にわかっている場合は、flyweight を事前にインスタンス化して、競合なしで複数のスレッドのコンテナーから取得できます。flyweight が複数のスレッドでインスタンス化される場合、次の 2 つのオプションがあります。
- flyweight インスタンス化をシングルスレッドにして、競合を導入し、値ごとに 1 つのインスタンスを確保します。
- 同時スレッドが複数の flyweight インスタンスを作成できるようにすることで、競合を排除し、値ごとに複数のインスタンスを許可します。
クライアントとスレッド間の安全な共有を可能にするために、flyweight オブジェクトを不変 値オブジェクトにすることができます。この場合、2 つのインスタンスの値が等しい場合は、それらのインスタンスは等しいとみなされます。
C# 9のこの例では、レコード[7]を使用してコーヒーのフレーバーを表す値オブジェクトを作成します。
公開レコードCoffeeFlavours (文字列flavor );
例
C#
この例では、MyObjectクラスのすべてのインスタンスがクラスを使用してPointerデータを提供します。
// 繰り返される Flyweight オブジェクトを定義します。public
class Flyweight { public string Name { get ; set ; } public string Location { get ; set ; } public string Website { get ; set ; } public byte [ ] Logo { get ; set ; } }
パブリック静的クラスPointer {パブリック静的読み取り専用Flyweight Company = new Flyweight { "Abc" , "XYZ" , "www.example.com" }; }
パブリッククラスMyObject {パブリック文字列Name { get ; set ; }パブリック文字列Company => Pointer . Company . Name ; }
C++
C++標準テンプレート ライブラリには、一意のオブジェクトをキーにマップできるコンテナーがいくつか用意されています。コンテナーを使用すると、一時オブジェクトを作成する必要がなくなり、メモリ使用量をさらに削減できます。
#include <iostream> #include <map> #include <string>
// Tenant のインスタンスは Flyweights になります
class Tenant { public : Tenant ( const std :: string & name = "" ) : m_name ( name ) {}
std ::文字列名() const { return m_name ; } private : std ::文字列m_name ; };
// レジストリは、テナント フライウェイト オブジェクトのファクトリーおよびキャッシュとして機能します。
クラスレジストリ{ public : Registry () : tenants () {}
Tenant & findByName ( const std :: string & name ) { if ( ! tenants.contains ( name ) ) { tenants [ name ] = Tenant { name }; } return tenants [ name ]; } private : std :: map < std :: string , Tenant > tenants ; } ;
// アパートメントは、一意のテナントを部屋番号にマッピングします。class Apartment { public : Apartment () : m_occupants (), m_registry () {}
void addOccupant ( const std :: string & name , int room ) { m_occupants [ room ] = & m_registry . findByName ( name ); }
void tenants () { for ( const auto & i : m_occupants ) { const int & room = i.first ; const auto & tenant = i.second ; std :: cout << tenant - > name () << " occupies room " << room << std :: endl ; } } private : std :: map < int , Tenant * > m_occupants ;レジストリm_registry ; } ;
int main ( ) {アパートメント;アパート。addOccupant ( "David" , 1 );アパート。addOccupant ( "サラ" , 3 );アパート。addOccupant ( "ジョージ" , 2 );アパート。addOccupant ( "リサ" , 12 );アパート。addOccupant ( "マイケル" , 10 );アパート。テナント();
0 を返す; }
PHP の
<?php
クラス CoffeeFlavour {
プライベート 静的 配列 $CACHE = [];
プライベート 関数 __construct (プライベート 文字列 $name ) {}
パブリック 静的 関数 intern (文字列 $name ) : self {
self :: $CACHE [ $name ] ??= new self ( $name );
return self :: $CACHE [ $name ];
}
パブリック 静的 関数 flavorsInCache () : int {
return count ( self :: $CACHE );
}
パブリック 関数 __toString () : 文字列 {
戻り値 $this- > name ;
}
}
クラス Order {
プライベート 関数 __construct (
プライベート CoffeeFlavour $flavour 、
プライベート int $tableNumber
) {}
パブリック 静的 関数 create ( string $flavourName 、 int $tableNumber ) : self {
$flavour = CoffeeFlavour :: intern ( $flavourName );
return new self ( $flavour 、 $tableNumber );
}
public function __toString () : string {
return " { $this -> flavor }をテーブル{ $this -> tableNumber }に提供しています" ;
}
}
クラス CoffeeShop {
プライベート 配列 $orders = [];
パブリック 関数 takeOrder (文字列 $flavour 、 int $tableNumber ) {
$this -> orders [] = Order :: create ( $flavour 、 $tableNumber );
}
パブリック 関数 サービス() {
print ( implode ( PHP_EOL 、 $this -> orders ) . PHP_EOL );
}
}
$shop = new CoffeeShop ();
$shop -> takeOrder ( "カプチーノ" , 2 );
$shop -> takeOrder ( "フラッペ" , 1 );
$shop -> takeOrder ( "エスプレッソ" , 1 );
$shop - > takeOrder ( "フラッペ" , 897 );
$shop -> takeOrder ( "カプチーノ" , 97 ); $
shop -> takeOrder ( "フラッペ" , 3 );
$shop -> takeOrder ( "エスプレッソ" , 3 );
$shop -> takeOrder ( "カプチーノ" , 3 );
$shop -> takeOrder ( "エスプレッソ" , 96 );
$shop -> takeOrder ( "フラッペ" , 552 );
$shop -> takeOrder ( "カプチーノ" , 121 );
$shop -> takeOrder ( "エスプレッソ" , 121 );
$shop -> service ();
print ( "キャッシュ内の CoffeeFlavor オブジェクト: " . CoffeeFlavour :: flavorsInCache () . PHP_EOL );
参照
参考文献
- ^ Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides (1994)。デザインパターン: 再利用可能なオブジェクト指向ソフトウェアの要素。Addison Wesley。pp. 195ff。ISBN 978-0-201-63361-0。
{{cite book}}: CS1 maint: multiple names: authors list (link) - ^ ガンマ、エリック、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリサイドス(1995)。デザインパターン: 再利用可能なオブジェクト指向ソフトウェアの要素。アディソン・ウェズレー。pp. 205–206。ISBN 978-0-201-63361-0。
- ^ Calder, Paul R.; Linton, Mark A. (1990 年 10 月)。「Glyphs: ユーザー インターフェイス用の軽量オブジェクト」。第3 回 ACM SIGGRAPH ユーザー インターフェイス ソフトウェアおよびテクノロジ シンポジウムの議事録- UIST '90。第 3 回ACM SIGGRAPHユーザー インターフェイス ソフトウェアおよびテクノロジ シンポジウム。ユタ州スノーバード、米国。pp. 92–101。doi :10.1145/97924.97935。ISBN 0-89791-410-4。
- ^ ウェイナンド、アンドレ;ガンマ、エーリッヒ。マーティ、ルドルフ(1988)。ET++ - C++ のオブジェクト指向アプリケーション フレームワーク。OOPSLA (オブジェクト指向プログラミング システム、言語、およびアプリケーション)。米国カリフォルニア州サンディエゴ。 46~57ページ。CiteSeerX 10.1.1.471.8796。土井:10.1145/62083.62089。ISBN 0-89791-284-5。
- ^ 「Java でフライウェイト パターンを実装する」。Developer.com 2019年 1 月 28 日。2021年 6 月 12 日閲覧。
- ^ 「Flyweight デザイン パターン - 構造とコラボレーション」w3sDesign.com 。 2017 年 8 月 12 日閲覧。
- ^ BillWagner. 「レコード - C# リファレンス」. docs.microsoft.com . 2021 年 6 月 12 日閲覧。
