Fujitsu The Possibilities are Infinite

 

オブジェクト指向プログラミングへの道
3日目:インターフェースの活用


知史

知史さん

航祐

航祐君

それから何日かたって…。 新人の航祐(こうすけ)君とそのチューターの知史(さとし)さんの会話です。

航祐君、オブジェクト指向言語に対する興味が増してきたようです。 今日もさっそく知史さんに質問しています。


航祐

「知史先輩、こんにちは。 この前はありがとうございました。 おかげでオブジェクト指向が少し判ったような気がします」


知史

「そうか、良かったね」


航祐

「ありがとうございます。 ところで前回問題になった料金計算ロジックの多様化はどのようにできるのか、教えていただけませんか」


知史

「そうだね。 前回は継承とオブジェクトコンポジションを使って、以下の3つの要件への対応を考えたんだったね」

  1. 着信中心のユーザや、ヘビーユーザ用の新プランの開設
  2. 通信相手や時間帯による通話料の相違など料金計算ロジックの多様化
  3. メールサービスや集金処理などまったく別の業務処理への拡張

航祐

「はい。 このうち1番目と3番目の要件には対応できました。 しかし2番目の要件は未解決でした」


知史

「そうだね。 前のプログラムでは、料金計算ロジック(chargeメソッド)をPlanクラスに共通に書いてしまった。 楽々プランクラスと得々プランクラスのそれぞれにロジックを書いておけばよかったのに、そうはしなかった」


航祐

「なぜ、そうしなかったのですか。 コピー&ペーストを使えば簡単ですよね」


知史

「そうだね。 コピペを使えば書くこと自身は簡単だ。 しかし、同じコードを複数箇所に書くと、バグが見つかったときに全部忘れずに修正しなければならない。 だからプログラムの保守性は悪くなるんだ。 できるだけコードの重複を避けることは、プログラマーの鉄則の1つだ。 良く覚えておくんだね」(注1)


航祐

「はい。 判りました」


知史

「しかし、共通化しようとすると拡張性は逆に下がってしまう。 つまり『共通化』と『拡張性』のトレードオフだ。 どちらを取るかは、将来ロジックに影響するような拡張が本当にあると思うか否かで判断するわけだね」


航祐

「なるほど」


知史

「拡張性を重視するなら、料金計算ロジックはサブクラスに書くべきだ。 しかしこのときは、単に並べて書くのではなく、オブジェクト指向言語ならではの工夫がある。 それはインターフェースの活用だ」


航祐

「Javaのインターフェースの機能を使うことは前回に聞いていましたので、少し調べてきました」


知史

「おお、それはいいね。 それでは、インターフェースについて説明してみて」


航祐

「はい。 インターフェースとはメソッドの宣言だけを集めたものです。 インターフェースはそのままでは使えず、他のクラスが実装して初めて使われるとのことです。 しかし、なんでこんなものが必要なのかは、よく判りません」


知史

「そうだね。 それでは実際にインターフェースを使って、以前のプログラムを書き直してみよう」

知史さんは、変更点を明確にしながらプログラムを書き直しました。


まず、元のプログラムを思い出してみましょう。 内容は同じなので得々プランクラスは省略してあります。

元のプログラム(Planクラス)
図解: 元のPlanクラス

元のプログラム(楽々プランクラス)
図解: 元の楽々プランクラス

これに対し、Planクラスをinterfaceにして内容を楽々プランクラスと、得々プランクラスに移します。 Planインターフェースにはcharge( )メソッドの宣言だけを残します。 楽々プランクラスと得々プランクラスではPlanインターフェースをimplementします。 このようにして作ったのが以下のリストです。

改良したプログラム(Planインターフェース)
図解: 改良したPlanインターフェース

改良したプログラム(楽々プランクラス)
図解: 改良した楽々プランクラス



航祐

「ふーん。 Planはクラスからインターフェースになって、ほとんど何も無くなってしまいましたね」


