OCP――共有先を一つ足したときの差分を追う
作成: / 公開: / 内容更新:
執筆:tamito0201 / 掲載・運営:プロマリ
丸い共有ボタンの裏側を掘る連載の第5回。SOLID の2つ目の原則 OCP を、本物の SNS(Bluesky)の追加を通じて確かめます。名札カード1枚で済む変化と、共通処理のコードを変更することになる変化。差分を1行ずつ追いかけ、閉じる向きは変化の来やすさで選ぶことを、図22点で読み解きます。
約束は、テストで守る
2ページ目では、Bluesky の名札カードを足しても、共通処理のコードは1行も変わりませんでした。けれど、「コードを変えていない」ことと、「今までのボタンが今までどおり動く」ことは、同じではありません。たとえば、足した名札カードのせいで、X のボタンの並び順がずれることもありえます。OCPの約束、つまり「足しても今までのものは変わらない」が守れているかを確かめる証拠になるのが、テストです。
テストとは、「この入力なら、この結果になるはず」という答えを先に書いておき、プログラムを動かして一致するかを機械で確かめる仕組みです。先に書いておく答えを期待値と呼びます。
今回の検証では、X や LINE の URL の作り方、ボタンの並び、クリックしたときの動きを確かめる Node のテスト63件が、期待値を1つも書き換えずにすべて通りました。これが「今までの振る舞いを変えていない」ことの証拠です。書き換えたのは、共有先の一覧をそのまま書いた「在庫表」のテスト3件だけでした。一覧に bluesky が増えたのだから、在庫表にも1行増えるのが正しい、という種類の修正です。新しく足したのは、3ページ目の textWithUrl を確かめるテスト1件です。
反対に、今までのテストの期待値を書き換えないと通らないとしたら、それは今までの振る舞いが変わってしまったという合図です。足しただけのつもりで、閉じていた共通処理に手が入ってしまったのかもしれません。
たとえば、Bluesky を足したら、X の URL を確かめるテストが落ちたとします。ここで X のテストの期待値を新しい URL に書き換えれば、テストは通ります。けれど、それは「X の URL が変わった」という事実を見えなくしただけです。本当に調べるべきなのは、「Bluesky を足しただけなのに、なぜ X の URL が変わったのか」のほうです。今までのテストは、足したものが今までのものに影響していないかを見張る役目です。落ちたときほど、期待値を簡単に書き換えないようにします。
想定の外は、生成器が止める
差し込み口を用意したら、その口から想定外のものが入り込まないようにすることも大切です。このプラグインでは、それを生成器が受け持っています。3ページ目で見たとおり、名札カードの右側に書けるのは、用意された6つの名前だけでした。そこで、共通処理を直す前の生成器に、textWithUrl と書いた名札カードを読ませてみました。
$ python3 tools/config.py --config config/share_config.example.toml --generate
❌ シェア設定が不正です: bluesky.toml: params は「SNS 側の項目名 = hashtagsCsv / site / text / title / url / via」の形にしてください1行目が実行したコマンド、2行目が生成器の返事です。「bluesky.toml の params は、hashtagsCsv / site / text / title / url / via のどれかで書いてください」と、書ける名前の一覧を示して止まりました。一覧に無い名前を書いても、黙ってそのまま動いてしまうことはありません。口の外から入ろうとする変化は、作り直しの時点で機械が止める。だから、名札カードという差し込み口を安心して用意しておけるのです。止まったということは、「その変化は名札カードだけでは足せない」という知らせでもあります。
差し込み口を開けておくことの代償
それなら、あらゆる変化に差し込み口を用意しておけばよいのでしょうか。そうはいきません。差し込み口を1つ用意するたびに、それを持ち続けるための手間が増えるからです。
名札カードという差し込み口のために、このプラグインが抱えている手間は3つあります。1つ目は、名札カードの書き方の決まりと、決まりを守れているかを調べる検査です。2つ目は、名札カードを読んでブラウザ用の JavaScript ファイルを作り直す生成器と、その作り直しの手順です。名札カードを足しても、作り直しを忘れるとボタンは出ません。3つ目は、共有先の情報が、名札カードと共通処理の2か所に分かれて置かれることです。「Bluesky のボタンはどう動くのか」を調べるとき、名札カードと共通処理の両方を見る必要があります。
名札カードは、新しい SNS というよく来る変化のためのものなので、この手間に見合っています。けれど、めったに来ない変化のためにまで差し込み口を用意すると、手間だけが残ります。差し込み口は、よく来る変化のためだけに開けるのが、手間との釣り合いの取り方です。
新しい口は、いつ開けるか
では、3ページ目で Bluesky のために足した textWithUrl は、プラグインの本体に入れるべきでしょうか。実は、今回の変更は、まだ本体には入れていません。この記事のために、作業用のコピーで試しただけです。本体に入れるかどうかは、次の3つの問いを順にたどって決めます。
1つ目は、その変化の依頼が、いま実際に来ているか。2つ目は、同じ種類の変化が、ほかにも来そうか。たとえば Bluesky のように「URL を本文に含めて渡す」SNS が、ほかにもあるか。3つ目は、差し込み口を持つ手間よりも、次から書き換えずに済む利点のほうが大きいか。「まだ来ていない変化のために、先回りして仕組みを作り込まない」という考え方には、YAGNIという名前があります。Bluesky を足してほしいという依頼が実際に来て、同じような SNS がほかにも出てきたら、そのときに本体へ入れます。
SOLID の入門書でも、同じ考え方がよく勧められています。差し込み口(抽象化)は、最初から全部の場所に作る必要はありません。種類が増えそうかをいつも意識しておき、実際に増えたときに作れば十分です。すべてに差し込み口を用意するのは、やりすぎになります。
このプラグインの差し込み口
最後に、このプラグインに今ある差し込み口を、まとめて見ておきます。名札カードのほかにも、共通処理を書き換えずに足せる場所が3つあります。
| 差し込み口 | 置き場所 | 足せるもの |
|---|---|---|
| 名札カード | destinations/*.toml | 新しい SNS |
| サイトの設定 | config/share_config.toml | 並び・言い回し・色の上書き |
| 部品の差し替え | composition/(DI コンテナ) | コピーなどの実装の取り替え |
| 知らせ(イベント) | document に届く promari-sns-share | 計測の受け手 |
名札カードは、新しい SNS を足す場所です。サイトの設定は、ボタンの並び順や、ボタンに出す文字、色を、サイトごとに変える場所です。部品の差し替えは、たとえば「URL をコピーする」処理の中身を、別の作りに入れ替える場所です。どの部品を使うかを composition フォルダの1か所でまとめて決めているので、そこを変えるだけで入れ替えられます。この仕組みを DI コンテナと呼びます。
知らせ(イベント)は、ボタンが押されるたびに promari-sns-share という名前の知らせを出す仕組みです。アクセス解析などのプログラムは、その知らせを受け取って、どのボタンが押されたかを記録できます。4つとも、よく来る変化に備えて1つずつ用意してあります。部品の差し替えは、連載2.5で詳しく読みます。
考えてみる:差し込み口を増やしすぎると、何が困るの?
差し込み口ごとに、書き方の決まり・検査・作り直しの手順が増え、読む人が覚えることも増えます。しかも、使われない差し込み口も、決まりを変えるたびに合わせて直す必要があり、手間だけが続きます。差し込み口の数は、実際に来そうな変化の数に合わせるのがちょうどよく、それより多く用意するのは、将来の自分に手間を前払いするようなものです。











このページ(5ページ目)の感想・質問・設計へのコメント