自分がどういうことを考えてクラス設計しているかを整理してみました。同じようなことを大名エンジニアカレッジでも話したのですが、今回はフィヨルドの課題で考えてみました。
たとえば ls コマンドの場合はこういうレイヤがあるんじゃないかなあと考えられます。
- インプットする方法... OSの機能を使ってあるディレクトリのファイルを集めないといけない。その操作をする人が必要そう
- 集まったデータ... ファイルが集まってくる。ファイルには、普通のファイル、ディレクトリの二種類はありそう。
- アウトプット... 表示のフォーマットがいくつかある。一列のやつと、サイズなどを詳細に出すやつ。
- それらをつなぐ... ここに書いていないあふれた仕事をとりあえず押し付ける人がいると便利かな...
Railsでいう「MVC」のようなノリですが、まずは自分でそういったレイヤを考察します。図を描くのもいいです。
データの流れがどうなってるか? などはこのレイヤ分けの参考になります。
上のレイヤ分けから
- FileCollector
- NormalFile
- Directory
- OnelineOutput
- DetailedOutput
- Manager(...)
という風にクラスを抽出しました。これは暫定ではあります。
これらのクラスを:
- 誰が誰を必要としているか?
- 誰と誰が役割上「横並び」か?
という観点で関係を整理してみます(この観点は、本によって has_a is_a use_a などと呼ばれる概念に近いと思います)。
FileCollector ... File/Direcrotyを取得するのでその配列を持てると良さそう
NormalFile と Directory は横並び
OnelineOutput と DetailedOutput は横並び
横並びなので親クラスがあっても良さそう
*OutputはデータとしてやっぱりFile/Direcrotyの配列が必要そう
Manager ... CollectorやOutputのメソッドを呼び出すので、参照を持ってないとダメそう
この関係性を考えると、「継承」という関係性が意外と出て来ないのも気づきになるかもしれません。
ここまできたら多分実装を始められるんじゃないかなあ? と思います。
そのクラスが実装すべきメソッドや、クラスに必要な情報(依存、という言い方をする本もあります)がはっきりしているかなどを考えます。テストがしにくい場合、やっていることが複雑すぎたり、依存するオブジェクトが多い可能性があります。
依存でよく言われるのは:
- なににも依存しない
- 数値などに依存する
- 文字列やデータに依存する
- 別のオブジェクトに依存する
の順でテストがしづらくなる、という話です。このオブジェクト、もしかして数値にできないか? など振り返ってみるのはいいやり方かもしれません。
すんなりフィットする名前がいきなり決まれば、きっと設計はうまくいっていると思います。というより、いい名前が思いつくことが稀とも言えて、リファクタするポイントの一つのマークになるかもです。
Formatterはおっしゃる通り怪しい名前で、このプログラムが大きくなったら責務超過になったりするような雰囲気があります。ダメとは言わないが要注意ぐらいの。
とはいえ、書いているうちにクラスを分割したり統合したりするのは正直よくあると思います。
ポイントとしては、外から見た振る舞いを崩さないようなテストを用意しておいて、そもそも壊れていることをすぐ気づけるようにすべきです。「外からのテスト」は統合テストとかE2Eテストとか、書いているものの粒度によりますが...。
最後に言い訳を思いっきりするとぼくはクラス設計、オブジェクト指向に関してはまとまって勉強をしたことがなく、経験で書いている面もあります...
メンターの皆さん、適宜突っ込んでください。私も勉強させていただきます。
言い訳をコメントすると、悪名高い
Manager
が存在していますので、これはアプリケーションの機能が膨らんだら分割していく前提で入れております。