Flyweightパターンは、GoF(Gang of Four)によって定義されたデザインパターンの一つです。このパターンは、同じ性質を持つオブジェクトの
インスタンスを複数作成する代わりに、単一の
インスタンスを共有することで、メモリ使用量と計算コストを削減することを目的としています。Flyweightという名前は、
ボクシングの階級の一つである「
フライ級」に由来しており、軽量なオブジェクトを意味しています。
Flyweightパターンの構造
Flyweightパターンでは、主に以下の要素が関係します。
Flyweight: 共有されるオブジェクトのインターフェースを定義します。このインターフェースを通じて、共有オブジェクトの操作を行います。
ConcreteFlyweight: Flyweightインターフェースを実装した具体的なクラスです。このクラスの
インスタンスは共有されます。
UnsharedConcreteFlyweight: Flyweightインターフェースを実装していますが、共有されないクラスです。必要な場合のみ使用されます。
FlyweightFactory: Flyweightオブジェクトの生成と管理を行います。クライアントからの要求に応じて、既存のオブジェクトを返すか、新しいオブジェクトを生成します。
Flyweightパターンの動作
Flyweightパターンを採用したAPIでは、クライアントはFlyweightクラスの
インスタンスを直接作成する代わりに、FlyweightFactoryの`getFlyweight()`メソッドを使用します。`FlyweightFactory`は、以下の手順でオブジェクトを返します。
1.
インスタンスが存在しない場合:
新しいFlyweightインスタンスを生成します。
生成した
インスタンスを内部のプールに保存します。
生成したインスタンスを返します。
2. インスタンスが既に存在する場合:
プールから既存のFlyweight
インスタンスを取得します。
取得したインスタンスを返します。
このように`FlyweightFactory`は内部で異なる処理を実行しますが、クライアントは常に同じ結果を得られます。このため、クライアントは`FlyweightFactory`の内部構造を意識せずに利用できます。
Flyweightパターンの適用例
Flyweightパターンが特に有効なのは、不変な(イミュータブル)オブジェクトを扱う場合です。不変なオブジェクトは生成後に状態が変化しないため、複数の箇所で同じインスタンスを共有しても問題ありません。Javaでは`java.math.BigInteger`や`java.awt.Color`などが不変なクラスの例として挙げられます。
以下に、JavaでFlyweightパターンを実装した例を示します。
java
// Flyweightインターフェース
interface Stamp {
void print(String text);
}
// ConcreteFlyweightクラス
class ConcreteStamp implements Stamp {
private String value;
public ConcreteStamp(String value) {
this.value = value;
}
@Override
public void print(String text) {
System.out.print(value);
}
}
// FlyweightFactoryクラス
class StampFactory {
private static Map stamps = new HashMap<>();
public static Stamp get(String value) {
if (!stamps.containsKey(value)) {
stamps.put(value, new ConcreteStamp(value));
}
return stamps.get(value);
}
}
// 利用例
public class FlyweightTest {
public static void main(String[] args) {
StampFactory.get("た").print("a");
StampFactory.get("か").print("b");
StampFactory.get("い").print("c");
StampFactory.get("た").print("d");
StampFactory.get("け").print("e");
StampFactory.get("た").print("f");
StampFactory.get("て").print("g");
StampFactory.get("か").print("h");
StampFactory.get("け").print("i");
StampFactory.get("た").print("j");
}
}
このコードを実行すると、「たかいたけたてかけた」と出力されます。`FlyweightTest.main()`では`StampFactory.get()`を10回呼び出していますが、実際に生成されたインスタンスは5つ(「た」、「か」、「い」、「け」、「て」)のみであり、インスタンスが共有されていることが分かります。
注意点
`FlyweightFactory`に一度保存されたインスタンスは、不要になったとしてもガベージコレクションの対象になりません。そのため、状況に応じて`FlyweightFactory`から明示的に削除する必要があります。
関連するパターン
Singletonパターン: `FlyweightFactory`はSingletonパターンとして実装されることが多いです。これにより、`FlyweightFactory`の
インスタンスが一つだけになるため、オブジェクトの管理を効率的に行えます。
関連項目
デザインパターン (ソフトウェア)
ガベージコレクション
イミュータブル
外部リンク
Javaの理論と実践: 可変性か、不変性か? | IBM, Internet Archive