Processingで学ぶ 実践的プログラミング専門課程

第17回 継承と委譲

この記事を読むのに必要な時間:およそ 4 分

導入

オブジェクト指向のプログラミング言語の機能の中でも目玉と呼べるものが「継承」です。しかしながら継承によって発生する可能性があるデメリットが大きいため,Java言語やProcessingでは使用方法に制限が加えられています。前回は,継承で行うことができる機能を部分的に実現する方法であるインターフェイスを学びました。今回は,継承を用いずに継承で行うことができる機能を実現するもう1つの工夫,委譲(いじょう)を紹介します。

展開

必要な機能を持っているクラスを利用する仕組みが継承と委譲

継承や委譲を使う基本的なストーリーは次の2通りが考えられます。

  • あるクラスAを作成した後に,そのクラスAの機能と同じ機能を持ち,さらに別の機能を持つクラスBを作成したくなったとします。このような場合に継承や委譲を用います。

  • あるいは,あるクラスAとBが同じ機能を持つ良く似たコードを持っているとします。この良く似た部分のコードを切り出してクラスCにまとめるリファクタリングを行います。そしてクラスCを継承,あるいは委譲するクラスA'とB'を作成します。

どちらも複数のクラスの間に,包含関係や依存関係を上手に設定することを目的としています。上手に設定することでコードの見通しが良くなり,その結果つくりやすく,メンテナンスのしやすいコードになるのです。

単純に継承したクラスと,著作者名情報を取得するメソッドを追加したクラスをつくる

これから紹介するサンプルコードは,先ほど紹介したストーリーが両方適用されたものです。 連載第16回ではインターフェイスを活用して設定ファイルの情報を読み込むクラスをつくりました。 連載第16回演習2の解答例で掲載したコードを,インターフェイスを使わず継承を利用して再作成してみます。解答例のクラスから設定情報を読み込むコードをクラスに切り出しConfigInfoクラスとします。そしてこれを継承したAppInfoLoader8クラスを作成します。さらに新しいクラスAppInfoLoader9には著作者情報を持たせ,それを取得するメソッドを付加します。これらをテストするスケッチをTestAppInfoLoader6.pdeとします。

以下のファイルはTestAppInfoLoader6フォルダに納めてください。CONFIG.TXTSETTING.TXTはさらにその中のdataフォルダに納めてください。

「インターフェイスの実装」「クラスの継承」の最も大きな違いでありメリットは,クラスの継承の場合はスーパークラス側で実装済みのメソッドを利用できることです。AppInfoLoader8クラスはコンストラクタとgetClassNameメソッドしか持ちません。それにもかかわらず,スーパークラスであるConfigInfoクラスのメソッドを呼び出して使うことができます。目的の仕事をするメソッドをスーパークラスが持っていれば,サブクラスは特にコードを追加すること無く利用できます。これは便利な仕組みです。

さらに追加の機能が欲しくなった場合,新しいメソッドをサブクラスに書き込みます。クラスAppInfoLoader9では,クラスConfigInfoを継承した後に著作者名を取得するための新しいメソッドgetAuthorNameを追加しています。

プロトタイピング中など,コードが発展途上にある場合や,そもそもどんなことができるか不明瞭な場合は,このように継承したクラスを発展させていき,やがて重要なメソッドが明らかになればスーパークラスのConfigInfoクラスへ吸収し,ConfigInfoクラスを継承するクラスを再構成するのです。

緻密な設計を必要とするクリティカルなシステムのコードではこんな行き当たりばったりな方法論は通用しませんが,私たちが今利用している言語はProcessingです。アートやプロトタイピング,そして本来は教育に適した言語です。思う存分行き当たりばったりにコーディングしましょう。やがて勘がつかめれば,洗練された設計を行うスキルを効率良く身につけられるはずです。今,Processingを使う私たちは,それとは別の「作品作り」に注力しましょう。

継承した後,今あるメソッドをオーバーライドする

クラスConfigInfoとクラスAppInfoLoader8AppInfoLoader9を比較すると,同じ名前のメソッドgetClassNameが定義されています。

クラスConfigInfogetClassNameメソッド

  private   final String CLASS_NAME      = "ConfigInfo";
  
  public String getClassName(){
    return CLASS_NAME;  
  }

クラスAppInfoLoader8getClassNameメソッド

  public static final String CLASS_NAME      = "AppInfoLoader8";

// 中略

  public String getClassName(){
    return CLASS_NAME;
  }

このように,サブクラスでスーパークラスと同じメソッドを再定義することをオーバーライドと呼びます。 クラスConfigInfoを単体で使用する際には,getClassNameメソッドでクラス名を取得すると,クラスConfigInfoで定義されている定数CLASS_NAMEの値ConfigInfoが取得されます。

クラスAppInfoLoader8を使用する際には,オーバーライドしたgetClassNameメソッドでクラス名を取得すると,クラスAppInfoLoader8で定義されている定数CLASS_NAMEの値AppInfoLoader8が取得されます。 2つのクラスを使うコードの側から見ると,至って自然な挙動です。メソッドを「上書き」したわけですから,サブクラスのインスタンスを利用しているのに,スーパークラス側のメソッドが優先されては困ります。クラスAppInfoLoader8のインスタンスのスーパークラスのクラス名が必要ならば,クラスAppInfoLoader8内でsuper.getClassName()と呼べば,スーパークラスのクラス名が得られます。

著者プロフィール

平田敦(ひらたあつし)

地方都市の公立工業高等学校教諭。趣味はプログラミングと日本の端っこ踏破旅行。やがては結城浩氏のような仕事をしたいと妄想している。

Twitter : @hirata_atsushi

コメント

コメントの記入