概要
この投稿の構成をツリーにあらわしました。
- はじめに
- オブジェクト指向(OOP)とは
-
「責任」という観点から見たOOPと非OOP
- OOPの責任
- 非OOPの責任
-
オブジェクト指向特有の概念
- カプセル化
- 継承
- 抽象
- インターフェース
-
色々な違いを比較する
- 集約と継承の違い
- 継承と抽象とインターフェイスの違い
- virtualとabstractの違い
はじめに
初投稿ということで、簡単に経緯・目的を説明します。
これまで、ブランク有りで3年ほどJavaやvb.netなどのオブジェクト指向の言語にふれてきました。 オブジェクト指向を理解できてますか?と聞かれると、**自信無し**の状態。 そんな状態のまま、新たな職場でC#を扱うことになりさあ大変。 オブジェクト指向への理解へ向け、気持ち新たに意気揚々と勉強を開始しました・・・**が、しょっぱなで躓きました!** あれこれ情報収集するも「そもそも」がわからない状態。 いくら手段を覚えたところで、**『なぜそれが必要なのか』**というナゾループに陥ってしまい、説明がぜんぜん頭に入ってこない。 それでも、あれやこれやとひっしのぱっちで勉強すること4日が経過しました。 至らぬところも多々あると思いますが、現在の**なんとかここまで来た**という状況を記録します。読み飛ばしてもらっても大丈夫です!
オブジェクト指向(OOP)とは
OOPとは**オブジェクト指向プログラミング(OOP:object-oriented programming)の略。
OOPの考え方に、クラスという考え方がある。
クラスは、状態をあらわす「フィールド」と、ふるまいをあらわす「メソッド」**をメンバとして構成される。
これらはクラス内で扱われるため、管理が一元化できるというメリットがある。
「事象を擬人化する」プログラム。
クラスは設計書であり、それを実体化したものがオブジェクトという。
OOPの反対は、非OOPと呼ぶ。
OOPは、関数型や手続き型と並べられて比較されることが多い。
「責任」という観点から見たOOPと非OOP
岩谷 和男さんの投稿記事が私にはビンゴで、参考にさせて頂きました。
ポイントを抜粋してメモします。
OOPの責任
クラスの情報を、オブジェクト(またはインスタンス?)の生成から消滅まで保持し続けて、呼び出し元からの求めに応じて値を返却する、という責任を持つ
非OOPの責任
非OOPのプログラムは単純に「入力パラメータを受け取って出力結果を返す」という手続きの集合体。
クラスに所属する属性や処理という位置づけはなく、単に都度呼ばれる単発の関数のようなイメージ。
オブジェクト指向特有の概念
カプセル化
目的
- 他クラスから、自クラスのメンバ状態(フィールド)や振る舞い(メソッド)を隠蔽することで、クラスに含まれる状態や動作などの内容を意識せずに、使うことができる。
- 外部からの不必要な操作を制限することで、自由気ままにオブジェクトのフィールドの値を変えたり、メソッドを利用できないようにする。
- 色々見えすぎたら混乱するので、必要なものだけ見せるようにしている。
概要
- 必要最低限のメンバだけをpublicで公開し、そのほかのメンバをprivateで非公開にすることで、プログラムの**保守性(改造しやすさ)**を向上させることができる。
- そのほか、目的に合わせてアクセス修飾子を指定することができる。
継承
目的
- 親クラスのメンバ(フィールドとメソッド)を、子クラスでも使えるようにする。
- 親クラスの本質的な機能(または、階層構造において共通化できる機能)を、子クラスに受け継ぐことができる。
- 子クラス間で共通している機能を親に持たせて継承することで、コード量を少なくすることができる。
概要
- カテゴライズした際に、親子の関係をなすクラス間には「継承関係」があると言える。
- 単に、共通する振る舞いが多ければ継承関係があるというものでもないので、継承関係を指定する際には階層構造の見極めが必要となる。
- C#の場合、単一継承はできるが、多重継承はできないため、子クラスは複数の親クラスを指定できない。
- 親クラスのメンバをそのまま継承して使うこともできるし、子クラスで特有の動きや状態を保持する場合は、親のメンバをオーバーライドすることができる。
代表するアクセス修飾子
親クラスの修飾子 | 子クラスの修飾子 |
---|---|
virtual | override |
抽象
目的
- 上位クラスでは実装を必要せず、下位クラスで特殊な動作が必要とされる場合に、抽象化クラスを継承させることで、具体的な動作は下位クラスに委ねることができる。
概要
- ポリモーフィズム(多態性、多相性)を実現する手段の一つ。
- オーバーライドされることを前提としたクラスであり、継承されて始めて実態を持つ
- オーバーライドされることを前提のメンバだけでなく、処理が実装されたメソッドや、状態を保持するフィールドも含むことができる。
- 上位クラスのアクセス修飾子abstractが付く抽象メソッドは、下位クラス(具象クラス)で必ず定義・実装をしないと行けない。
代表するアクセス修飾子
親クラスの修飾子 | 子クラスの修飾子 |
---|---|
abstract | override |
インターフェース
目的
- 新たにサブクラスを追加する際、そこに含まれるメンバーに関する契約や約束事項を決めておきたいときに、インターフェースを実装する。
- インターフェース(契約)を実装して作られたクラスには、特定の機能が用意されている事が保証され、仕様が統一されていることで使いやすくなる。
概要
- ポリモーフィズム(多態性、多相性)を実現する手段の一つ。
- インターフェースは、継承するものではなく、実装するもの。
- インターフェースは、処理内容を持たない抽象メソッドだけで構成されるクラスで、フィールドはメンバに含めることができない。
- インターフェースの役割は**「ふるまい」の共通化**であり、コードの再利用ではない。
- インターフェースクラスを実装するクラスに対して、メソッドの実装強制をしている。
- インターフェースへのメンバーを追加することで、ライブラリやそれを利用する全てのクラスに対して破壊的な変更を発生させる足かせとなっていることが、昨今、問題視されている。
色々な違いを比較する
集約と継承の違い
- 継承関係にあるかどうかは関係なく、他クラスの機能をより集め、自クラスでそのまま使いたいことを、集約という。
- 集約とは、他のクラスの既存の機能をそのまま利用すること。
- 継承とは、他のある特定のクラスの機能を追加し、既存のクラスを拡張すること。
- 継承をis-a関連、集約をhas-a関連と呼ぶ。
継承と抽象とインターフェイスの違い
継承
- 親子関係にあるクラスにおいて、親クラスのメンバを子クラスでも使うことができる。
- 親のメンバを継承することもできれば、子クラスでユニークなふるまいをするメンバで、親クラスのメンバをオーバーライド(上書き)することもできる。
- 上位クラスの修飾子:virtual、下位クラスの修飾子:override
抽象
- 親子関係にあるクラスにおいて、親での実装したところで、子クラスのユニークなふるまいで完全にオーバーライドすることになる場合、上位クラスを抽象化させる。
- 実装を伴わないものだけでなく、実装を伴うメンバも含めることができる。
- 上位クラスの修飾子:abstract、下位クラスの修飾子:override
インターフェース
- フィールドはメンバに含めることができず、実装を伴わないメソッドのみをメンバとする。
- インターフェースを実装するクラスでは、その契約内容に含まれるメソッドの実装が強制される。
- 上位クラスの修飾子:interface
virtualとabstractの違い
virtual
- 継承関係にあるクラス間において、親クラスのメソッドを継承する場合もあれば、子クラスで実装したメンバを優先して呼び出すという場合、親のメンバーにはアクセス修飾子virtualをつける必要がある。
- ただ、overrideのつけ忘れのために、意図せず親のメンバが呼び出されてしまう恐れがある。
abstract
- 親クラスのメンバーの実装が不要で、継承先の子クラスで個々にユニークな動作を必要とする場合、親クラスのメンバにアクセス修飾子abstractをつける。
- 親クラスでabstractがついたメンバは、サブクラスで必ず定義(及び実装)をしないと行けない。
最後に
最後までご精読頂きありがとうございました。
私と同じ境遇の方も多いはずではないでしょうか?(私の周りには、4、5人いました...)
文字に起こして再認識しましたが、まだまだ理解を深めていくところ、あまた。
何においても、設計の際に、クラス間の階層構造や相関関係の分析を正しく行う。
これに尽きるのではないかと思いました。
今回投稿した内容が、皆様の勉強のちょっとしたヒントになればとてもうれしいです。
内容に関して、ご認識や間違った解釈していましたら、お気軽にご指摘やアドバイスのコメントお待ちしております。
参考サイト・参考書籍
OOPと非OOPの違い|オブジェクト指向プログラミング(OOP)をおさらいしよう(1)
JIS規格対応 標準C#入門 改訂第2版
C#で始めるプログラミング 導入編
C#で始めるプログラミング オブジェクト指向編
インターフェースを「契約」として見たときの問題点