はじめに
この一週間、Recursionのプロジェクト5で実装されていたアプリケーションサーバ、コマンドラインシステム、マイグレーションシステム、シーディングシステムのコードをゼロから自分で作ってみました。
これらのコードは読むことで理解はできていましたが、実際に自分で書いてみることで、OOPの設計思想をより深く理解できるのではと思い、取り組みました。その結果、OOPの恩恵を感じながらコードを設計し書けるようになってきました。
「タスクを依頼する」という感覚
現在のOOPに対する感覚を一言で言うなら、「タスクを依頼する」です。
たとえば、アプリケーションサーバーを自作する際に、ルートファイルからパスの一覧を取得し、それに対応するビューを検証・生成する処理が必要でした。これをエントリーポイントにすべて書くとコードが煩雑になるため、Response
クラスを用意して、その中でビューの検証や生成を行ってもらう設計にします。つまり、「その処理はこのクラスに任せる」という発想です。
また、DB接続設定を .env
ファイルから取得する処理も、Settings
クラスに任せることで、存在しないキーの検証や環境ごとのファイルパス管理なども依頼できます。
同様に、MySQLの接続処理を MySQLWrapper
クラスに任せることで、設定の組み込みや初期化処理もコンストラクタで完結でき、保守性の高いコードになります。
このように、クラスに「タスクを依頼する」ことで、コードの読みやすさや保守性が飛躍的に上がることを実感できました。
インターフェースと抽象クラスの使い分け
インターフェースと抽象クラスの違いについては、最初かなり混乱していました。
たとえば、今回コマンドラインシステムを作る際に、Command
インターフェースと AbstractCommand
抽象クラスを作成しました。Command
に getArguments()
というメソッドを定義し、AbstractCommand
でその実装を行ったとき、
「処理内容が同じなら、抽象クラスだけでよくない?」
と疑問に感じました。というのも、これまでは「インターフェースは処理が異なるときに使う」という理解だったからです。
しかし、今回の実装を通して、以下のように整理できました。
- インターフェース:そのクラスが「何ができるか」を外部に伝えるためのもの
- 抽象クラス:共通する実装や状態をまとめるもの
インターフェースを見るだけで、そのクラスで何ができるかがわかることも、インターフェースの恩恵なんだと実感しました。「この処理を任せるクラスを作ろう」と考えたときに、自然と「どんなインターフェースが必要か?」という視点も持てるようになってきました。
OOPは「頭」だけでなく「身体」で覚える
OOPは、知識として理解するだけでなく、実際にコードを書いてこそ本質が見えてくる設計思想だと感じました。
最初は「手続き型の方がシンプルで分かりやすい」と思っていましたが、プロジェクトが複雑になればなるほど、OOPのありがたみを感じられるようになってきました。
おわりに
今回のプロジェクトを通じて、OOPの設計は「複雑な処理を誰かに任せる」ための仕組みである、ということを身体で理解することができました。
今後も「この処理は誰に任せるべきか?」「どんなインターフェースを持たせるべきか?」という視点を持ちながら、より良い設計と実装を意識してコードを書いていきたいと思います。