概要
今回は、「オブジェクト指向でなぜつくるのか」を読んだので学習した内容をアウトプットしていきます。
この本は、とても内容が分かりやすく、間違いなくオススメの本なのでオブジェクト指向を学習したい方は是非チェックしてみてください!!
オブジェクト指向とは
オブジェクト指向は、ソフトウェアの保守性や再利用をしやすくすることを重視する技術です。
個々の部品により強く着目し、部品の独立性を高め、それらを組み上げてシステム全体の機能を実現することを基本とします。部品の独立性を高めることで、修正が起きた場合の影響範囲を最小限にし、他のシステムで容易に再利用できるようにします。
一言で表現すれば、**「難しいソフトウェア開発を楽に行うための総合技術」**です。
オブジェクト指向と現実世界は似て非なるもの
実際には、オブジェクト指向と現実世界は似て非なるものです。
私もオブジェクト指向を理解する時に無意識のうちに現実世界と比べて考えていたので、まずはその考え方を矯正する必要があります。
では、いったい何が違うのかを具体的に見ていきましょう。
まずはクラスとインスタンスです。
クラスは種類で、インスタンスは具体的なものを表します。そして現実世界を例としてクラスとして犬、インスタンスとしてポチやタローが定義されているとしましょう。
オブジェクト指向の世界では、最初にクラスを定義し、そこからインスタンスを作る仕組みになっています。しかし当たり前の話ですが、現実世界で犬が生まれるのは、メス犬が出産するからで、犬クラスから生まれるわけではありません
基本的に、オブジェクト指向と現実世界では、クラスの位置付けが大きく違います。
オブジェクト指向におけるクラスは、インスタンスを作るための仕組みであり、インスタンスが帰属するクラスは一つだけです。
しかし、現実世界では先に具体的なモノ(インスタンス)があり、それを見る側の立場や興味の違いによって、いろいろな基準で分類します。
つまり、オブジェクト指向の世界では、あらかじめ全ての行動をメソッドとして準備しておく必要があります。そしてそのメソッドを呼び出すことで、物事が動き始めます。
オブジェクト指向の三大要素
ここからは、オブジェクト指向の三大要素について説明していきます。
まずは、三大要素の一つであるクラスの仕組みについて説明していきます。
クラス
クラスの役割は一言で言うと、「まとめて、隠して、たくさん作る仕組み」のことです。
まずは「まとめる」について説明していきます。
まとめる
クラスは、変数とサブルーチンをまとめることができます。ここでいう変数とはC言語やCOBOLなどにおけるグローバル変数のことです。
オブジェクト指向ではクラスにまとめたサブルーチンをメソッドと呼び、グローバル変数をインスタンス変数と呼びます。
実際、サブルーチンと変数をまとめただけではあるのですが、まとめて整理整頓することにとても価値があります。
現実世界で例を挙げるならば、散らかった部屋を片付けるのに、大きな箱を1つだけ用意するよりも、複数の箱を用意して洋服、書籍、文房具、小物と種類別に分けて入れる方が使い勝手が良いのと同じです。
クラスを使ってまとめると下記のコードのようになります。
クラスでまとめる前
// アクセス中のファイル番号を格納する変数
var fileNum:Int
// ファイルをオープンする
// 引数にパス名を受け取る
func openFile(pathName:String){
// ロジック省略
}
// ファイルをクローズする
func closeFile() {
// ロジック省略
}
// ファイルを1文字読み込む
func readFile() {
// ロジック省略
}
クラスでまとめた後
class TextFileReader {
var fileNum:Int = 0
func open(pathName:String){
// ロジック省略
}
func close() {
// ロジック省略
}
func read() {
// ロジック省略
}
}
また、クラスにまとめることによって名前づけがしやすくなります。クラスでまとめた時にメソッド名のFileを取ってopen/close/readとしています。
なぜ、取ることができるかというとクラスに対してTextFileReaderという名前をつけており、このクラスがファイルを読むためのものであることを宣言しているため、いちいちメソッド名にFileをつける必要がなくなります。
隠す
次は2つ目の「隠す」です。「まとめる」の所ではサブルーチンとグローバル変数をクラスにまとめました。
しかしこの状態では、まだfileNum変数はクラスの外側からアクセスできてしまいます。
このfileNum変数はTextFileReaderクラスのopen/read/closeメソッドからアクセスしますが、そのほかの処理からアクセスする必要はありません。
、
オブジェクト指向ではインスタンス変数の前に「private」をつけることで外部のアクセスを遮断することができます。
こうすることで、プログラムが不正な動作をした場合には、fileNum変数と、この3つのメソッドだけを調べれば良くなるからです。
class TextFileReader {
private var fileNum:Int = 0
func open(pathName:String){
// ロジック省略
}
func close() {
// ロジック省略
}
func read() {
// ロジック省略
}
}
たくさん作る
いったんクラスとして定義すると、実行時にそこからいくつでもインスタンスを作ることができます。
これにより同種の情報を複数同時に扱う処理であっても、そのクラス内部のロジックをシンプルにできます。
一般的にアプリケーションでは同種の情報を複数同時に扱う場合がよくあるため、この仕組みは非常に強力と言えるでしょう。
呼び出す側を共通化するポリモーフィズム
次に。三大要素のポリモーフィズムについてです。ポリモーフィズムは「いろいろな形に変わる」といった意味を持ち、日本語では「多様性」などと訳されます。
ポリモーフィズムはひと言で表現すると、共通メインルーチンを作るための仕組みと言えます。
さて、今回もポリモーフィズムについて簡単なプログラムを例を示して説明していきます。
まず、ネットワーク経由で送信された文字列を読み込むクラスを作ります。
class NetworkReader {
func open(){
// ロジック省略
}
func close() {
// ロジック省略
}
func read() {
// ロジック省略
}
}
続いて、TextFileReaderクラスを作ります。
class TextFileReader {
private var fileNum:Int = 0
func open(pathName:String){
// ロジック省略
}
func close() {
// ロジック省略
}
func read() {
// ロジック省略
}
}
呼び出す側はNetworkReaderとTextFileReaderのどちらのメソッドを呼び出すことができます。
そして、文字列を入力する他の方法などを追加したとしても、下記のコードのように呼び出す側のプログラムはいっさい修正する必要はありません。
// 呼び出す側
class HomeViewContoroller: UIViewController {
private var networkReader = NetworkReader()
private var textFileReader = TextFileReader()
override func viewDidLoad() {
super.viewDidLoad()
networkReader.open()
textFileReader.open()
}
}
ポリモーフィズムの利用による拡張性の確保
下記の画像は、ポリモーフィズムの利用のイメージ図です。
クラス定義の重複を排除する継承
最後の要素は、継承です。継承は一言で表現すると「クラスの共通部分を別クラスにまとめる仕組みです。
この仕組みを利用することで、変数とメソッドをまとめた共通クラスを作り、別のクラスからその定義を丸ごと拝借することが可能になります。
上記のポリモーフィズムで呼び出し側を一本化するだけでは飽き足らず、似たようなクラスの共通部分もまとめてしまおうという機能です。
継承を使う場合、共通に使いたいメソッドとインスタンス変数を共通クラスに定義し、利用したいクラスはその共通クラスを「継承すること」を宣言します。
これにより共通クラスの定義内容がそのまま使えるようになります。オブジェクト指向では、この共通クラスのことをスーパークラスと呼び、それを利用するクラスのことサブクラスと呼びます。
下記の画像は、継承のイメージを 簡略した図です。
まとめ
以上がオブジェクト指向の三大要素であるクラス、ポリモーフィズム、継承の説明となります。ここでおさらいのために、もう一度整理しておきましょう
それぞれの説明、目的、覚え方は以下の通りです。
これらの仕組みを組み合わせることで、従来のサブルーチンだけでは実現できなかった大規模な再利用が可能になりました。
今後は、オブジェクト指向の原則を意識をして、ソフトウェア開発に取り組んで行けたらなと思います!
それでは、また。