実装パターン

はじめに

会社で同僚と話していた時にダブルディスパッチという単語が出てきて、紹介されたのがこの本。
あのケント・ベックさんの著書ということだったんだけど、存在自体知らなかった。

気になったところ

原則 P.16

結果の局所化

  • 変更のコストを低くとどめることが、実装パターンを行う一番重要な動機

繰り返しの最小化

  • コードの繰り返しはコピーの一形態

ロジックとデータの一体化

  • 変更を行う際には、ロジックとデータは同じタイミングで変更しなければならないことが多い

対称性

  • addとremove
  • inputとoutputとtally

宣言型の実現

  • プログラム内において、順序や条件分岐がなく、純然たる事実に似た部分に対しては、コードが宣言的に書かれているほうが、読みやすくなる

変更頻度

  • 同じタイミングで変更されるロジックやデータは同じ場所に置き、変更されるタイミングが異なるロジックやデータは分けておくというもの

いきなりガツンと原則を並べてくれるというのは助かる。
ざーっと眺めてみると、色んな所で見かける原則だったりルールだったりすることが多いなと感じる。

特に気に入ったのが、コードの繰り返しがコピーの一形態という表現。
この辺りの表現は流石だなと思った。

あと、無意識に気にしているのが対称性だ。
これが上手く出来ているコードに出会うと、個人的に美しく感じるんだなと再発見できたのは面白かった。

他の人とのコミュニケーションこそが、クラスの名前の目的である P.31

こういったわかりやすいコードみたいなのは、全て人間のためであってコンピュータの為ではないのだよね。
特にコミュニケーションの為と言い切ってしまっているところが気持ちが良い。

バリューオブジェクトとは変化する状態の入れ物ではなく、整数のように振る舞うオブジェクト P.39

自分の場合、DDDのバリューオブジェクトしか知らなかったので、認識を改めることが出来て良かった。
この本に出てくるバリューオブジェクトは、数字のようにイミュータブルなものであると説明されていて、DDDのバリューオブジェクトよりも狭義だと思われる。
なんとなくこちらのバリューオブジェクトの方がイメージしやすく、こちらがあったうえでのDDDのバリューオブジェクトな気がした。

プログラム内のパスが増えれば増えるほど、プログラム全体は正しく動作しなくなっていく P.45

パスが増えるということは、プログラムが複雑になるということ。
複雑になれば、プログラムを正しく動作させるのが難しくなるということ。
とても分かりやすく表現されていて良い。

プラガブルセレクタを使用するコストは大きいが、難しい問題を解決するためだけ使用を限定すれば、そのコストは正当なものと見なしていいだろう P.51

プラガブルセレクタという名前が付けられていたことにびっくりしたが、クラス名やメソッド名を変数とかに入れて実行時に振り分けするみたいなことは昔良くやっていたが、IDEの恩恵が受けられないので、最近はあんまり使わなくなった気がする。
まだまだ似たようなことを乱用しがちなのだけども、どこで使われているのか不明瞭になったりしてデメリットも大きいと再認識した。
気をつけたい。

我々の脳は、変化する状態に対応するように構造化され、条件付けられているので、状態は有用なメタファーだ P.57

並行処理などは状態を持たない関数型プログラミングの方が相性が良いのだけども、人間の脳は変化する状態を扱う方が扱いやすく出来ているので、オブジェクト指向プログラミングのほうが有用なケースもあるよねという話。 関数型プログラミングの有効性を認めつつも、オブジェクト指向プログラミング程世間に広まってないのは、人間の脳には理解しづらいからだというニュアンスで書かれているが、昨今の関数型プログラミングの盛り上がりをみると、ケント・ベックの意見も変わっているかもしれない。

当時のケント・ベックの関数型プログラミングへの意見が垣間見えるという点では個人的に興味深かかった部分でもあった。

対称性のように「美学的」要求を満たすため「だけ」にメソッドを追加するのは愚かに感じるときもある。しかし美学はもっと深いものだ。厳密に直線的な論理思考よりも、美学は脳の多くの箇所に働きかけてくれる。いったん、コードに対する美学を養えば、コードから受ける美学的な印象は、コードの品質に関する貴重なフィードバックとなるはずだ。表象的な思考の下から浮かんでくるこれらの感情は、明示的に名前を付けられ、正当化されているパターンと同じだけの価値を持つことが出来る。 P.85

深い。
そして、とても共感した。
美しいコードを書こうとするコストは、傍から見るとエンジニアの単なるエゴに見えることも多いと思うが、その積み重ねが文化や教養のようなものを育てていくのだと思うと価値のある行為に感じられて嬉しくなった。

メソッドが返す型は、まず、そのメソッドが副作用を伴うプロシージャなのか、特定の型のオブジェクトを返す関数なのかを伝える。 P.104

プロシージャというのは、いくつもの処理を束ねて一つにまとめたものみたいな良くわからないイメージを持っていたが、返り値を持たないという要素もあることを初めて知った。
とても分かり易い。

自動テストを作るのは、それ自身が貴重な設計活動である。 P105

これはテストコードを書いていると実感する言葉だ。
まだ未読のテスト駆動設計も読んでみたくなった。

変換コンストラクタ P109

