スタンプ結合
Stamp coupling
複数のモジュールが、複合データ構造を共有し、その一部のみを使用する
moduleが必要としないfieldが変更されることで、変更しないといけない可能性が生じる
比較的、結合は低いものとして整理されている
起きうる問題
与えていたobjectの構造が変わった時に影響を受ける
FullName
というobjectを受け取るが、 last
のみに依存している関数がある時
tstype FullName = { first: string; last: string }
const f = (n: FullName) => { console.log(`last: ${n.last}`) }
FullName
の構造が変更されたり、property名が変わった時に、影響を受ける
スタンプ結合をしていない実装になっていれば影響を受けない
tsconst f_ = (last: string) => { console.log(`last: ${last}`) }
呼び出し側で修正が必要になるだけ
testの際に、過剰なデータを用意する必要がある
渡されたobjectに対するinterfaceが変更された時に変更が必要になる
これはスタンプ結合とか関係なく影響を受けるのでスタンプ結合の問題ではないな
ただ、実際は、さほど問題にならない気がしてきた

つまり、module内に2種類の関数が用意される
module内でのみ使うprivateな関数
スタンプ結合を避けるように実装するのが良い
module外で使うpublicな関数
そのmoduleが公開するデータ構造を丸ごと取るように設計する
そのmoduleに属するデータ構造が修正された時に、
そのmoduleに属する関数に修正が必要になるのは、割と許容できる
データ構造の変更は、module外部には影響を及ぼさない
module内の修正も最小限に抑えたいというのもあるが、「丸ごと渡すことの意味」を重視する方が利がある、と思う
例えば、 user
というmoduleを作った時に、
f(user.a, user.b, user.c, user.d.e.f)
のようにして、スタンプ結合を避けるために必要な引数だけを個別で渡すよりも、
user
というデータ構造は隠蔽されているとみなして、 f(user)
とした方がmoduleの成り立ちとして意味が通る
呼び出した人からすると、「関数 f
は user.x
にも依存してるんだっけ」というのが気になるところだが、moduleの成り立ちからして、そもそもそこは君が気にするポイントじゃない、ということになる
結果的に、 f
は user
と密に結合するが、それは当然で、 f
はuser moduleのmethodとして提供してるのだから、寧ろ密結合してる方が正解、といえる
testの際に、過剰なデータを用意する必要がある
publicな関数なら、他のテストでも使うはずなので、用意したらよろしい、という感じになる
privateなら、スタンプ結合をやめて、単体のデータを用意する
言語の型検査の性質上、objectを丸ごと取らないとどうにもならないときもある
tsだとtagを持っていないと分岐ができない
関数の責務として、個々の値に対する処理なのか、グループに対する処理なのかを見極める
moduleの考え方がない状態で、無理にスタンプ結合を避けても修正が楽にならないこともある
その計算に必要なpropertyを引数に細かく取ったとして、内部の計算ロジックが変わった時に
関数の引数が増減する修正を施す
関数を呼び出している箇所全ての修正をする必要がある
特定のデータ構造に依存するmoduleでない関数の場合はどうするか?
要は、何らかのinterfaceに依存するようにすれば良い