オブジェクト指向のこころ要約&考察|Pythonで学ぶシステム設計 #1

R&D的な立ち位置でプログラミングを行うにあたってよくあることですが、どうしても『動けば良い』になりがちで、プログラミングの保守などを考えないで書くケースが多いのではと思います。というのも、理論ベースでしっかり裏付けを取るところの方がプライオリティが高く、実際の実装は別の方に任せるケースも多いかと思います。研究にあたって作成したコードがスパゲッティ化した方も結構おられるのではと思います。

とはいえ、プロジェクトに中長期的に関わる際は保守の視点が重要だし、規模の小さいプロジェクトに関わる際は一気通貫で全て実装しなくてはならないなど、全く知らないというのも損するケースが多いです。
個人的にこの辺の役割分担がうまくできずに苦戦したプロジェクトなども多いです。分析を行うにあたって最初は分析言語のRを用いていたのですが、言語面の仕様が弱く運用に耐えないということから最近ではPythonを用いています。

上記の問題意識から、システムの設計について深く知らねばと思い、オブジェクト指向やシステム設計について色々と勉強してみました。オブジェクト指向は表面上の知識自体は以前から知っていて、クラスが変数とメソッドで成り立っていて、継承でサブクラスを生成して等の基本原理は理解していたつもりでしたが、深い考察ができていませんでした。
そこで、まずは基本的な考え方を抑えようと思って、以前知人に勧めていただいた『オブジェクト指向のこころ』を読んでみようということで、ざっと読んでみました。こちらの記事は読解して重要だと思った点をまとめたり読んでみての考察についてまとめます。

f:id:lib-arts:20190110181352p:plain

オブジェクト指向のこころ : デザインパターンとともに学ぶ Shalloway, Alan(著) - ピアソン・エデュケーション | 版元ドットコム

(必要以上に固く書きたくなく90%〜95%程度の確信度で書きますので、もし間違いなどがあれば気軽にご指摘いただけたらと思います。)

以下目次になります。1が本の概観、2~4が本の1,2,5章の内容の抜粋と要約、5が本を読んだ上でPythonにおけるシステム設計の考察になります。

 

1. オブジェクト指向のこころの概観
2. 責任の移行(shift of responsibility)
3. UML(Unified Modeling Language)
4. 設計とデザインパターン
5. Pythonにおけるシステム設計


1. オブジェクト指向のこころの概観
オブジェクト指向のこころの概観としては、根本的な考え方自体は1,2,5章にまとまっていた印象です。1章は2節で書く責任の移行(shift of responsibility)、2章は3節で書くUML(Unified Modeling Language)、5章は4節で書くオブジェクト指向プログラミングとデザインパターンについてです。個人的にはこの1,2,5章は非常に参考になったなと感じています。
3,4章はCADを用いたケーススタディで具体例を理解する方が時間がかかりそうだったのと、実装がJavaなので軽く読み流しました。6章以降は具体的なデザインパターンの話が中心でしたが、ストーリー性を重視しすぎている印象で個人的には冗長な印象を受けました。とはいえ、6章以降もある程度は知りたかったので半分くらいは理解できたら良いなという形で読み流し、大体はつかんだ形になります。

 

2. 責任の移行(shift of responsibility)
2~4節では重要だと思った1,2,5章に関して簡単な要約をまとめます。3つの章をピックアップしましたが、特に感銘を受けた考え方が1章に記載されていた責任の移行と5章の設計とデザインパターンの話です。2節では責任の移行について記していきます。

1章に記載されていた責任の移行(shift of responsibility)は、『学生に次のセミナーの教室を教える』ことを例として解説されています。以下本の1.5節から概要を取りまとめます。

【目的】
学生に次のセミナー教室について伝える

【構造化プログラミング(関数での実装)の実装手順例】
1. 聴講学生のリストを取得
2. リスト上の各学生に対して、以下の処理を行う
a. その学生が聴講する次のセミナーを調べる
b. そのセミナーが行われる教室を調べる
c. 現在の教室から次のセミナーが行われる教室までの行き方を調べる
d. 学生に次の教室への行き方を伝える

 

【上記の実装手順例の問題点】
講師が様々な詳細に注意を払う必要があり、全てに責任を持ち誰も代わりに責任を持ってくれない。
-> 例えば、学部生と大学院生など学生の種類で処理を分けると、制御プログラムの負担が増す
-> 制御プログラム(講師)から責任をオブジェクト(学生)に移行すべき
-> 具体的には教室の後ろに張り紙を貼り、講師は生徒に張り紙の指示に従って次の教室に行くようにという指示を出すだけにする(責任を講師から学生に移行する)

 