コンストラクタで変換元のオブジェクトを引数で受け取り、変換後のオブジェクトを返すというテクニック。
自分はこのテクニックを今まで使った覚えはないけども、ファイルパスの文字列をコンストラクタの引数で渡すとFileオブジェクトが生成されるようなパターンは良く見かけるし、分りやすいと思う。
積極的に使っていきたい。

引数のないコンストラクタでオブジェクトを生成してから、一連のsetterオブジェクトを呼び出すことで、柔軟性が高まることもある。しかし、この手法では、オブジェクトが正しく動作するためにどんなパラメーターの組み合わせが必要なのかわからない。 P.111

メリットは理解できていたが、デメリットは明確に意識していなかった気がする。
コンストラクタにどういった引数を渡すべきかというのは、もう少し慎重に考慮したいなと思った。

キャッシュにオブジェクトを記録したり、作成するサブクラスを実行時に切り替えたり、オブジェクト生成以外の複雑な処理を意図している場合は、ファクトリメソッドが役に立つ。しかし、コードを読んでいてファクトリメソッドを見ると、私はいつも気になるのだ。「オブジェクト生成以外のほかにどんなことが行われているのだろうか」コードを読む人の時間を無駄にしたくないので、行われるのがオーソドックスなオブジェクト生成だけであるときは、私はコンストラクタで表現する。同時に他のことが行われているのであれば、読む人の目をその事実に向けさせるためファクトリメソッドを導入することにしている。 P.112

コードの書き手の意図を読み手に伝える時に、如何に読み手のコストを減らせるよう配慮出来るかということの重要性が書かれているなと強く感じた。
こういった細かい一つ一つの積み重ねが、コードの分りやすさに繋がっているんだなと思うと、一行一句気が抜けないんだなと改めて思い知らされた。

論理値設定メソッド P.114

これは

void setValid(boolean newState) {
}

よりも

void valid() {
}
void invalid() {
}

の方が人間には分かりやすいからオススメという話なのだけども、自分は今まであまり意識してこなかった気がする。
最近ではgettersetterはIDEの機能で自動生成してしまい、思考停止状態に陥っている気がした、反省。

等価性メソッド P.116

equalshashCodeメソッドを作成して等価性を評価しましょうという話。
equalsメソッドは使うことはあっても、自分で作ったことはない気がするし、オブジェクト自身のハッシュ値を返すようなhashCodeメソッドも作った覚えがない。 getIdじゃなくてhashCodeの方が適切なケースは結構ある気がしたので、今後は積極的に使っていきたいなと思った。

getterメソッドの場合と同様にツールからsetterメソッドを呼び出す必要があるのであれば、「ツール専用」というコメントを付けた上で、パブリックにするようにしよう。人間に対しては、さらに伝達性とモジュール性に富んだインターフェースを提供するようにしよう。 P.119

これは論理値設定メソッドをもう少し推し進めた内容で、setterメソッドは基本的に公開しないほうが良いんじゃないかという提案。
自分の場合、あんまり深く考えず作ってしまっているのが現状なので、コンストラクタの引数と一緒に慎重に考えていかないとなと反省した。
けど、テストのしやすさ考えると結果的にsetterメソッドを作り続ける気もするし、一旦作ってしまうと人間も使い始める気がするので、現実的かどうかはやってみないと分からないという感触。

コレクションの拡張 P.139

  • コレクション系クラスを拡張するのなら、使わないメソッドはUnsupportedOperationExceptionを投げるようにしよう
  • 使わないメソッドが多いのであれば、安易に拡張するのではなく、ラッパークラスを用意してコレクションクラスは内包してしまおう
  • java.utilに追加できるような、汎用のコレクションクラスを実装する場合にだけ、コレクションクラスを拡張しよう

という戦略。
個人的にUnsupportedOperationExceptionを投げるというのは、とても分りやすいと思うので真似したくなった。
ラッパークラスはケースバイケースな気がするが、安易に拡張するのは個人的に好きじゃないので、基本方針は賛成。
汎用のコレクションを実装する時だけ拡張するというのも、とても良い指針だと思う。

あと、Javaのコレクション系クラスやインターフェースの多さは素敵だなと思うし、参考になるなって思う。
自然と色んなパターンがあることを自覚させてくれる効用もある気がするし、個人的に学びが多いなと感じた。

おわりに

この本を読んで、ケント・ベックという人はとてもバランス感覚の良い人なんだろうなと感じた。
自分が分かっていることだけを丁寧に、そして素直に書かれていて、その誠実さに人々は惹きつけられるのかもしれない。

内容的にも、知らなかったことやまだまだ出来ていないことも多く、とても学びが多い本だった。
絶版していて手に入りづらいのがとても残念。
自分はまたまた同僚から借りることが出来たのでラッキーだったんだけど、出来れば常に手元に置いておきたい本だと思ったので、再販されるようなことがあったらぜひ購入したいと思う。

あと、今回は必ずパソコンの前で本を読むようにして、気になったところをその場でメモを取るようにしてみた。
その場で自分が感じたことはメモを取らなかったのだけども、結果的に感想を書く段階で見返すことになり、自然と復習することも出来たのも良かった。
このスタイルで何冊か読んでみたいなと思う。