Ateam Lifestyle Advent Calendar 2019の23日目は
株式会社エイチームライフスタイル 名古屋開発部の@masatomasato1224が担当します。
今年の5月からRailsでバックエンド開発のお仕事をしております。
はじめに
@masatomasato1224はエイチームライフスタイルに入るまで、Rubyに触れたことがありませんでした。
C#やJavaの経験はあるものの、動的型付け言語の経験は皆無。。。
「このままじゃまずい!!」と手に取った本の内の一つが
オブジェクト指向設計実践ガイド -Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方-
でした。
とても良い本で、学ぶことがたくさんありました。
なので読み進めた中で特に勉強になったことをまとめて、自身が設計に迷った時はもちろんのこと
- クラス設計を始めようとする方
- クラス設計に迷っている方
そんな方の「道しるべ」になればと思い、この記事を執筆しました。
目次
- 前提
- 設計とは何か
- なぜ設計が必要か
- 設計の道しるべ
- 単一責任のクラス
- 依存関係の管理
- 柔軟なインターフェース
- 継承/ダックタイプ(ロールによる役割の共有)の使い分け
- 最後に
前提
具体的な設計に対するアプローチの前に
- 「設計とは何か?」
- 「なぜ設計が必要か?」
についてまとめていきたいと思います。
**「そんなこと既に知っているぜ!早くどうするのか教えろよ!」**って方は読み飛ばしてしまって下さい ^^。
設計とは何か?
『変更が簡単になる工夫をすること』
もう少し掘り下げると...
- 変更時の影響範囲が分かりやすい
- 要件の変更が小さければ、変更範囲も小さい
- 自分だけじゃなく、他の人も変更できる
- 再利用がしやすい
なぜ設計が必要か?
『アプリケーションは変更が加えられるものだから』
『設計されていないアプリケーションのコードは書いていて楽しくないから』
作ってから全く変更されないことが約束されているアプリケーションであれば設計は不要です。
※そんなものがあればですが。。。
実際には、開発中、リリース後問わず何度も変更が発生します。
変更のたびに、影響範囲が分からず、変更範囲の特定もままならず、作った人しか読めないコードをコピペするしかないとしたら。。。
設計の道しるべ
ここからは、実際に設計を行っていく上での道しるべとなるポイントについてまとめていきます。
単一責任のクラス
クラスに複数の責任を持たせた場合、再利用することが難しくなります。
再利用をしようと思っても、余分なパーツがあればあるほど必要なモノを取り出すのが難しくなります。
また、複数の責任があるということはそれだけ影響を受けやすく変更が発生する可能性が高くります。
クラスが単一責任かどうか見極める方法
-
一文でクラスの責任を説明してみる
- 「それと」や「または」が含まれないか
-
クラスに対してメソッドで質問してみる
- 自然な質問になっているか
- 例1:「タイヤクラスさん、あなたのサイズを教えてくれませんか?」 → OK(タイヤクラスにタイヤ自身のことを訪ねている)
- 例2:「タイヤクラスさん、あなたを取り付けることが出来る車を教えてくれませんか?」 → NG(タイヤクラスに車の情報を訪ねている、タイヤは自身のことについてのみ知っているべき)
- 自然な質問になっているか
依存方向の整理
依存方向が整理されていない場合、変更の影響を受けやすくかつ影響が広範囲に及びます。
そのため依存関係を作り出す(他のクラスの機能を利用する)際には、
自身よりも安定しているクラスに依存するように依存の方向を整理します。
依存方向を整理する際の基準
- 要件変更が起こりにくいクラスに依存させる
- 具象よりも抽象(ダックタイプ ※C#やJavaであればAbstractやInterfce)に依存させる
- より少ない依存関係を持つクラスに依存させる
A,B,Cに当たるものに依存させる
クラス間のインターフェース定義
ちょっと長くなります。
個人的には一番重要かなと思いました。
「インターフェース」にはいろんな意味がありますが、
ここでは**「クラスに定義されるメソッド」**という意味で使います。
オブジェクト指向アプリケーションはクラスが集まって構成されますが、
単に集まっているだけではなく、各クラスにはそれぞれの責任があり、お互いに連携します。
クラスの責任はパブリックインターフェースで表現され、
クラス間の連携の多くはパブリックインターフェースを呼び出すことで実現します。
つまり、パブリックインターフェースの定義によって以下が決まります。
- クラスの実質的な責任
- 依存の方向
そのた、パブリックインターフェースには次のような性質が求められます。
- 抽象的で、変更されにくい名前やパラメータを持つ
もし、不適切なインターフェースがパブリックとして定義されていたとしたら。。。
クラスの責任はあいまいになり、意図しない使い方をされる。
そして、あらゆるところから呼び出されて、依存の方向を管理することが出来なくなります。
そのため、クラス間のインターフェース定義を作る際は
クラスが単一責任になっているか、依存の方向が正しいかを
前述した方法で確認しながら作ることが大切です。
クラス間のインターフェース定義を効果的に検討する方法
シーケンス図を使う
シーケンス図を使うことで、クラス同士が『だれ』と『どんな連携』をすれば良いかを俯瞰することができます。
これにより、クラスの責任と依存の方向を視覚的に確認しながらより抽象的なインターフェースを見つけ出すことができます。
また、クラス間を抽象的なインターフェースで結びつけることで、クラスの間にある継承関係やダックタイプを
見つけやすくなります。
継承/mixin/コンポジットの使い分け
ここも、かなり勉強になったのですが。。。
力付きたので、また機会があれば書いていこうと思います ^^;
最後に
この本は、Rubyに特化している訳ではありません。
どちらかというとオブジェクト指向設計全般に関して一般的な解説をしています。
ただ、文中のコードはRubyで書かれていますし、Rubyの言語仕様に沿った設計手法についても解説がなされています。
なので、Rubyでオブジェクト指向設計を行いたい人にはぜひおススメです!
Ateam Lifestyle Advent Calendar 2019の24日目は、@hsmyがお送りします!
"挑戦"を大事にするエイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/