(HASHIMOTO SOFTWARE CONSULTING)トップ
技術コラム
第1回:継承①〜科学的に継承を理解する準備-PART1
今回から「継承」について解説します。まずは「設計モデル」を対象として継承の解説をします。
モデルには「設計モデル」「概念モデル」「分析モデル」があります[注1-1]。
「設計モデル」は、「概念モデル」「分析モデル」とは異なり、プログラミング⾔語や動作させる環境
を考慮したモデルです(「概念モデル」「分析モデル」は特定のプログラミング言語や動作環境を考慮し
ないで問題空間の構造と振る舞いおよび制約に焦点をおいたモデルを作成します)[注1-2]。
「概念モデル」「分析モデル」と「設計モデル」ではモデル作成の目的や視点が異なるので、「継承」に対して異なる理論・定理・原理を利用します。
「概念モデル」「分析モデル」で継承をどのように考えてモデルを行うかについては、別の機会に解説したいと考えています。
[注1-1]:
モデルの名称には「設計モデル」「概念モデル」「分析モデル」以外に「実装モデル」というモデルが
あります。「実装モデル」は、多くの場合「設計モデル」の別名と考えてよいでしょう。
実はこれらのモデルの厳密な区別は存在しておらず、研究者や開発方法論提唱者が、自らの考えでモデ
ルの名称と定義を行っています。そして特に標準的な決まりはないのが実情です。
[注1-2]:
「設計モデル」あるいは「実装モデル」では、利用するプログラミング言語や動作環境を考慮してモデ
ルを作成します。今回のコラムでは不特定の読者の方を対象とするので、Java、C++、C#、Objective-
Cなどの特定の言語や、特定の動作環境を指定して解説はしません。そのため、本来の「設計モデル」あ
るいは「実装モデル」よりはモデルの記述内容の抽象度が高くなります。
「継承」の可能性を探る
今回と次回(第2回)は継承を科学的に理解するための準備の解説をします。
「継承」は「集約/コンポジッション」や「総称(ジェネリック/テンプレート)」と共に、オブジェ
クト指向でソフトウェアコンポーネントを組み立てる際の基本的なメカニズムを提供します。
効果的にオブジェクト指向設計を用いたければ、「継承」を効果的に利用することが重要です。
継承はソフトウエア開発技法や言語設計の専門家の間でも議論が多い技術テーマですが、継承を正確に
理解して設計と実装に利用できるようになると、劇的な効果を得る事が可能となります。
ただし、継承を理解するには、実に多くの関連技術事項を知らなければなりません。また、継承にも
用々な種類や戦略があります。プログラム言語の継承メカニズムの仕様も大きく関係します。これらの事
を踏まえて議論しなければ継承は語れません。
また、これらのことを一度に紹介し、解説することは困難なので、段階的にコラムの中で解説していきます。
今回は、継承の意味、価値、奥の深さを考える準備として、継承にどのような効果があるのか、代表的
な専門家の意見を取り上げ見ていきましょう。特に継承を積極的に利用することを提案している専門家の
意見に耳を傾け、継承の意味と効果を「科学的」に理解していく準備とすることにします。
「共変化(co-variant)」「逆変化(contra-variant)」「無変化(no-variant)」の継承
Ian Joynerは、彼の書著である文献[1-1]の中で、C++、JavaおよびEiffelを比較しながら継承の正し
い理解と利用が劇的にソフトウエアの設計と実装の能力を向上させることを力説しています。
Ian Joynerは、実システムの開発において、スーパークラスとサブクラスの間の継承関係では「is-a」
関係あるいは「kind-of」関係が成立するが、「共変化(co-variant)」による継承の利用が多いことを紹
介しています。一般に「共変化(co-variant)」による継承は、「多態(ポリモフィズム/多相)」が成立し
なくなりますが、「タイプ(型)安全」を保証することができます。
継承には、サブクラスから継承する操作(メソッド/関数)の引数のタイプ(型)や返値のタイプ(型)に
関する考え方のものがあるのです。
「共変化(co-variant)」はスーパークラスが型Aの引数を持っている場合、それをA型のままに保つ
か、Aの任意のサブクラスの型に再定義出来ることです。
「逆変化(contra-variant)」は、継承とは逆の方向に操作の引数のタイプ(型)が変化することを表しま
す。つまり、サブクラスで再定義された操作の引数の型が、スーパークラスの操作の型でなければなりま
せん。
「無変化(no-variant)」はJavaやC++で採用されており、引数の型をサブクラスで再定義できません。
なお、「共変化(co-variant)」「逆変化(contra-variant)」「無変化(no-variant)」は、操作の返り
値と引数に対して、別々に説明する必要がありますが、今回は割愛します[注1-3]。
以上の3種類は、プログラム言語の言語設計者が言語の「継承メカニズム」の戦略を踏まえて決定して
います。逆に言えば、言語毎に継承に対する考え方がかなり異なるとも言えます。
【図1-2】を使って簡単に継承の「共変化(co-variant)」「無変化(no-variant)」の概念を紹介いたします。
クラス「animal」からクラス「person」は継承しています。「共変化(co-variant)」「無変化(no-
variant)」は、操作のシグネイチャに関する考え方ですので、説明の都合上、通常はクラス図に表記しな
い対象オブジェクトへのポインタ変数(C++やJavaではthis、Eiffelではself)を表示しています。
さて、クラス「animal」は、操作「pair」を持っています。引数はanimalタイプ(型)のthisとanimalタイプ(型)のpartnerです。
サブクラス「person」は、この操作を継承しています。C++やJavaでは継承する操作の引数のタイプ
(型)は変更できません(thisポインタやselfポインタは別)。
【図1-2】を見るとサブクラス「person」において、この操作の対象オブジェクトを示すthisポインタ
は、personタイプ(型)になっていますが、partnerのタイプ(型)はanimalタイプ(型)で変化していません。
しかし、サブクラス「person」にとってpartnerのタイプ(型)がanimalタイプ(型)では不都合です。人
間の結婚相手は同じ人間であるparsonタイプ(型)である必要があります。そのため、サブクラス
「person」において、この操作のpartnerのタイプ(型)をanimalタイプ(型)からparsonタイプ(型)に変更
するという考えもあって当然です。サブクラス「person」の操作のpartnerのタイプ(型)が、animalタイ
プ(型)では人間以外のオブジェクトが引数に渡される不適切なケースが有り得ます。
オブジェクト指向プログラム言語の中には、サブクラス側でスーパークラスから継承する操作の引数の
タイプ(型)を継承が深くなる方向に準じて、タイプ(型)を変化できる「共変化(co-variant)」を採用して
いる言語もあります(代表例はEiffel)。操作を継承する際に「共変化(co-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パターン、
Mediatorパターン、Observerパターン、Template Methodパターンなどの「共変化(co-variant)」のパ
ターンを使う時は、共変化(co-variant)」の継承メカニズムを補足する技法を併用することが求められます。
「共変化(co-variant)」「逆変化(contra-variant)」「無変化(no-variant)」の継承や、共変化(co-
variant)」の継承メカニズムを補足する技法の詳しい解説は、機会を改めて本コラムで詳しく解説しま
す。今回はこのような継承の種類が存在するのだということを理解して頂ければ結構です。
[注1-3]:
C++は返値のタイプ(型)は、仕様が変更されて共変的に変化させることが可能となりました。
「開放閉鎖原理(Open-Close Principle)」Bertrand Meyerは、極めて重要な設計原理「開放閉鎖原理(Open-Close Principle)」
を最初に提唱した人として知られています。Bertrand Meyerは有名なオブジェクト指向言語Eiffelの設計者です。
Bertrand Meyerは文献[1-2]及び[1-3]の中で、継承の有用性を詳しく述べています。
「開放閉鎖原理(Open-Close Principle)」は、今後のコラムで詳細に解説しますが、今回は簡単に紹介します。
開放閉鎖原理(Open-Close Principle)」
Bertrand Meyerは、極めて重要な設計原理「開放閉鎖原理(Open-Close Principle)」を最初に提唱した人として知られています。
Bertrand Meyerは有名なオブジェクト指向言語Eiffelの設計者です。
Bertrand Meyerは文献[1-2]及び[1-3]の中で、継承の有用性を詳しく述べています。
「開放閉鎖原理(Open-Close Principle)」は、今後のコラムで詳細に解説しますが、今回は簡単に紹介します。
「拡張可能で開放されている」とは開発者がクラス(orコンポーネントorモジュール)の振る舞い(機
能)を変更(修正)できることを意味し、「閉鎖されている」とは、クライアントがクラス(orコンポーネントorモジュール)
を利用できることを意味します。
「閉鎖されている」については、設計レベルと実装レベルに分けて説明します。
クライアントは【図1-3】のクラス「client」が該当します。クラス「supperClass」の利用者です。
「拡張可能で開放されている」ということが要求されるのはクラス「supperClass」です。
設計レベルにおいて「閉鎖されている」の意味は、クラス「supperClass」のインタフェース(操作/
メソッド/関数)が安定(修正が無い)していて、クラス「client」に対して修正が不要であることを意味します。
実装レベルにおいて「閉鎖されている」の意味は、クラス「supperClass」がコンパイル可能で、クラ
ス「client」から利用可能できることを意味します。
結局、この原則は【図1-3】のクラス「client」に全く影響を与えず、クラス「supperClass」の機能を
修正あるいは拡張できると述べているのですが、より具体的に言えば、「開放閉鎖原理(Open-Close
Principle)」を遵守して設計と実装を行う事で、システム開発中あるいは出荷後のコードに対してソース
コードを変更せずに機能修正や機能追加を行うことができることを意味します。
つまり、試験中や動作中の既存ソースコードを変更せずに、機能修正や機能追加が可能になります。
おそらく初めて聞くときは「そのようなことが可能なのか?」と思うことでしょう。
「開放閉鎖原理(Open-Close Principle)」を遵守するには、クラス「supperClass」のインタフェース
(操作/メソッド/関数)を変更しては「開放閉鎖原理(Open-Close Principle)」を適用できません。
「開放閉鎖原理(Open-Close Principle)」を適用するには、クラス「supperClass」のインタフェース
(操作/メソッド/関数)の名称やシグネイチャが変更されないという条件が付きます。
「開放閉鎖原理(Open-Close Principle)」の理解と適用のポイントは継承にあります。
【図1-3】でクラス「supperClass」には、サブクラスが存在しています。クラス「supperClass」のイ
ンタフェース(操作/メソッド/関数)が変更せず、機能拡張や機能の修正はサブクラスで実施するとい
うものです。
さらに「開放閉鎖原理(Open-Close Principle)」の理解と適用で重要となるのは、別の重要な原理である
「振る舞いタイプ(型)置換原理」を併用することになります。「振る舞いタイプ(型)置換原理」が理
解できなと「開放閉鎖原理(Open-Close Principle)」を真の意味で使いこなすことは困難です。
なお、「振る舞いタイプ(型)置換原理」は、別の機会に詳しく解説します。
それから「開放閉鎖原理(Open-Close Principle)」は、コードの試験や構成管理のやり方にも影響を与
えます。試験や構成管理作業も「開放閉鎖原理(Open-Close Principle)」と同調させることでより効果的
な開発が可能となります。「開放閉鎖原理(Open-Close Principle)」は単なる設計や実装のテクニックと
いうよりも、ソフトウエア開発作業全般に影響を与える原理となっています。
試験および構成管理を含めた「開放閉鎖原理(Open-Close Principle)」に興味のある方は文献[1-2][1-3]を参照してください。
今回は「開放閉鎖原理(Open-Close Principle)」についての紹介はここまでとします。
「開放閉鎖原理(Open-Close Principle)」の価値を理解し、使いこなすには継承に対して、もっと多くの
ことを理解する必要があります。後述する【表1-3】にまとめてあります。
今後、【表1-3】に登場する内容をコラムで解説する際に、あらためて「開放閉鎖原理(Open-Close
Principle)」も一緒に詳しく解説します
参考文献
文献[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-basedInheritance
文献[1-5] [Peter Wegner 1991]Concepts and Paradigm of Object-Oriented Programmin
文献[1-6] [Erich Gamma,その他 1994]Design Patterns: Elements of Reusable Object-Oriented Software (邦訳「オブジェクト指向における再利用のためのデザインパターン」)