コードの構造化 - コードの分割
コードを短時間で書き終えることが目的なら細かく分割せずに1つのコンポーネントにまとめて書いた方が楽だ。
すると後で困る。満足な試験できなくてバグに悩まされたり、仕様を変更するときどこを変更すればよいか作成者ですらわからなくなる。
よって後々のために構造化する。コードのライフサイクル全体を見ればその方が低コストに済むからだ。
今更だがコードをどのように分割すべきなのか複数回に分けてまとめてみる。まず初回は分割の仕方だ。
個人的な事情でPython言語を前提にしている。またソフトウェアを分割したもの全般を「コンポーネント」と記載する。
分割の単位
コードはどのような単位に分割できるのだろう。Pythonでは以下の単位に分割できる。
- 関数
- メソッド
- クラス
- モジュール
- パッケージ
- ライブラリ
適切に分割されたソフトウェアは試験しやすいだけでなく、ある箇所の変更が他の箇所に影響を及ぼさない。よってバグにも変更に強くなる。
関数
- 多くの言語において最小の分割単位
- 本当に小さなスクリプトならこれだけで済む
メソッド
- クラス内で定義された関数
- クラス内の属性を処理する
クラス
- 属性とメソッドの集まり
- オブジェクトの雛形となる
- 既存クラスから新しいクラスを作成することができる(継承)
- クラスは暗黙でマジックメソッド(後述)を持つ
モジュール
- 関数やクラスの集まり
- Pythonでは.pyファイルがモジュールに相当する
- プラクティスとしてimportされたときに実行されるような処理をモジュールに書いてはいけない
- python xxx.pyで呼び出されたときに実行したい処理は以下のように記述する
if __name__ == "__main__": <python xxx.pyで呼び出されたときに実行したい処理>
パッケージ
ライブラリ
- 正確にはPythonにはライブラリという機能はない
- 次のようなものを慣例的にライブラリと呼んでいる
- パッケージを集めたもの
- pipなどで管理してインストールする単位
マジックメソッド
- 特殊メソッド、ダンダーとも呼ばれる
- クラスの振る舞いを定義するための特殊なメソッド
- アンダーバー2つに囲まれている
- 以下にいくつか例を上げる
- コンストラクタ
__init__
- オブジェクトをprint関数で出力するときの出力内容を定義
__str__
- jupyterなどでオブジェクトの内容を表示するときの表示内容を定義
__repr__
- オブジェクト同士の比較方法を定義(Sortで利用)
__lt__, __gt__, and __eq__
- オブジェクトを「+」するときの挙動を定義
__add__
- 実行時に新しい属性をクラスに追加する
__setattr__
分割の指針
関数、メソッド、クラスの分割についてはSOLID原則の単一責任の原則を指針とすればよいだろう。SOLID原則についてはこちらを参照。
モジュール、パッケージ、ライブラリについては「コードの構造化 - コードのグループ化」で取り上げる凝集度が高くなるように構成すること。