【責任の移行を裏で支えている要素】
・学生は自身のふるまいに責任を持ち、中央集権的なプログラムは存在していない
・制御プログラムは様々な種類の学生に分け隔てなく語りかけられる
・制御プログラムは各学生が行うことについて知る必要がない

 

 

3. UML(Unified Modeling Language)
UMLはUnified Modeling Languageの略で、プログラムのモデルを表現するために用いられる視覚言語のことです。視覚化することによって、自分自身の頭の整理に加えてクライアントやチーム内でのコミュニケーションのツールとしても役立てることができます。

f:id:lib-arts:20181226192103j:plain

 上図のように、集約、コンポジション、継承、依存などプログラム間の様々な関係性を図を用いて把握することが可能です。

 

4. 設計とデザインパターン
5章の設計とデザインパターンの話ですがシステム設計を建築になぞらえて解説しており、こちらも非常に面白いです。オブジェクト指向の心は"The Timeless Way of Building"というChiristopher Alexanderの著作を元に考察を深めています(若干冗長な印象もありますが。。)。こちらの本では『品質は客観的なものか?』という問いに関して議論を進めています。こちらの問題の解決に当たって、優れた設計の共通点を洗い出すことで、『解決するべき問題から構造物を切り出すことなどできない』という仮説を立て、それに基づいて色々と考察が行われています。
言い換えるなら『設計(解決策)は問題に依存する』ということですが、Alexanderはこの解決策のことをパターンと呼んでいます。オブジェクト指向のこころではこの考え方をシステムにあてはめて、GoFの著したDesign Patternsについて言及しています。

上記がデザインパターンの概要として記されているのですが、オブジェクト指向のこころではデザインパターンを学習するメリット(モチベーション)についてもまとめています。

・解決策の再利用
・共通用語の確立

解決策の再利用は先人の知恵を利用できるという意味で役に立つし、共通用語の確立はコンテクストを共有した用語を用いることでコミュニケーションの円滑化を促進するという意味で役に立ちます。


5. Pythonにおけるシステム設計
本を読んでみた感想としては、以下になります。

・1,2,5章で重要なポイントの感覚がつかめて非常に満足
Javaベースだったので、実装はそこまで読まなかった
・具体例が冗長だったので、少々読みづらい点があった

概要を掴むという意味では非常に満足だったものの、具体例はもう少し身近な例で理解したいというのはありました。そのため、身近な実装でかつ良い実装ということでPythonで書かれたOSSが良いのではと思いまして、いくつか読んでみています。中でもscikit-learnのインターフェースがシンプルで非常に読みやすいので、こちらを中心に色々と読んだりしています。(ライブラリを使いこなせるようになるという意味でも一石二鳥でした)
scikit-learnは非常にシンプルで洗練されたインターフェースで、基本的に学習ではfit、予測ではpredictでそれぞれの処理を行うことができます。非常にスリムで読みやすいライブラリなので、同様にR&DでPythonを用いていてシステム設計について学びたい方はscikit-learnの読解をお勧めします。

オブジェクト指向のこころを片手にscikit-learnのライブラリ実装を読んでいて感じたのが以下になります。

・各クラスのfitやpredictメソッドに責任の移行が行われており、制御プログラムは統一的に処理を記述できる
・__init__.pyに集約する形で書かれている(継承よりも集約を用いて実装すべき)
・基本はSciPyのラッパーで最適化処理などの処理はSciPyに投げている

また、具体的に読み方がわかればということでlinear_modelを題材に軽く読み解きます。
https://scikit-learn.org/stable/modules/linear_model.html
より、下記のサンプルコードを得られます。

>>> from sklearn import linear_model
>>> reg = linear_model.LinearRegression()
>>> reg.fit([[0, 0], [1, 1], [2, 2]], [0, 1, 2])
...
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
normalize=False)
>>> reg.coef_
array([0.5, 0.5])

ここでimportに関してですが、
https://github.com/scikit-learn/scikit-learn
"上記の元ライブラリのProjectルート/sklearn/linear_model/"の__init__.pyから諸々を読み込んでいます。また、linear_model.LinearRegression()でオブジェクトを生成していますが、こちらについては__init__.pyから同一ディレクトリのbase.pyからLinearRegressionをインポートしています。
https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/linear_model/base.py
最終的には上記に実装されているクラスでfitやpredictが実装されています。