【Ruby】オブジェクト指向設計を噛み砕いていく(#1 単一責任を意識したクラス設計)

投稿日:

プログラミング初心者の勉強ブログ #77

「オブジェクト指向設計」について理解を深めてようと思います。今回は「単一責任のクラス」の設計方法ついて理解し、プログラム設計のあるべき姿を学習しました。リファクタリングの観点として非常に役立ちます。

「クラスクラスとは、メタクラスのクラスであり、メタクラスとは、クラスがクラスとして持つ、名前のないクラスのことである」

 

目次

 

プログラム設計をするにあたっての大前提

今回、オブジェクト思考設計を学習するにあたり、「オブジェクト指向設計実践ガイド」という本を読んでおります。以下の内容は、その本を参考に、自分の言葉でまとめたものになります。

 

まず、プログラムを設計するにあたっての、大前提となる考え方を確認します。

 

 

「プログラムは本来、一度定義すれば永遠に動き続け、変化を必要としない。」

「しかし、"何かしらの外部要因"を受けたとき、変化が必要になる。」

「"何かしらの外部要因"は、どのようなプログラムであっても必ず存在する。」

「したがって、プログラムは必ず”変化すること”が必要になる。」

「つまり、プログラムは"変化すること"を前提とした設計である必要がある。」

 

 

見やすいコードであることや、同じコードを繰り返し書かないなど、設計において大事な点はいくつもあると思いますが、それらの認識の要素も含め、何よりもまず「プログラムは変化を求められるもの」という認識が、一番頭にあるということです。

 

「変化するから、修正が必要になる。だからどのような人でもわかりやすいコードであるべき。」

「変化するから、修正箇所は少ないほうが良い。だから同じコードを繰り返さず、一箇所にまとめ、使うときにそれを参照するべき。」

 

より良い設計を行うためには、プログラムを「変化するもの」として受け入れ、変化に対応しやすいプロブラムを設計していく、という前提が「プログラム設計」のあるべき姿であることを、今一度再認識する必要があります。

 

オブジェクト指向設計は、オブジェクトを管理することである

「オブジェクト指向に基づいて設計されたプログラムは、オブジェクト同士の自発的な相互作用によって成り立つ。」

 

オブジェクト指向設計の考えにおいて、「プログラムを作る」ことは「現実世界で何かモノを本当に作る」ことと似たイメージで成り立っています。オブジェクト同士が連携しあって(本では”メッセージの受け渡し”と書かれていました。)、プログラムが機能するわけです。

オブジェクト同士が連携し合うわけですから、お互いに依存関係が生まれます。

依存関係が発生すると、オブジェクト単体では「そのもの」が完成しない状況です。そうなると、「どう連携させるか」「どのような仕組みの方が効率が良いか」などの管理業務が発生します。

その管理業務こそ、「オブジェクト指向設計」そのものであるということです。

 

なぜ単一責任であるべきなのか

単一責任のクラス設計とは、「とにかくシンプルなクラス」のことであり、一文でクラスの内容を説明できるくらいに、クラスの持つ要素を簡単にわかりやすくする組み立て方のことです。

こういう勉強をしてると、「クラス」と「オブジェクト」がごっちゃになってくるのですが、そもそも「クラス」も「Classクラス」で生成されたオブジェクトなので、そもそもがごっちゃであり、「ウチものすごい入れ子構造なの」という、クラスの事情を理解してあげなければなりません。

RubyのClassクラスとかのモヤッと感をズバッと解決したい - Qiita

 

ちなみにリファレンスマニュアルにおける「Classクラス」は、

クラスのクラスです。

より正確に言えば、個々のクラスはそれぞれメタクラスと呼ばれる名前のないクラスをクラスとして持っていて、Class はそのメタクラスのクラスです。この関係は少し複雑ですが、Ruby を利用するにあたっては特に重要ではありません。

class Class - Ruby 2.5.0 リファレンスマニュアル

クラスクラス言いまくってるせいで、少しふざけてるように見えます。しかも特に重要ではないってリファレンスが言っちゃってます。

 

本題に戻りますが、単一責任のクラスである必要性についてです。

オブジェクト単体では「そのもの」が完成しない状況にする必要は何なのか、という話ですが、最初に書いた「プログラムを定義するにあたっての大前提」が理由そのものになります。

 

「プログラムに訪れる"変化"に対し、簡単に変更できるようにするため」に、クラスを単一責任にすべきということです。

「クラスがシンプルであればあるほど、変化に対応しやすい」ということです。これはメソッドについても同じです。

 

例えば「車」というテーマで、「タイヤ」や「ハンドル」や「エンジン」クラスを定義するとします。

「タイヤ」クラスの責任は、タイヤの幅であったり、タイヤの回転数であったりと、「タイヤの振る舞い」に関する内容の範囲であり、この振る舞い一つ一つを、タイヤクラスのメソッドとしてコーディングするわけです。

「ハンドル」にしても「エンジン」にしても同じです。

ここで、「タイヤ」クラスが「タイヤの回転数だけでなく、エンジンの回転数」に関する内容を含んでしまった場合、それは明らかな責任オーバーとなります。

「クラスがシンプル」とは、このような責任オーバーなクラスを作らないことを指します。

責任オーバーになるようなコーディングを行うと、「修正作業」の段階で影響が出始めます。「エンジンの回転数」について、エンジンクラスがしっかり責任を持っていれば、修正時にエンジンクラスのみを変更すれば済みます。

責任の所在が明確になる点の他にも、エンジンについての内容がエンジンクラス内のコーディングで完結していれば、変更に伴う副作用をもたらすことがなくなります。

また、他のプログラムやクラスでエンジンクラスと似たような内容のプログラムが必要になった場合、「コードの再利用」が最小限のコストで済みます。

 

今回の例では、責任の境界線がわかりやすいですが、実際のプログラムはここまでわかりやすくないと感じます。オブジェクト指向設計では、このような意識を持ってコーディングに取り組むべきだということです。

 

まとめ

こういう概念系の学習は嫌いではないのですが、取っつきづらく、曖昧な理解だと勘違いしやすい内容なので慎重になります。オブジェクト指向設計について、今後も少しづつ本読んでいきたいと思います。

以上ありがとうございました。

-プログラミング学習
-, ,

Copyright© s u p ? , 2019 All Rights Reserved.