この記事は、
- C#とかかいてる
- オブジェクト指向完全に理解してる
- オブジェクト指向何もわからない
- そろそろ設計とか覚えたい
みたいな人を対象にしています。
なおコードは出てこないのでC#に限った話じゃないんですけど、せっかくの機会なので今まで小出しにしてた考えをまとめてみました。
今回はオブジェクト指向で用いる「概念」のみに焦点を当てた解説を心がけたので、プログラムの構成や運用などについては他の記事を併せて参考にしてください。
はじめてのオブジェクト指向
突然ですが、C#はオブジェクト指向プログラミング言語です。いえ、実態はマルチパラダイムですけど、標準ライブラリやAPIが基本的にはオブジェクト指向で書くことを要求してきます。
ところでみなさんは、オブジェクト指向って言葉を聞いたことはありますか?ない人もいるかもしれないので、説明しましょう!
オブジェクト指向っていうのは、_イヌとネコがワンワンニャーンしてアヒルがDuckで電子レンジが食品を解凍する概念_です。すごい!全く意味が分かりませんね?
せっかくなので、プログラミング言語の概念を復習しつつ理解していきましょう。ちなみに、この記事では↑の説明は一生理解できるようにならないので、ご了承ください。ください。
「オブジェクト」を知る
オブジェクト指向がどうのこうのという前に、まずは明らかにしておかないといけないのが、「用語の定義」です。汎用的な、即ち抽象的な概念を持ち込んでくるので、何の話をしてるのかを理解しておかないと詰みます。
あんまり聞き覚えのない言葉で説明することになりそうなので、頑張って理解してください!
まずは、
###「オブジェクト」とは何か?
オブジェクトは、オブジェクトです。(実際に見かけたことある説明)
オブジェクトというのは、プログラミング言語で取り扱える**「データ」の最小単位**です。1
はオブジェクトで、"abc"
もオブジェクトです。1
と"abc"
を合わせたデータもまたオブジェクトです。
言い換えると、そうですね、オブジェクトとは、「変数に入れられるモノ」です。それ以上でもそれ以下でもありません。
プログラムが計算するときに手で掴める「データ」の塊の単位なのです。
オブジェクトは……「存在」する……
あなたは今、宇宙にいます……。
オブジェクトはデータが存在する空間(即ちメモリ空間)に無数に散らばっています。たくさんあってどれがどれだか分からない?分からないままではプログラムで取り扱えない!それじゃあダメ!
「クラス」とは?
オブジェクトが宇宙空間に散らばった多様なデータの一つだとすると、プログラムで扱うにはそれらを「分類」する手段が必要になります。そう、オブジェクトはそれぞれが個性的な「性質」を持っているんです。
例えば、「四則演算ができる」という性質を持つオブジェクトを探して、丸で囲みます。この「丸」こそが、「クラス」です。四則演算ができるオブジェクトといえば、まずはint
ですね。
他には、文字列
を表す「丸」の中にあるオブジェクトは、「結合して一つにできる」「バラして1つの文字を入手できる」などの性質を持つ、ということになります。
性質は、受け継がれていくのだ……
ところで、この「丸」で囲まれたデータ群(A)
の中には、Aよりも小さな丸で囲まれたデータ(B)
が存在することがあります。このとき、「Bに囲まれたオブジェクトは、Aにもまた囲まれている」ということになります。これを「継承」と呼びます。
BはAでもあり、Aの中でもさらに限られた存在ということは、必然的にAよりも豊富な「性質」を持つことができます。即ち、BはAの持つ**「性質」を受け継いでいる**んですね。文字通りの「継承」なのです。
「インスタンス」とは?
宇宙空間に存在するオブジェクト、それは必ず何かしらの「丸」に囲まれています。そこで、ある一つのオブジェクトが例えば(A)
の丸に囲まれていることが明らかになったとき、それを「Aのインスタンス」であると呼びます。Aに囲まれているということは、そのオブジェクトがAの性質を持つことが保証されます。例えば、int
の丸に囲まれたオブジェクトは「四則演算に使える」ことが保証されるのです。
例えばC#でいうと、宇宙空間の全てのオブジェクトをもれなく包む大きな丸「object
」が存在します。なので、全てのオブジェクトは生まれながらにして_文字列になる性質(ToString)_を持っていることが保証されているんですね。
インスタンスを「生成」する
「インスタンス生成」というのは、何かしらの丸に囲まれた空間内に「オブジェクトを1個生成する」ことを指します。「インスタンス」とは「存在するモノ」であり、「インスタンス生成」は「生成するという作用」なので、これらを混同しないように注意する必要があります。
この生成されたオブジェクトが仮に(A)
の空間内に生成されたとすると、このインスタンスはAの性質を備えて生まれてくることになります。
「メソッド」とは?
オブジェクトの持つ「性質」そのものです。何かを変化させるもの、別のオブジェクトを探し出すもの、新しいオブジェクトを生み出すもの。そういった「変化を起こすモノ」を、メソッドと呼びます。
メソッドを「メッセージ」と呼ぶのは別の世界線のオブジェクト指向なので、今回は考えないことにします。
「プロパティ」とは?
ぼくたち神の目では、オブジェクトが「どの丸に含まれているのか」は分かりますが、そのオブジェクトがどのような状態のものなのかは分かりません。その状態を「覗き見ることができる」という「性質」を指して「プロパティ」と呼びます。
オブジェクト指向の流儀
以上がオブジェクト指向を構成する概念です。
ここまで全て、オブジェクト(==データ)が、どのようなモノであるかを指すものしか出てきていないことに気づいたでしょうか?
そうなんです。オブジェクト指向は、「データを性質によって分類し、階層的に管理する」という抽象化システムのことです。
オブジェクト指向は「データ至上主義」のことであり、「処理(計算)」のことには言及していないのですね。
データの性質を明らかにし、それを分類して管理する。データを生み出し、変化させ、組み合わせて、プログラムを作る。
それが、オブジェクト指向プログラミング、なのです。
オブジェクト指向をやると出てくる概念たち
カプセル化
「フィールド変数はプライベートにする」とか「ゲッターを経由してアクセスする」とかはオブジェクト指向を学んでいくと必ず出てくる話ですけど、これは一体どういう意味なのか?
オブジェクトっていうのは「特定の性質を持つデータの塊の一つ」です。
先ほどの説明に「フィールド」がなかったことに気づいたでしょうか。そうです。オブジェクト指向の考え方に「フィールド」というものは存在しません。フィールドの存在はオブジェクト指向の概念の「実装」のレイヤ上で現れるものです。
オブジェクトが、正しく「ただのオブジェクトであってほしい」ということを表現するためには、フィールドの存在は隠蔽しておいた方が都合が良いということになります。
カプセル化と同時に現れる概念である「ゲッター・セッター」の利用法を覚えるとこれらの意味が理解できるようになります。
ゲッター・セッター
ゲッターセッターなんて使わなくても変数をそのまま公開すればいいんじゃない?って思う人も多いと思います。実際、中身をそのまま公開してるならどっちであっても同じことができますね。何故ゲッターセッターなんてものを使うことを推奨しているのか?
オブジェクトの変数をそのまま公開すると、そのデータを変更する「責任」は変更する側にあります。つまり、データが正しくあるための責任がコード全体に分散します。これを、プライベートに隠蔽しゲッターセッターを介してアクセスするようにすると、そのデータを変更する「責任」はゲッターセッター、つまりその「オブジェクト自身」が持つことになります。このようにして、オブジェクトの状態(データ)が正しくあることの「責任」をその「オブジェクト自身に閉じ込める」こと、これがゲッターセッターを用いる最大の理由です。
変数をオブジェクトの内部に閉じ込めることで、「データを管理する責任の所在」を明確にできること。これがゲッターセッターの存在意義であり、カプセル化の概念です。
ポリモーフィズム・オーバーライド
メソッドはクラスが教えてくれる「オブジェクトが持つ性質」の実体です。
例えばクラスAに属するオブジェクトは「計算した結果を返す」メソッドを持っているとします。
クラスAの中に、Aの性質を継承したBやCが存在したとしても、全てAとしての「計算した結果を返す」性質は満たしていなければいけません。
しかし、どのように計算するのか?という実装には言及していないので、それをBやCが別々に実装することができます。
このようにして特定のメソッドの中身が「実体に引っ張られて変化する」というものをポリモーフィズムと呼びます。
具体例だと、object
は全てToString
という「文字列になることができる性質」を持ちます。ですが、様々なオブジェクトのToStringを呼んだとき「どのような文字列になるのか?」これはオブジェクト毎に異なります。これが、ただの「文字列になる性質」を満たしつつ、その実装(実際)は「実体によって変化している」ことを表します。
インターフェース
インターフェースはここまでに説明してきた縦割りのデータ構造継承空間と一段階別のレイヤで動く仕組みです。階層化されたクラスの見た目は、大きい丸小さい丸が無数に存在している空間となりますが、ここで、「とある丸が別の丸と線が交わることは決してない」ということに気づいたでしょうか。
クラスが「性質の定義」である、ということはここまでに話した通りですが、継承関係にない全く別のオブジェクト同士が、ある共通の性質を持つことがよくあります。例えば、「自身を破棄することが可能」という性質を表すDisposable
などを思い浮かべてください。この「破棄可能である」という共通の性質を定義するテンプレートを用意して、それを通して複数の異なるオブジェクトを同じように取り扱うことができると、継承という_縦方向の共通化_に加えて、インターフェースという_横方向の共通化_ができるようになって初めて、柔軟なプログラミングを行うことができるようになるのです。
なぜオブジェクト指向を使いたいのか
ここまで記事を読んできた方ならおわかりかもしれませんが、データの「性質」を階層的に管理し、同じモノは同じモノとして扱う、即ち「性質のグルーピング」を行うことこそが、オブジェクト指向の本質です。これはオブジェクト指向の概念の土台である「構造化プログラミング」における「データのグルーピング」を一段階抽象度の高いレイヤに持ち込んだ概念と言えます。これらを正しく設計した空間では、「新しい分類を追加する」「既存の分類に新しい実体を増やす」「必要な性質を持つデータを統一的に扱う」などの「オブジェクト空間の拡張」や、「性質のグループ構造を再利用した設計」が行えるのようになるのです。
まとめ
残念ですがこの記事は途中です。。。
書きたいことまだまだいっぱいあったハズなんだけど、全部忘れた……あと時間もオーバーしてるので、またいつかの機会に。
- 「オブジェクト」とは、プログラムで取り扱える「データ」の最小単位
- 「クラス」とは、無数に存在するデータの一部から「性質」と「名前」を明らかにし、それを分類して管理できるするための枠組み
- 「継承」とは、オブジェクトが「性質」を受け継いでいること
- 「インスタンス」とは、ある一つのオブジェクトが特定の「クラス」の枠組みの中に存在していること
- 「メソッド」とは、オブジェクトの「性質」を表すものの一つで、オブジェクト空間に、またはオブジェクト自身に、何らかの変化を起こすもの
- 「プロパティ」とは、オブジェクトの「性質」を表すものの一つで、オブジェクトの「状態」を覗き見るレンズのようなもの
- オブジェクト指向の設計とは「性質の設計」である
この記事は、某質問回答サイトやその他でちょこちょこと小出しにしてきた情報や思想をまとめたものです。この記事を読んでオブジェクト指向完全に理解できる人は居ないと思いますが、これが一般的な解説記事を読み進む際の下地となってくれたらいいなとひっそり思っています。
投稿するか迷ったけど投稿しちゃう
書き終わったらスマブラやるんだ……