親父の小言のように何度も同じ話をして恐縮ですが、とても大事なことなので聞いてください。GoFの23種類のデザインパターンは、生成に関するパターン、構造に関するパターン、振る舞いに関するパターンの3つに分類されています。このことから、オブジェクト指向プログラミングを上手に実践するには、生成、構造、振る舞いに関する工夫をすればよいことがわかります。GoFデザインパターンは、工夫のお手本集なのです。
個々のデザインパターンの本質(何が便利なのか)を見抜くコツは、どの分類に属しているかを確認することです。今回紹介するSingletonパターンは生成に関するパターンに分類され、Flyweightパターンは構造に関するパターンに分類されています。すなわち、オブジェクトの生成に関する工夫と、プログラムの構造に関する工夫です。
【お役立ち度】★★★☆☆
●オブジェクトを1つだけしか生成させないSingletonパターン
これは、私がJavaプログラミングの講師として、ある企業におじゃましたときのことです。教育担当者のAさん(女性)が、オブジェクト指向プログラミングがわからないと悩んでいました。
Aさん:私は、かつてバリバリのC言語プログラマだったのに、Javaを一向にマスターできないんです。
筆者:ええっ! 貴方ほどの方がなぜ?
Aさん:クラスとオブジェクトが違うってことが、どうにもわからないからです。クッキー型とクッキーの関係だなんて例え話をされても、まったく理解できないわ。
筆者:それじゃあ、こういう例ではどうでしょう。社員というクラスを定義して、AさんやBさんのように具体的な社員の数だけクラスのコピーをメモリ上にロードする。これらがオブジェクトだってのは? 1つのクラスから複数のオブジェクトを作れるようにするため、クラスという定義とオブジェクトという実体を分けているわけです。
Aさん:ごめんなさい。やっぱり、わからないわ。
筆者:え~ん(これじゃあ講師失格だ)。
こりゃもう「Javaを作った人が決めた約束事なんだから、あれこれ悩まずに納得してください」と言うしかないですね。意外と、この説明でわかってもらえちゃったりして。皆さんは、クラスとオブジェクトの違いわかりますよね。1つのクラスから、複数のオブジェクトを作れるってことも大丈夫ですね。
さてさて、場合によっては、1つのクラスから1つのオブジェクトだけをしか作れないように制限したいこともあるでしょう。たとえば、プログラムを実行しているコンピュータやOSなどを表すクラスです。このようなクラスのオブジェクトは、1つだけ作って共有すべきですね。それを実現するのが、Singleton(一人っ子)パターンです。
どのような工夫なのかを理解していただくには、くどくど言葉で説明するより、サンプルコードを見せちゃった方が早いでしょう。リスト1[拡大表示]をご覧ください。これは、コンピュータを表すMyComputerクラスです。フィールドとして、コンピュータ名を表すcomputerNameとIPアドレスを表すipAddressなどがあります。普通のクラスなら MyComputer obj1 = new MyComputer(); や MyComputer obj2 = new MyComputer(); のようにして、いくつでもオブジェクトを生成できますが、MyComputerクラスのコンストラクタに注目してください。private(クラスの外部から利用できない)になっていますね。したがって、このクラス は、newを使ってオブジェクトを生成できないのです。
それでは、どうやってオブジェクトを生成するのかと言うと、専用のメソッドを用意しておくのです。ここでは、getObjectメソッドが、それです。最初にgetObjectメソッドが呼び出されたときは、newを使って自分自身のオブジェクトを生成して返し、その参照(アドレス)をmyObjectフィールドに保持しておきます。コンストラクタがprivateでも、クラスの中からなら使えます。2回目以降getObjectメソッドが呼び出されたときは、既に生成済みのオブジェクトの参照を返します。
リスト1●これならオブジェクトを1つしか作れない
public class MyComputer { public String computerName; // コンピュータ名 public String ipAddress; // IPアドレス private static MyComputer myObject; // 唯一のオブジェクトへの参照 // 唯一のオブジェクトを返すメソッド public static myComputer getObject() { if (myObject == null) { // 唯一のオブジェクトを生成する myObject = new MyComputer(); } return myObject; } // プライベートなコンストラクタ private MyComputer() { this.computerName = "PC1234"; this.ipAddress = "xxx.xxx.xxx.xxx"; } } |
「Singletonパターンは、なかなか上手い工夫だが、フィールドをstaticにすれば同じ効果が得られるのでは?」と思われる方もいらっしゃるでしょう。実は、私も同感です。もしも、私なりにMyComputerクラスを作るとしたら、フィールドをstaticにしてMyComputer.computerNameやMyComputer.ipAddressという構文で共有できるようにするでしょう。その方が簡単なはずです。
それでは、Singletonパターンを使わなければできないことは何でしょう? それは、オブジェクトの数を1つではなく、2つや3つのように特定の数に制限することです(リスト1を改造すればできます)。staticを使った場合は、1つに制限することしかできません。さらに、もう1つSingletonパターンのメリットがあります。それは、オブジェクトの生成タイミングを制御できることです。getObjectを呼び出さない限りオブジェクトは生成されないのです。とは言え「これは素晴らしいパターンだ!」と歓喜して使う状況は、滅多にないと思いますので、私の独断と偏見による評価は星3つとさせていただきました。
【お役立ち度】★★★★★
●軽量オブジェクトを使いまわすFlyweightパターン
先ほどのAさんとの会話に戻ります。社員を表すShainクラスのメンバとして、氏名を表すshimeiフィールド、役職を表すyakushokuフィールド、およびそれらを操作するメソッドがあるとしましょう。社員名簿みたいなプログラムをイメージしてください。
Aさん:社員の数だけクラスのコピーをメモリ上にロードするのよね?
筆者:そうです。
Aさん:それじゃあ、社員が1000人いたら、メモリ上にオブジェクトが1000個もあるの?
筆者:そ、そうです。
Aさん:それって、無駄じゃないかしら。ロード時間もかかるし、メモリも圧迫されちゃうわよ。
筆者:う~む...言われてみれば、確かにそうですね。
Aさん:どうすればいいの?
筆者:さて、どうしましょう。あはははは(ますます講師失格だ)。
このような問題を解決する工夫が、Flyweightパターンです。小さな(Flyweight=軽量級)オブジェクトを数多くロードする状況では、ロード済みのオブジェクトを使いまわした方がプログラムのパフォーマンスが向上しますよというアイディアです。
社員の数が1000人であっても、役職の数は社長、専務、部長、課長、主任、担当の6種類だけだとしましょう。最初にShainクラスのオブジェクトを6つだけロードし、それぞれのyakushokuフィールドだけを設定しておきます。shimeiフィールドに設定するデータは、必要に応じてファイルから読み出せばよいのです(図1[拡大表示)。
「オブジェクト指向プログラミングは、現実世界をそのままプログラムに置き換えられる技法である」てなことをおっしゃる人がいます。「社員という概念がクラスで定義され、社員の実体がメモリ上のオブジェクトとなることは、正に現実世界と同じだ。メモリは、社員オブジェクトが働く会社みたいなものだ」というわけです。この考えを否定するつもりはありません。ただし、1000人の社員オブジェクトの例のように、それをそのままプログラムの構造として実現するのがナンセンスなこともあります。「夢物語のようなことを言ってないで、もっと冷静に考えましょう!」と諭してくれたFlyweightパターンには、星5つの満点をつけさせていただきます。
次回は、BridgeパターンとDecoratorパターンを紹介します。
矢沢久雄 グレープシティ株式会社(http://www.grapecity.com)アドバイザリースタッフ |
表紙ページへ |