Skip to content

Instantly share code, notes, and snippets.

@yoppi
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yoppi/315593a10262d21848fa to your computer and use it in GitHub Desktop.
Save yoppi/315593a10262d21848fa to your computer and use it in GitHub Desktop.
オブジェクト指向入門 - 15章 1〜2

15章 多重継承

  • 継承をより完全なものにするには複数の親を持てるように拡張する必要がある
    • 抽象化することが目的

15.1 多重継承の例

15.1.1 導入例として使うべきでないもの

  • 大学システムのモデルにおけるTAの例(生徒であると同時に先生でもある)は多重継承の入門例としてはふさわしくない。
  • 正確なグラフを書くと反復継承(いわゆるダイヤモンド継承)にあたる
  • 多重継承に習熟してから反復継承は学ぶべき
  • 一般的な多重継承であれば衝突は 一切 発生しない(衝突が発生することが前提であると捉えない)

15.1.2 飛行機は資産といえるか?

社用飛行機モデルを多重継承で表現するのは典型的な例だ。

  • PLANEクラス(飛行機モデル。飛行機を操作したり、クエリを持っている)
  • ASSETクラス(資産モデル。会社の資産を扱う)

の2つのクラスを継承することで表現できる。

class COMPANY_PLANE inherit
  PLANE
  ASSET
feature
  -- 社用機に特有の特性
end

他の例として、

  • 腕時計
  • 水陸両用車
  • 食堂車
  • ソファーベッド
  • トレーラハウス

等。

15.1.3 数値と比較可能な値

  • カーネルライブラリクラスの幾つか(例えばINTEGER、REAL、DOUBLE)には算術的な特性(値、操作)が必要 また、アプリケーション特有のクラス(行列計算のためのMATRIXクラス)でも同様な算術計算が欲しい。
    • そういった算術特性を暫定クラスNUMERICを使う
  • 順序特性(任意の要素を比較するための特性)が欲しいクラスもある
    • STRINGや、アプリケーションクラスでも役に立つ
    • 暫定クラスCOMPARABLEを使う
  • COMPARABLEのすべての子孫がNUMERICの子孫というわけではない
    • 逆にNUMERICの子孫がCOMPARABLEというわけではない
  • 比較可能であると同時に数値でもあるクラスはNUMERIC、COMPARABLEを継承する

15.1.4 ウィンドウは木構造であると同時に長方形でもある

ウィンドウシステムは、

  • 階層構造のメンバとしてウィンドウを扱い
  • 図形オブジェクトである

の特性がある。 これらの特性を抽象化する。

  • 階層構造は、TREE
  • 長方形オブジェクトは、RECTANGLE
class WINDOW inherit
  TREE[WINDOW]
  RECTANGLE
feature
  -- 特定のウィンドウ特性
end
  • TREEは総称クラスとなる(Generics。Java語でいうとTree)

15.1.5 木構造はリストであると同時にリスト要素でもある

  • 総称クラスであるTREEもまた多重継承で表現できる
    • リスト(LIST)の特性(この数を調べる、子を追加する、子を削除する等)を持ち
    • リストの要素(CELL)の特性(兄弟を取り出す、親にアタッチする等)を持つ
class TREE[G] inherit
  LIST[G]
  CELL[G]
feature
  -- TREE特有の特性
end

15.1.6 複合図形

  • 単一の図形を組み合わせたものを表現する[COMPOSITE_FIGURE]
    • 単一の図形[FIGURE]
      • これもまたあらゆる特性をもったクラスを継承している
    • 図形をまとめるデータ構造[LINKED_LIST]
class COMPOSITE_FIGURE inherit
  FIGURE
  LINKED_LIST[FIGURE]
feature
  -- ...
end
  • 図形を表示するオペレータdisplayrotatetranslateといったCOMPOSITE_FIGUREの特性それぞれは順番にFIGUREを操作する
    • 「イテレータ」クラスをさらに作って抽象化可能である
  • [超訳]つまるところ継承による抽象化はパワフルである、ということがいいたい

15.1.7 政略結婚

  • 多重継承の重要な使い方は、暫定クラスで定義された抽象的概念を有効クラスで提供された特性を使って実装することである
  • スタッククラスを配列で実装する(ARRAYED_STACK)
    • STACKクラス(暫定クラス)
    • ARRAYクラス(有効クラス)
  • この暫定クラスの特性を、有効クラスの特性で実装するための多重継承を「政略結婚」と呼ぶ
    • STACKクラスは暫定クラスだが貴族出身。富はなくとも高い名声を持つ
    • ARRAYクラス有効クラスで、その富と権力に見合う名声が欲しい

