本について
Amazon: https://www.amazon.co.jp/dp/4822284654
題材として選択したのは、「オブジェクト指向でなぜつくるのか-第2版-」という本です。13章で構成されており、今回は第4章について、まとめました。
ゴール
構造化言語で解決できない問題をオブジェクト指向プログラミングはいかに解決したか説明できる
構造化プログラミングで解決できない問題
第3章ではプログラミング言語が 機械語 から アセンブリ言語 、 高水準言語 、 構造化言語 へと進化する歴史が紹介されていました。
章の最期に 構造化言語 でも「 グローバル変数問題 」と「 貧弱な再利用 」の問題は解決できなかったと書かれています。
第4章では、そういった問題を解決するために発明された オブジェクト指向プログラミング について、詳しくみていきます。
オブジェクト指向プログラミングの三大要素
オブジェクト指向プログラミングには、構造化言語でも解決できなかった グローバル変数問題 と 貧弱な再利用 を解決するための、優れた3つの仕組みが備えられています。
これらの仕組みは「 クラス 」「 ポリモーフィズム 」「 継承 」と呼ばれ、オブジェクト指向プログラミングが普及し始めた1990年代には オブジェクト指向プログラミングの三大要素 と呼ばれていたそうです。
これら3つの仕組みの説明と、それぞれがもたらすメリットをまとめます。
クラス
クラスは「 まとめて、隠して、たくさん作る 」仕組みと表現することができます。
まとめる仕組み
結びつきの強い(複数の)サブルーチンと(複数の)グローバル変数を1つにまとめる仕組みのことです。
オブジェクト指向言語では、クラスにまとめられたグローバル変数を インスタンス変数 、サブルーチンを メソッド と呼びます。
コードだと以下のようになります。
# アクセス中のファイル番号を格納するグローバル変数
fileNO = ""
# ファイルを開くサブルーチン
def openFile
# ロジックは省略
end
# ファイルを閉じるサブルーチン
def closeFile
# ロジックは省略
end
# ファイルから1文字を読み込むサブルーチン
def readFile
# ロジックは省略
end
↓
class TextFileReader
# アクセス中のファイル番号を格納するインスタンス変数
@fileNO = ""
# ファイルを開くメソッド
def open
# ロジックは省略
end
# ファイルを閉じるメソッド
def close
# ロジックは省略
end
# ファイルから1文字を読み込むメソッド
def read
# ロジックは省略
end
end
結びつきがの強いサブルーチンやグローバル変数をひとつにまとめることで、それまで散らばっていた部品を一箇所にまとめることができます。
またクラスの名前が適切であれば、要素名を短くしても意味が伝わりやすくなるというメリットもあります。サンプル2をみると、メソッド名から File
という文字が無くなっていますが、サンプル1と同様に意味は伝わるはずです。
さらに要素名は同じクラス内で重複しなければよいので、他クラスで重複する要素名を使って変数やメソッドを定義しても問題ありません。要素の名前付けが楽になりますね。
つまり、クラスに「まとめる」ことでコードを整理整頓し、メソッド(サブルーチン)を探しやすくするメリットや、メソッド(サブルーチン)に短くシンプルな名前をつけることができるというメリット、さらには要素への名前付けが楽になるというメリットも得ることができます。
隠す仕組み
クラスに定義した変数とメソッドを、他クラスからアクセスできないように隠す仕組みのことです。
サンプル2のコードだと、インスタンス変数である fileNO
にはアクセスすることができません。グローバル変数とは違い、インスタンス変数はアクセスできる範囲を限定することができます。
インスタンス変数にアクセスできる範囲をクラス内に限定しておけば、インスタンス変数に意図しない値がセットされてバグが発生しても、クラス内のメソッドだけを調べればよくなります。
つまり、変数へアクセスできる範囲を限定することで、プログラムの保守性を向上することができるというメリットを得られます。
たくさん作る仕組み
クラスを定義することでインスタンスをいくつも作成できる仕組みのことです。
サンプル2はファイルを開いて、文字を読み込んで、ファイルを閉じるといった処理を実行するプログラムです。
対象ファイルが複数ある場合、従来のプログラミング言語であればファイル番号を保存している変数 fileNO
を配列にするという方法で対応するかもしれません。しかし、 fileNO
を配列にしてしまうと、 fileNO
に関連するメソッドのロジックは途端に複雑になるはずです。
オブジェクト指向プログラミングを使えば、対象ファイルが複数あっても、メソッドを複雑にすることなく、クラスからのインスタンス生成で簡単に対象ファイルを操作することができます。
対象ファイルが複数あった場合、それぞれのファイルに紐づくインスタンスを作成します。そうすれば、インスタンスを指定することで操作対象ファイルを特定することができます。 fileNO
を配列にしなくても良いので、メソッドのロジックもシンプルに保つことができます。
ポリモーフィズム
共通メインルーチンを作る仕組み
ポリモーフィズムはサブルーチンを呼び出す側のロジックを共通化する仕組みであり、この本では「共通メインルーチン」を作る仕組みと表現されています。
共通サブルーチンは呼び出される側を共通化して再利用しますが、ポリモーフィズムは呼び出す側を共通化して再利用します。
コードだと以下のようになります。
class TextReader
# オープンする
def open
# ロジックは省略
end
# クローズする
def close
# ロジックは省略
end
# 1文字読み込む
def read
# ロジックは省略
end
end
class TextFileReader < TextReader
# ファイルをオープンするメソッド
def open
# ロジックは省略
end
# ファイルをクローズするメソッド
def close
# ロジックは省略
end
# ファイルから1文字を読み込むメソッド
def read
# ロジックは省略
end
end
class NetworkReader < TextReader
# ネットワークをオープンするメソッド
def open
# ロジックは省略
end
# ネットワークをクローズするメソッド
def close
# ロジックは省略
end
# ネットワークから1文字を読み込むメソッド
def read
# ロジックは省略
end
end
def get_count(reader)
char_count = 0
while true do
char = reader.read
# 終了したらループを抜けるロジックは省略
char_count += 1
end
end
上記のような実装であれば、 get_reader
の引数に TextFileReader
のインスタンスと NetworkReader
のインスタンスどちらがセットされても、文字をカウントすることができます。
つまり、ファイル用とネットワーク用でそれぞれ get_count
メソッドを作成しなくても、ポリモーフィズムによって呼び出し側のメソッドを共通化し、再利用することができたということです。
継承
クラスの共通部分を別クラスにまとめる仕組み
継承は、クラス定義の共通部分を別クラスにまとめて、コードの重複を排除する仕組みです。
サブルーチン単位で構成されるオブジェクト指向プログラミング以前のプログラミングでは、共通サブルーチンを作って重複する命令群をひとつにまとめました。
それと同じように、クラス単位で構成されるオブジェクト指向プログラミングでは、継承を使って共通する変数やメソッドを共通クラスにまとめることができます。
継承によって作成された共通クラスのことを、オブジェクト指向プログラミングでは スーパークラス と呼び、それらを利用するクラスのことを サブクラス と呼びます。
継承によってコードの重複が排除され、より再利用性が高まることがわかるかと思います。
まとめ
構造化言語で解決できなかった問題として、 グローバル変数問題 と 貧弱な再利用 が挙げられます。
オブジェクト指向プログラミングは、 クラス という仕組みを使ってサブルーチンと変数を「まとめて」、他クラスから変数にアクセスできないように「隠す」ことで、 グローバル変数問題 を解決しました。
さらに ポリモーフィズム によって、呼び出される側だけでなく、呼び出す側のロジックを共通化できるようにしたり、 継承 によってクラスの共通部分を別クラスに切り出して、コードの重複を排除できるようになりました。
クラス や ポリモーフィズム 、 継承 の仕組みを組み合わせることで 貧弱な再利用 も克服しました。
所感
オブジェクト指向プログラミングは、より効率よく、安全にソフトウェアを開発しようとする先人たちの試行錯誤の結果なのだなと改めて実感しました。
特に下記の引用部分を読んで、先人たちの熱き想いを感じたような気がします。
先ほどのポリモーフィズムで呼び出し側を一本化するだけでは飽きたらず、似たようなクラスの共通部分もまとめてしまおうという機能です。まさに徹底した合理化、プログラミングを楽にする機能はなんでも用意してしまえ、という発想です。
オブジェクト指向プログラミングはあくまで手段であり、それをうまく活用するか否かは開発者の心がけ次第です。だからこそ、日々意識的に重複するコードを書かないようにしたり、あるべきクラスにあるべきメソッドを作成したりするようにしなければと再確認しました。
備考
最近のオブジェクト指向言語はさらに進化している仕組みがあるようなので、それは補足記事としてまとめさせていただきます。