知史

「そうなんだ。 Planインターフェースには int charge( ); があるだけだね。 これは変数のように見えるが、名前の後に ( ) が付いているので、変数ではなくメソッドだ。 メソッドの宣言だけがあって、内容がないんだ」


航祐

「ああそうか。 普通メソッドは中括弧 { で始まるプログラムの実体があるのに、ここには何もありませんね」


知史

「そう。 メソッドの宣言だけとはこの様な意味だ。 そして航祐君が判らないと言っていたインターフェースの役割だけどね」


航祐

「はい」


知史

「インターフェースはメソッドの書式だけを集めた『仕様書』であると言えるんだ。 この例では引き数が無いchargeメソッドが1つ宣言されているだけなので、ありがたみがあまり判らないかもしれない。 しかし、複雑なクラスでは名前と引数構成がよく吟味されたインターフェースがうまく用意されていれば、プログラマーは要求されたメソッドを記述していくだけで望ましいクラス構成が実現できるわけだ」


航祐

「ふ~ん」


知史

「初めにインターフェースをきちんと設計しておき、それを実装する形でプログラムを書くのが優れた開発方法であると言われているんだよ」


航祐

「へ~、そうなんですか。 僕もきちんとインターフェースを定義して、うまいプログラムが設計できるように頑張ります」


知史

「おお、それは頼もしいね。 ところでこのプログラムの構造をクラス図に表してみよう」

知史さんは、新プログラムのクラス図を書きました。


(知史さんのプログラム1のクラス図)
図解: プログラム1のクラス図



航祐

「クラス図は前とあまり変わりませんね」


知史

「そうだね。 Planの前に《interface》が付き、Rakurakuプランクラスのメソッドにchargeが追加されただけだ。 《…》はステレオタイプと言ってUMLで規定されている表現方法だ。 対象物の意味や内容を説明するものだよ。 慣れればクラス図を見ただけで、クラス間の関係がはっきり判るようになる」


航祐

「しかし知史先輩、今は説明してもらったから判りますが、自分ではこんな複雑な構造のプログラムを考え出すことはできそうもありません」


知史

「そうかもしれないね。 実はそう考えた先人達がいて、このようによく使われて役に立つ構造を集めたものがあるんだ」


航祐

「え~、それは凄いですね。 どこにあるのですか」


知史

「それはデザインパターンというものだ。 GoF(Gang of Four:4人組)と言われる人たちが全部で23種のパターンを考えて、その使い方や注意点をまとめたんだよ。 ここで示した構造はその中のStrategyデザインパターンとして載っているんだ。 大きな本屋さんに行くとデザインパターンの本があるので、それで勉強するといい」


航祐

「それではさっそく帰りに本屋さんに寄って探して見ます」


知史

「うん、それがいいね。 GoFのオリジナル本も和訳があるが、それより日本人が書いた解説書の方が読みやすいかも知れないよ(注2)。 Strategyデザインパターンはアルゴリズムを交換するときに使うと説明されていて、どの本でも応用分野の紹介は少ないように思われるが、僕はこの例のようにもっと気軽に多様な場面で使える、素晴らしいパターンだと考えているんだ」


航祐

「ありがとうございました」


(注1) Springフレームワークを開発したことで有名なRod Johnsonはコード重複の問題点として次の点を指摘している。 (『実践J2EEシステムデザイン』SoftBank社 より)

  • コードが増える。
  • 読み手は何かの意図でコードを分けたと思い込み、比較のための時間を無駄にする。
  • 矛盾する実装の可能性がある(例えば一方は例外をログするが他方はしないなど)。
  • 修正時の変更箇所が増える。

(注2) 『オブジェクト指向における再利用のためのデザインパターン』Erich Gamma等著、SoftBank社:GoFのオリジナル本
『Java言語で学ぶデザインパターン入門』結城浩著、SoftBank社