Flyweightパターン


 ★★☆

Flyweightパターン

インスタンスを共有し無駄なインスタンスを生成しない

Flyweight とは、英語で「フライ級」を意味する単語です。 フライ級とは、ボクシングなどの体重階級の中で軽い部類に分類できる階級です。もっと軽い階級もありますが。 Flyweight パターンとは、いかにしてプログラムの動作を軽くするかに焦点を当てたパターンです。


Flyweight パターンとは、このように、同じインスタンスを共有することで、無駄なインスタンスを生成しないようにして、 プログラム全体を軽くすることを目的としたパターンなのです。

インスタンスを生成するということは、コンピュータのリソースを使うということです。リソースとはメモリや時間です。インスタンスを生成するときに、一定の時間が掛かるとしてFlyweight パターンを使ってインスタンスを共有すればインスタンスの生成する数を減らしスピードも上げることもできます。


また、Flyweight パターンを利用することで、どのインスタンスを持っているのかなどを、利用者(main クラスのような呼び出し側) で把握しておく必要もなくなります。また、複数の呼び出し先からの要求にも無駄なく応えることが可能となります。

ただし、Flyweight パターンは、ひとつのインスタンスを各所で共有することになりますので、 共有されるインスタンスが本質的(intrinsic)な情報のみを持つ場合にのみ利用するべきです。 非本質的(extrinsic)な情報は、どこかの誰かが変更する可能性のあるものであり、 共有するインスタンスを勝手に誰かが変更することで、そこかしこに影響を与えることになりかねません。


例題

与えられた客先番号から、客先の情報を持つインスタンスを生成するクラスを作りなさい。


実行結果
1 名前1           
25 名前25
1 名前1









共通クラス

Customer.java
public class Customer { private int number; private String name; private String address; public Customer(int number) { this.number = number; // // DBを見に行ったつもり name = "名前" + number; address = "住所" + number; // } public int getNumber() { return number; } public String getName() { return name; } public String getAddress() { return address; } }

Flyweightパターンを使用しない例

Main.java
public class Main { public static void main(String[] args) { Customer c1 = new Customer(1); System.out.println(c1.getNumber() + " " + c1.getName()); Customer c2 = new Customer(25); System.out.println(c2.getNumber() + " " + c2.getName()); Customer c3 = new Customer(1); System.out.println(c3.getNumber() + " " + c3.getName()); } }

Flyweightパターンを使用した例

CustomerFactory.java
public class CustomerFactory { private HashMap<Integer, Customer> pool = new HashMap<Integer, Customer>(); private static CustomerFactory singleton = new CustomerFactory(); private CustomerFactory() {} public static CustomerFactory getInstance() { return singleton; } public synchronized Customer getCustomer(int number) { Integer no = new Integer(number); Customer c = (Customer)pool.get(no); if (c == null) { c = new Customer(number); pool.put(no, c); } return c; } }
Main.java
public class Main { public static void main(String[] args) { CustomerFactory factory = CustomerFactory.getInstance(); Customer c1 = factory.getCustomer(1); System.out.println(c1.getNumber() + " " + c1.getName()); Customer c2 = factory.getCustomer(25); System.out.println(c2.getNumber() + " " + c2.getName()); Customer c3 = factory.getCustomer(1); System.out.println(c3.getNumber() + " " + c3.getName()); } }

この例では、与えられた客先番号をもとにデータベースから、その客先の情報を取り出すクラス Customer を作成しています。このクラスを利用する側は、客先番号を引数に、この Customer クラスのインスタンスを生成すれば、後は、そのメソッドを呼び出すだけで必要な情報が得られます。

この例では1、25、そしてまた1の客先番号の情報を得ています。しかし、同じ1の客先の情報を得るのに、別のインスタンスを生成していて、無駄があります。つまり、c1 と c3 は同じ情報なので、新たに c3 に設定する情報を生成する必要は無いのです。

この例のように、生成する数が少ないとか、生成する場所が限定されているのならば、まだ分かります。しかし、そうでなければ、すでに必要なインスタンスが生成されているかどうかは分かりにくですし、生成されているのは分かっていても、参照できないようになっているかもしれません。 (スーパクラスや他のサブクラスで private な変数に入れられている、など)

Flyweight パターンでは、一度生成された、客先番号のインスタンスは、 (この例では、ハッシュテーブル pool に)保存されています。よって、同じ客先番号であれば、この保存されたインスタンスが返されます。まだ生成されていない、初めての客先番号の場合は、インスタンスが生成され、そのインスタンスが返されると同時に保存されます。