15.1.8 構造継承

  • 多重継承はあるクラスに、そのクラスを表す基本の抽象的概念以外にもいくつかの性質があることを明記したいときになくてはならない技術である
  • オブジェクトの構造を永久的なものにする(ストレージに保存可能)性質を実装する
    • STORABLEクラスを継承することでストレージに保存可能になる
    • が、それらは他のクラスを親に持っているはずであるので多重継承なしには機能しない
  • クラス名が「-ABLE」で終わるものはこういった汎用的な構造の性質を表すクラス

15.1.9 機能継承

  • 「履歴」機能
  • 「テスト」機能

これらの機能を使いたい時、それぞれ機能を実装しているクラスを継承する

15.1.10 ボタンホール

その新しいクラス自身の特性は特に定義することはないが、継承したそれぞれの親クラスとして振る舞う 複数の親からの特性と性質を単に結合しただけ

  • BUTTONクラスとHOLEクラスを継承したBUTTON_HOLEクラス
    • 右クリックするとピック&スロー機能
    • 左クリックするとボタン機能

15.1.11 評価

  • 多重継承はパワフルだ(再)
  • 多重継承ではなく、単一継承で2つの抽象的概念を結合するのは、コピー&ペーストでの複製を意味する
    • 多相性、開放/閉鎖の原則、継承の再利用性がもたらすメリットをすべて失う

これは受け入れがたいことである

15.2 特性の改名

多重継承には、「名前の衝突」という技術的な問題が生じる。 この問題の解決方法は、特性の改名(feature renaming)である。

15.2.1 名前の衝突

  • クラスは親の特性全てに、明示せずにアクセス可能である
class SANTA_BARBARA inherit
  LONDON
  NEW_YORK
feature
  -- ...
end

LONDONとNEW_YORKに同じ名前の特性fooがある場合どうしたらいいのか?

  • 親クラスであるLONDONやNEW_YORKを修正する、という手は避けるべきである
    • それらのソースにはアクセスできない場合や、アクセスできても変更できない場合、変更できたとしてもバージョンアップのたびに変更するのは辛い
    • 開放/閉鎖の原則(モジュールを再利用して新規に拡張する場合そのモジュールを変更してはいけない)
  • 継承時に特性名を変更することで解決する
class SANTA_BARBARA inherit
  LONDON
    rename foo as fog end
  NEW_YORK
feature
  -- ...
end
  • LONDONから継承した特性fooはfogとして、NEW_YORKから継承した特性fooはfooとして参照可能
    • LONDONの顧客はそのままfooとして使用できる

15.2.2 改名の効果

class SANTA_BARBARA inherit
  LONDON
    rename foo as fog end
  NEW_YORK
    rename foo as zoo end
feature
  -- ...
end

について効果を考える。

  • 名前の衝突問題の本質は「名前は恣意的(その時々の思いつきで物事を判断する)なものである」こと
    • つまり適切な名前を付けることで問題を解決できる

まさかとおもうだろうが、この問題を「奥の深い意味的問題」であるという人もいるらしい。 意味的でも意味深でもない。むしろ、単純な構文的問題である。 ローカルな文脈で、クラス作成者の一人が最初のクラスで名前fogを選び、次のクラスでzooを選んでいたならば名前の衝突はおきない。 いずれの場合も、1文字かわっただけであるが。 このように名前の衝突は不運なケースである。

15.2.3 改名と再宣言

  • 再宣言では特性が変わるが、名前は変わらない
  • 解明では名前が変わるが、特性は変わらない

15.2.4 ローカル名の適合性

継承された特性を改名できるということは、名前の衝突がない場合においても有用な手段である。

  • 継承した特性は有用であるが、名前が適切でない場合(そのクラスの抽象的概念にそぐわない)

たとえば、WINDOWクラス(TREEクラスを継承)を考える。 WINDOWインスタンスに対するインタフェースとして、TREEクラスの特性の名前は適切ではない

class WINDOW inherit
  TREE[WINDOW]
    rename
      child as subwindow, is_leaf as is_terminal, root as screen,
      arity as child_count,
  RECTANGLE
feature
  -- ...
end
  • WINDOWクラスにおける抽象的概念に合わせて改名する

15.2.5 名前のゲーム

  • オブジェクト志向ソフトウェア構築において、名前付け(特製名だけでなくクラス名)はとても大切
  • 全体的な名前をつけることは、だいたい良い手
  • しかし、WINDOWクラスの例のように、それぞれのクラスの抽象的概念にそぐわないときは特性名を改名する柔軟性を持つこと

15.2.6 親の生成プロシージャを使う

改名された特性が生成プロシージャである場合 たとえば、ARRAYED_STACKを例に考えてみる。

  • プロシージャ名として標準的にmakeを使う
  • ARRAYは有効クラスですでにmakeが定義されている
  • そこでARRAYクラスのmakearray_makeに改名する
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment