第1回:継承①〜科学的に継承を理解する準備
今回から「継承」について解説します。まずは「設計モデル」を対象として継承の解説をします。モデ ルには「設計モデル」「概念モデル」「分析モデル」があります。 「設計モデル」は、「概念モデル」「分析モデル」とは異なり、プログラミング⾔語や動作させる環境 を考慮したモデルです。(「概念モデル」「分析モデル」は特定のプログラミング⾔語や動作環境を考慮 しな いで問題空間の構造と振る舞いおよび制約に焦点をおいたモデルを作成します) 現実の対象 概念モデル問題空間
【図1-1】 「概念モデル」「分析モデル」と「設計モデル」ではモデル作成の目的や視点が異なるので、「継承」 に対して異なる理論・定理・原理を利⽤します。 「概念モデル」「分析モデル」で継承をどのように考えてモデルを⾏うかについては、別の機会に解説し たいと考えています。「継承」の可能性を探る
今回は継承を科学的に理解するために準備の解説をします。 「継承」は「集約/コンポジッション」や「総称(ジェネリック/テンプレート)」と共に,オブジェク ト指向でソフトウェアコンポーネンを組み⽴てる際の基本的なメカニズムを提供します。 効果的にオブジェクト指向設計をおこないたければ、「継承」を効果的に利⽤することが重要です。 継承はソフトウエア開発技法や⾔語設計の専門家の間でも議論が多い技術テーマですが、継承を正確に 理解して設計と実装に利⽤できるようになると、劇的な効果を得る事が可能となります。 具体的にはどのような効果があるのか代表的な専門家の意⾒を取り上げ⾒ていきましょう。特に継承を 積極的に利⽤することを提案している専門家の意⾒に⽿を傾け、継承の意味と効果を「科学的」に理解し ていく準備とすることにします。継承を積極的に利⽤することを推奨する専門家の主張 正しい設計を⾏うには継承を正確に使⽤する事が極めて重要である[Ian Joyner]
「開放閉鎖原理(Open-Close Principle)」を可能とするのは継承だけである[Bertrand Meyer] 【表1-1】 「共変化(co-variant)」「逆変化(contra-variant)」「無変化(no-variant)」の継承 Ian Joynerは、彼の書著である文献[1-1]の中で、C++JavaおよびEiffelを比較しながら継承の正しい 理解と利⽤が劇的にソフトウエアの設計と実装の能⼒を向上させることを⼒説しています。 Ian Joynerは、実システムの開発において、スーパークラスとサブクラスの間の継承関係では「is-a」 関係あるいは「kind-of」関係が成⽴するが、「共変化(co-variant)」による継承の利⽤が多いことを紹 介しています。 「共変化(co-variant)」はスーパークラスが型Aの引数を持っている場合,それをA型のままに保つか,A の任意のサブクラスの型に再定義出来ることです。 「共変化(co-variant)」の継承以外に、「逆変化(contra-variant)」であり、継承が逆の方向に変化す ることを表します。つまり、サブクラスで再定義された操作(メソッド/関数)の引数の型が、スーパー クラスの操作(メソッド/関数)の型でなければなりません。 「無変化(no-variant)」はJavaやC++で採⽤されており、引数の型をサブクラスで再定義できませ ん。なお、「共変化(co-variant)」「逆変化(contra-variant)」「無変化(no-variant)」は、操作(メソ ッド/関数の)返り値と引数に対して、別々に説明する必要がありますが、今回は割愛します。
Ian Joynerは、Erich Gamma達の有名なデザインパターンを掲載した書籍(文献[1-6])を例に挙げ、 「共変化(co-variant)」による継承を⽤いた多くのパターンが存在していることを述べています。具体的 には、Abstract Factoryパターン、Factory Methodパターン、Bridgeパターン、lteratorパターン、 Mediatorパターン、Observerパターン、Template Methodパターンなどが該当します。これらのパター ンが「共変化(co-variant)」の継承であることを理解した上で利⽤しないと品質や再利⽤に影響を与えま す。優れた開発が困難になるので要注意です。 なお、JavaやC++やC#をはじめとする広く利⽤されているオブジェクト指向⾔語は、直接「共変化 (co-variant)」の継承のメカニズムを採⽤していませんが、「共変化(co-variant)」の継承メカニズムを 補足する技法がいくつかあります。しかし、直接的に「共変化(co-variant)」の継承のメカニズムがない ので、Abstract Factoryパターン、Factory Methodパターン、Bridgeパターン、lteratorパターン、
ターンを使う時は、共変化(co-variant)」の継承メカニズムを補足する技法を併⽤することが求められま す。 「共変化(co-variant)」「逆変化(contra-variant)」「無変化(no-variant)」の継承や、共変化(co-variant)」の継承メカニズムを補足する技法の詳しい解説は、やはり機会を改めて本コラムで詳しく解説 します。 「開放閉鎖原理(Open-Close Principle)」
Bertrand Meyerは、極めて重要な設計原理「開放閉鎖原理(Open-Close Principle)」を最初に提唱 した人として知られています。Bertrand Meyerは有名なオブジェクト指向⾔語Eiffelの設計者です。 Bertrand Meyerは文献[1-2]及び[1-3]の中で、継承の有⽤性を詳しく述べています。 「開放閉鎖原理(Open-Close Principle)」は、今後のコラムで詳細に解説しますが、今回は簡単に紹介 していておきます。 「開放閉鎖原理(Open-Close Principle)」 クラス(orコンポーネントorモジュール)は「拡張可能で開放されている」こと かつ クラス(orコンポーネントorモジュール)は「閉鎖されている」のでクライアントから利⽤可能で あること 【表1-2】 「拡張可能で開放されている」とは開発者がクラス(orコンポーネントorモジュール)振る舞い(機能) を変更(修正)できることを意味し、「閉鎖されている」とは、クライアントがクラス(orコンポーネント orモジュール)を利⽤できることを意味します。 「閉鎖されている」については、設計レベルと実装レベルに分けて説明します。 クライアントは【図1-2】のクラス「client」が該当します。クラス「supperClass」の利⽤者です。 「拡張可能で開放されている」ということが要求されるのはクラス「supperClass」です。 設計レベルにおいて「閉鎖されている」の意味は、クラス「supperClass」のインタフェース(操作/ メソッド/関数)が安定(修正が無い)していて、クラス「client」に対して修正が不要であることを意 味します。 実装レベルにおいて「閉鎖されている」の意味は、クラス「supperClass」がコンパイル可能で、クラ ス「client」から利⽤可能できることを意味します。
結局、この原則は【図1-2】にクラス「client」に全く影響を与えず、クラス「supperClass」の機能を 修正あるいは拡張できると述べているのですが、より具体的に⾔えば、「開放閉鎖原理(Open-Close Principle)」を遵守して設計と実装を⾏う事で、システム開発中あるいは出荷後のコードに対してソース コードを変更せずに機能修正や機能追加を⾏うことができることを意味します。 つまり、試験中や動作中の既存ソースコードを変更せずに、機能修正や機能追加が可能になります。 おそらく初めて聞くときは「そのようなことが可能なのか?」と思うことでしょう。 「開放閉鎖原理(Open-Close Principle)」に遵守するには、クラス「supperClass」のインタフェース (操作/メソッド/関数)が変更をしては「開放閉鎖原理(Open-Close Principle)」を適⽤できません。 「開放閉鎖原理(Open-Close Principle)」を適⽤するには、クラス「supperClass」のインタフェース (操作/メソッド/関数)の名称やシグネイチャが変更されないという条件が付きます。 「開放閉鎖原理(Open-Close Principle)」の理解と適⽤のポイントは継承にあります。 【図1-2】でクラス「supperClass」には、サブクラスが存在しています。クラス「supperClass」のイ ンタフェース(操作/メソッド/関数)が変更せず、機能拡張や機能の修正はサブクラスで実施するとい うものです。 さらに「開放閉鎖原理(Open-Close Principle)」の理解と適⽤で重要となるのは、別の重要な原理である 「振る舞いタイプ(型)置換原理」を併⽤することになります。「振る舞いタイプ(型)置換原理」が理 解できなと「開放閉鎖原理(Open-Close Principle)」を真の意味で使いこなすことは困難です。 なお、「振る舞いタイプ(型)置換原理」は、本コラムで別に機会に詳しく解説します。
「拡張可能で開放されている」 同時に 「閉鎖されている」 機能の拡張はサブクラスで⾏う クラス「client」は影響を受けない 【図1-2】 それから「開放閉鎖原理(Open-Close Principle)」は、コードの試験や構成管理のやり方にも影響を与 えます。試験や構成管理作業も「開放閉鎖原理(Open-Close Principle)」と同調させることでより効果的 な開発が可能となります。「開放閉鎖原理(Open-Close Principle)」は単なる設計や実装のテクニックと いうよりも、ソフトウエア開発作業全般に影響を与える原理となっています。 試験および構成管理を含めた「開放閉鎖原理(Open-Close Principle)」に興味のある方は文献[1-2][1-3]を参照してください。 今回は「開放閉鎖原理(Open-Close Principle)」についての紹介はここまでとします。 「開放閉鎖原理(Open-Close Principle)」価値を理解し、使いこなすには継承に対して、もっと多くのこ とを理解する必要があります。後述する【表1-6】にまとめてあります。 今後、【表1-6】に登場する内容をコラムで解説する際に、あらためて「開放閉鎖原理(Open-Close Principle)」も一緒に詳しく解説します。 実装継承と多重継承の効果 【表1-3】の内容は巷の書籍やセミナー、トレーニングの継承の解説で良く⾒かけます。しかし継承の 解説が少々単純すぎています。もっと⾔えは、【表1-3】の説明は間違いではないものの適切とも⾔えな いガイドラインです。まだ継承になれていない方には良いかも知れません。
巷でよく聞く継承関係のガイドライン 正しい継承関係は、クラス間に「is-a」関係あるいは「kind-of」関係が成⽴するかどうかで判定する 「実装継承」は「is-a」関係あるいは「kind-of」関係が成⽴しない間違った継承関係である 「多重継承」は難しいので避け、委譲(delegation)を⽤いて単一継承で設計や実装をした方が良い 【表1-3】 最も基本的となる継承に「タイプ(型)継承(type inheritance)」という種類があります。この継承 と対をなすのが「実装継承(implementation inheritance)」です。
別の継承の分類として「単一継承(single inheritance)」と「多重継承(multiple inheritance)」 があります。
なお、本コラムをご覧なる方が、まだ「タイプ(型)継承(type inheritance)」「実装継承 (implementation inheritance)」「単一継承(single inheritance)」「多重継承(multiple
inheritance)」についてご存知ない場合は、分かる範囲で読み進めて頂ければ結構です。第3回以降で科学 的に継承を説明していく中で「タイプ(型)継承(type inheritance)」「実装継承(implementation inheritance)」「単一継承(single inheritance)」「多重継承(multiple inheritance)」を解説します。
さて、「多重継承(multiple inheritance)」や「実装継承(implementation inheritance)」は、多くの 場合あまり好ましく考えられていないようです。 時には強く「多重継承」や「実装継承」を戒めている書籍やセミナー、トレーニングもあるようで、 「多重継承」や「実装継承」は悪役の感じすら受けます。 C++やEiffelには「多重継承(multiple inheritance)」のメカニズムがサポートされていますが、Javaを はじめ多くのオブジェクト指向⾔語では「多重継承(multiple inheritance)」がサポートされていないこ とからも「多重継承(multiple inheritance)」があまり積極的に利⽤されない傾向にあることが分かりま す。これは⾔語設計やコンパイラー作成が複雑になるという理由もありますが、最も⼤きな理由は⾔語設 計時の「多重継承(multiple inheritance)」に対する重要性を反映したものでしょう。 一方、オブジェクト指向技術やプログラム⾔語の専門家達は、「多重継承」や「実装継承」をどのよう に継承を捉えているのでしょうか? 実は専門家の中には「多重継承」や「実装継承」[注1-2]は有⽤かつ重要と考えている人は多く存在し ます。
Bertrand Meyerは彼の書著(文献[1-3])の中で、彼独自の視点で「継承のテクニック」の分類を掲載 していいます(文献[1-3]の第24章)。 この分類の中で,継承を3つの⼤きなグループに分け、さらに12個に分類し継承の使い方を特定してい ています。Bertrand Meyerは12個の継承の分類が全て有⽤と考えています(【表1-4】)。この12個の 分離の中に「多重継承」や「実装継承」を利⽤した継承が含まれています。 ただし、この12個に分類された継承を理解するには、【表1-6】の内容を理解しなければなりません。 また、Bertrand Meyerの分類は、Bertrand Meyerが設計したオブジェクト指向⾔語Eiffelの機能を利 ⽤したときに可能な継承の分類になっています。オブジェクト指向⾔語Eiffelのメカニズムは、特性 (Eiffelではフィチャーと呼ぶ)ごとに細かな継承の設定が可能になる機能を持っています。 そのため、JavaやC++や他のオブジェクト指向では直接実現することが困難な継承もあります。 Bertrand Meyerの継承の分類 モデル継承(model inheritance) 部分型継承 ビュー継承 制約継承 拡張継承 ソフトウエア継承(software inheritance) 具体化継承 構造継承 実装継承 定数継承 マシン継承 バリエーション(variation inheritance) ファンクショナルバリエーション継承 タイプ(型)バリエーション継承 無効化継承 【表1-4】 Peter Wegnerも文献[1-5]において「実装継承」に実⽤的な価値があると考えていることを述べている 専門家です。 Bertrand Meyerは文献[1-2] [1-3]の中で「多重継承」を持つことをオブジェクト指向⾔語の定義に含 めており、「多重継承」が極めて自然な継承である事を述べています。
Gilad BrachaとWilliam Cookは文献[1-4]の中で「実装継承」の技法であるミックスイン[注1-3]につ いて「異なる複数のクラスから抽象的でないルーチンの集合を継承して新しい複雑なクラスを構築する能 ⼒がある」と有効性を述べています。
実装継承 (implementation inheritance) コードを再利⽤する目的の継承です。そのため実装継承は「コード継承 (code inheritance)」とも呼ばれます。スーパークラスとサブクラス間に 「is-a」関係や「a-kind-of」関係は必ずしも成⽴していません。 別の⾔い方をすればポリモフィズム(多相/多態)が出来ない継承です より正確に⾔えば、「タイプ置換原理」を満たさない継承となります ポリモフィズム(多相/多態)による利⽤をしなければ問題はありません。 詳しい事は改めてコラムで解説します ミックスイン (ミキシン) (mixin) ミックスイン(またはミキシンと呼ばれます)は、多重継承により異なる複 数のクラスから抽象的でないルーチン(操作/関数)の集合を継承して、新 しい複雑なクラスを構築することが可能になります ミックスインで作成されたクラスはミキシンクラス(mixin class)と呼ばれ ます スーパークラスとサブクラスに間に、通常は「is-a」関係や「a-kind-of」関 係は成⽴していません 多くのクラス間には「is-a」関係や「a-kind-of」関係は成⽴しなくても、再 利⽤できる機能(メソッド/操作/関数)が点在しているので、それらの機 能を効果的に再利⽤する技法で、コード作成の生産性と品質を向上すること が可能です 逆に⾔えば、既に存在する似た機能が別のクラスの中にあるのに、それを利 ⽤せず、同じ機能を複数実装することは冗⻑でありオブジェクト指向の精神 に反するとも考えられます ミックスインはプログラム⾔語の持つ継承や総称(ジェネリック/テンプレ ート)などの機能に依存するので⾔語によって実装のメカニズムが異なる点 は注意が必要です 【表1-5】
『科学的モデリング』で継承を正確に理解する
『科学的モデリング』の視点で継承を正確に理解し、効果的に利⽤するには、いくつかの重要な理論や 定理等を学習する事が必要です。 本コラムでは今後下記の重要事項を紹介しながら継承を解説していく予定です。継承に重要となる概念・理論・定理・原理 継承される特性(プロパティ)の種類 タイプ(型)安全な継承 クラスの2つの性質〜タイプ(型)とモジュール タイプ(型)置換原理(substitute principle) 実装継承(implementation inheritance)とタ イプ(型)継承(type inheritance) 共変化(co-variant) 逆変化(contra-variant) 無変化(no-variant) タイプ(型)の拡張(extension) タイプ(型)の制限(restriction) タイプ(型)の特殊化(specialization) タイプ(型)の再定義(override,redefine) タイプ(型)推論(type reasoning) モジュラー推論(modular reasoning) 構文継承(syntactic inheritance) 振る舞い継承(behavior inheritance) 安全なコンポーネント設計・実装および再利⽤ DesignByContract™(DbC:契約による設計) 継承を利⽤した原理群 【表1-6】
参考文献
文献[1-1] [Ian Joyner 1999] Objects Unencapsulated: Java, Eiffel and C++??(邦訳「オブジェクト指 向⾔語のはなし」)
文献[1-2] [Bertrand Meyer 1988] Object-Oriented Software Construction(邦訳「オブジェクト 指向入門」)
文献[1-3] [Bertrand Meyer 1997] Object-Oriented Software Construction(邦訳「オブジェクト 指向入門 第2版-原則・コンセプト」「オブジェクト指向入門 第2版-方法論・実践」)
文献[1-4] [Gilad Bracha and William Cook] Mixin-based Inheritance
文献[1-5] [Peter Wegner 1991] Concepts and Paradigm of Object-Oriented Programming
文献[1-6] [Erich Gamma,その他 1994]Design Patterns: Elements of Reusable Object-Oriented Software (邦訳「オブジェクト指向における再利⽤のためのデザインパターン」)