デザインパターンFlyweightについて勉強した
flyweightパターンとは
ボクシングなどのフライ級を意味します。
プログラミングでは軽量化と言ったような意味になります。
クラスを扱うにはインスタンスを生成します。
プログラムにもよりますが、オブジェクトが千や万も作られることもあるかもしれません。
また、オブジェクト数はそれほど多くはないけれど、一つ一つのオブジェクトのメモリ量が多いこともあります。
オブジェクトの数が多いほどpcのメモリは圧迫され、動作が遅くなっていきます。
メモリを節約するにはどうすればいいでしょうか。
その方法の一つに、同じ内容のオブジェクトは作らずに使い回すことが考えられます。
例えば画面上に同じ写真を10個並べて表示するプログラムを作ったとしましょう。
同じ画像ファイルを10回読み込むのでは明らかに無駄です。
読み込みは1回だけにして、残り9個は最初に読み込んだオブジェクトの参照を渡してやればいいのです。
コード例
ここではランダムに文字を選択して並べ、特定の文字列が揃ったらあたりになるプログラムを作ってみます。
CharItemクラス
public class CharItem { private char item ; public CharItem(char item) { this.item = item; } public char getItem() { return item; } @Override public String toString() { return ">>> " + item + " <<<"; } }
文字一つを表すクラスです。
既にcharクラスがあるので本当は不要ですが、説明のために作りました。
このクラスが何か重いデータを持っている(gif画像データなど)と考えてください。
ItemFactoryクラス
public class ItemFactory { private static ItemFactory factory = new ItemFactory(); private HashMap<Character,CharItem> items = new HashMap<Character,CharItem>(); private ItemFactory() { } public static ItemFactory getInstance() { return factory; } public CharItem createItem(char c) { CharItem item = items.get(c); if(item==null) { item = new CharItem(c); items.put(c, item); } return item; } }
CharItemクラスを作成するためのクラスです。
シングルトンパターンになっています。
itemsフィールドは既に作成済みのCharItemを保持しておくためのものです。
createItemメソッドは、渡されたcharを元にCharItemを返します。
既に作成済みであればitemsフィールドに保持されたものを返し、未作成であれば新たにオブジェクトを生成しています。
このメソッドがflyweightパターンの本体です。
使いまわせるものがあればそれを使い、なければ新しく作ることで無駄なメモリの使用を抑えようとしています。
実装
ItemStackクラス
public class ItemStack { private CharItem[] items = new CharItem[0]; private char[] elements = new char[] { 'O','C','H','I','N', }; private String[] lucky_text = new String[] { }; public boolean randomItems(int size) { Random random = new Random(); ItemFactory factory = ItemFactory.getInstance(); items = new CharItem[size]; try { for(int i=0;i<size;i++) { items[i] = factory.createItem(elements[random.nextInt(elements.length)]); System.out.println(items[i]); Thread.sleep(500); } }catch(InterruptedException e) { e.printStackTrace(); } lucky: for(String text : lucky_text) { if(text.length()!=size) continue; for(int i=0;i<size;i++) { if(items[i].getItem()!=text.charAt(i)) continue lucky; } System.out.println("おめでとうございます!!!!"); return true; } System.out.println("残念..."); return false; } public boolean randomItems() { return randomItems(9); } public void auto() { while(!randomItems()); } }
特定のcharからランダムにいくつか選択し、特定の文字列になったらあたりになるゲームです。
itemsフィールドは、最後に選択されたCharItemの羅列を保持します。
elementsフィールドは選択できるcharです。
lucky_textフィールドは当たりの文字列です。elementsフィールドに含まれる文字で好きな文字列を入力してください。
randomItemsメソッドは、渡された数のCharItemの列を作り、それがあたりかどうかを判定しています。
forの中でCharItemを生成しています。factory.createItem(elements[random.nextInt(elements.length)]);
これで、既に作られているものと同じオブジェクトは新たに生成されず、以前に作ったものを使いまわすようになります。
randomItemsメソッドはオーバロードされています。
引数なしの場合はデフォルトで9文字の文字列をランダム生成します。
autoメソッドは、当たりが出るまで回し続けるメソッドです。
実行
public class Main { public Main() { ItemStack stack = new ItemStack(); //stack.randomItems(); stack.auto(); } public static void main(String[] args) { new Main(); } }
stack.autoメソッドで当たりが出るまで回し続けます。
randomItemsに変えると、1回の実行ごとに1回だけになります。