PROMARI JOURNAL

SRP――一つの共有先の定義は、一つの理由で変わるか

作成: / 公開: / 内容更新:

執筆:tamito0201 / 掲載・運営:プロマリ

丸い共有ボタンの裏側を掘る連載の第4回。SOLID の最初の原則 SRP を、共有先の名札カード1枚からプラグイン全体まで当てて確かめます。責任とは仕事の数ではなく、変更を頼んでくる人の数。Martin の車の修理のたとえから、実際の変更履歴を数える宿題まで、図22点で読み解きます。

PASS IT ON

ひとつの発見を、次の会話へ。
自然光の差す木の机を正面から撮った写真風のイメージ。4つの仕切りを持つ木の仕分け棚に、紫・青緑・青・黄橙の色札が付き、各仕切りに同じ色の封緘シールの封筒が1通ずつ差し込まれている。手前には紫のシールの封筒と、紫の札を付けた白いカードが1枚だけ置かれている

こんにちは、プロマリの紫です。連載「丸いボタンの裏側」の第4回、今回から第2章に入ります。第2章では、SOLID の5つの原則を1回に1つずつ、このプラグインのコードに当てて確かめます。最初はSRPです。テーマは責任とは仕事の数ではなく、変更を頼んでくる人の数であるということ。共有先の名札カード1枚から、プラグイン全体のフォルダまで、「誰が変更を頼んでくるのか」を1つずつ数えながら読んでいきます。

点線の付いた用語は、その言葉を押すと詳しい説明が開きます。意味、身近なたとえ、この実装での使い方を順に読めます。キーボードではTabで用語へ移動し、EnterまたはSpaceで開き、Escapeで本文へ戻れます。各ページで最初に出る用語から参照できるようにしました。

この記事のコードと図の土台は、promari-sns-share 4.0.0の公開ソースです。既存の実験の件数・差分は執筆時の記録として残し、今回加えた場面は「説明例」「練習例」と区別しています。現在の4.2.2にはComposeや下書きの対応もあるため、ここに載せた操作の種類や行数を、現在版の一覧としては読まないでください。各原則を、同じ実装を土台にして一つずつ確かめるための版の固定です。

この記事は連載「丸いボタンの裏側」の第4回(2.1)です。前回の1.3「ADRに残すのは結論より、捨てた案と制約」で読んだ ADR-0002 を、今回は SRP の目で読み直します。連載全体の地図は、本編5ページ目の連載の目次からどうぞ。

この回は6ページあります。1ページ目で SRP の「一つ」が何を指すのかを確かめ、2〜4ページ目で名札カードと設定ファイルとコードを数え、5ページ目で分けすぎの側とプラグイン全体を見て、6ページ目で「見つけたらどうするか」と宿題に進みます。図は26点あります。どの図も、上の問いに答えてから下の結論を読む順に作ってあります。

本題に入る前に、第2章で読む SOLID の全体像を見ておきます。SOLID は、Robert C. Martin がまとめた設計の5つの原則を、頭文字で並べた呼び名です(この頭文字の呼び名は、のちに Michael Feathers が付けたとされています)。目指しているのは、変更しやすく、変更しても壊れにくく、読んで分かりやすいコードです。

第2章で読む SOLID の5つの原則
頭文字原則ひとことで言うとこの連載の回
SSRP(単一責任の原則)1つのまとまりに、変更を頼んでくる人(役割)は1つだけにする2.1(この回)
OOCP(開放閉鎖の原則)機能を足すとき、今動いているコードを書き換えずに済むようにする2.2
LLSP(リスコフの置換原則)同じ約束を名乗るものは、取り替えても使う側が困らないようにする2.3
IISP(インターフェース分離の原則)使う側に、使わない機能まで押し付けない2.4
DDIP(依存性逆転の原則)大事な判断を、具体的な道具ではなく約束(抽象)に頼らせる2.5

5つに共通するねらいは、2つにまとめられます。一緒に変わるものは近くに集めること(これを「凝集度を高める」と言います)。別々に変わるもの同士のつながりは弱くしておくこと(これを「結合度を下げる」と言います)。そうしておけば、1つの変更の影響が、関係のない場所まで広がりにくくなります。

ただし、SOLID はどんな場面でも守れば正解、という規則ではありません。守ることにこだわりすぎると、ファイルや部品が増えすぎて、かえって読みにくくなったり、作る手間が増えたりします。この章では、5つの原則を1つずつ、この共有ボタンのコードに当てて、「どこで効いているのか」と同時に「どこでは使わないのか」も確かめていきます。

「一つの責任」は、一つの仕事のことではない

SRPは、日本語では「単一責任の原則」と訳されます。この訳を読むと、多くの人がこう受け取ります。1つの関数は1つのことだけをする。1つのクラスは1つの処理だけを持つ。だから、細かく刻めば刻むほど良い。

図1-1 「一つ」は、仕事の数ではなく依頼主の数
図1-1 「一つ」は、仕事の数ではなく依頼主の数。説明。行数や処理の数ではなく、変更を頼んでくる人(依頼主)が1人かどうかで考えます。図1-1 「一つ」は、仕事の数ではなく依頼主の数。説明。行数や処理の数ではなく、変更を頼んでくる人(依頼主)が1人かどうかで考えます。

けれど、この原則を広めたRobert C. Martinは、2014年に書いた記事で、この受け取り方をはっきり正しています。記事は「SRP は、1つのモジュールが変わる理由は1つだけであるべきだ、と言っている」という確認から始まり、すぐに「では、変わる理由とは何か」と問い直します。そして答えとして、Martin は「この原則は、人についての原則だ(This principle is about people.)」と書いています。1つのまとまりに、変更を頼んでくる人が1人だけになっているか。それが SRP の問いです。

図の右側を見てください。x.toml に変更を頼むのは X(SNS各社)だけ、share_config.toml に変更を頼むのはサイトの運営者だけです。どちらのファイルにも項目は何行もありますが、依頼主は1人ずつです。行数が多いか少ないかは、この問いには関係がありません。この回では、この「依頼主の数」を手がかりに、プラグインのファイルを1つずつ数えていきます。

図の細かい文字に入る前に、線の終点だけをたどってみましょう。仕事が五つあっても、最後に同じ依頼主へ集まるなら、五つに分ける根拠にはなりません。反対に、たった二行でも、一行はSNSの仕様、もう一行はサイトのキャンペーンの都合で変わるなら、二つの理由が同居しています。ここで数えたいのは処理の個数ではなく、変更について別々に判断する立場です。

「短い関数にする」という読みやすさの工夫は、もちろん役に立ちます。ただ、それはSRPの判定とは別に行えます。長い処理を小さな関数へ分けても、給与と監査の判断が同じ共有変数を変更し続けていたら、依頼のぶつかり方は残ったままです。関数を分けたあとにも、誰の判断がどの行へ届くかを、もう一度たどるところまでが今回の練習です。

なぜ「人」で数えるのか――頼んでいないところが壊れる

変わる理由を、わざわざ「人」で数えるのはなぜでしょうか。Martin は、記事の中で車の修理にたとえています。窓が動かなくなったので、修理を頼みました。翌日、直ったと連絡があって取りに行くと、窓は確かに動きます。ところが、エンジンがかかりません。その修理工場には、もう二度と頼まないでしょう。

図1.1-1 頼んでいないところが壊れる
図1.1-1 頼んでいないところが壊れる。説明(Martin のたとえ)と説明例。困るのは、頼んでいないところが壊れることです。だから、変わる理由の違うものを1か所に置きません。図1.1-1 頼んでいないところが壊れる。説明(Martin のたとえ)と説明例。困るのは、頼んでいないところが壊れることです。だから、変わる理由の違うものを1か所に置きません。

困るのは、頼んだところが直らないことより、頼んでいないところが壊れることです。頼んだ人は、自分の頼みとは関係のない場所が壊れるとは思っていません。だから、壊れたときの驚きも不信も大きくなります。

共有ボタンでも同じことが起きえます。サイトの運営者に「ボタンの色を少し明るくしたい」と頼まれて直したら、翌日から X の共有画面が開かなくなった。もし色と共有URLが同じファイルの同じ場所に書かれていたら、色を直す作業の途中で、URL の行をうっかり壊すことはありえます。色を頼んだ人も、URL を決めた X も、そんな変更を頼んではいません。変わる理由の違うものを1か所に置かないのは、頼んでいない人の持ち物を壊さないためです。

Martin の例:1つのクラスに、3人の依頼主

Martin は、もう1つ、会社の例も挙げています。社員を表す Employee というクラスに、3つのメソッドがあるとします。

JavaMartin の記事の例(Employee クラス)
public class Employee {
  public Money calculatePay();
  public void save();
  public String reportHours();
}
図1.2-1 Martin の例:1つのクラスに、3人の依頼主
図1.2-1 Martin の例:1つのクラスに、3人の依頼主。説明(Martin の記事の例)。1つのクラスに3人の依頼主がいます。1人の頼みで直した変更が、別の人の担当を壊しうる形です。図1.2-1 Martin の例:1つのクラスに、3人の依頼主。説明(Martin の記事の例)。1つのクラスに3人の依頼主がいます。1人の頼みで直した変更が、別の人の担当を壊しうる形です。

calculatePay は、社員の契約や勤務時間から給与を計算します。save は、社員のデータを会社のデータベースへ保存します。reportHours は、監査の担当者が読む勤務時間の報告を作ります。では、それぞれの「こう動くべき」を決めるのは誰でしょうか。給与の計算方法を決めるのは、お金の責任者である CFO です。保存の仕組みを決めるのは、技術の責任者である CTO です。報告の形を決めるのは、業務の責任者である COO です。

1つのクラスに、3人の依頼主がいます。CFO の頼みで給与の計算を直したとき、うっかり報告の形まで変えてしまったら、困るのは頼んでもいない COO です。Martin は、変更の依頼が「1つの狭く定まった業務の役割を担う、ひとまとまりの人たち」からだけ来るようにしたい、と書いています。1人の人というより、1つの役割の人たち、という意味です。

Employeeという名前だけを見ると、三つとも社員に関する仕事なので、一緒に置きたくなりますよね。ところが、給与計算の締め日を変える判断と、保存先の構成を変える判断は、同じ「社員」の話でも別々にできます。題材が同じことと、変更の理由が同じことを分けて読むと、この例の線が見えてきます。

そこで、図のメソッド名をいったん隠し、三人から届く依頼文を想像してみてください。「手当の計算を変えて」「保存先を移して」「勤務時間の報告を月別にして」。どれか一つを取り下げても、残りの二つは成立します。この独立性が、分ける候補を見つける手がかりです。三つのメソッドがあるから分けるのではなく、別々に承認・延期・取り消しができる判断があるから、境目を考えます。

責任とは、変更を頼んでくる人のこと

変更を頼んでくる人を、この記事では依頼主と呼びます。では、共有ボタンのコードには、どんな依頼主がいるでしょうか。数えてみると、4者に分かれました。

図1.3-1 責任とは、変更を頼んでくる人のこと
図1.3-1 責任とは、変更を頼んでくる人のこと。現在のしくみ(依頼の中身は説明例)。どのファイルにも、変更を頼んでくる依頼主は1人だけです。図1.3-1 責任とは、変更を頼んでくる人のこと。現在のしくみ(依頼の中身は説明例)。どのファイルにも、変更を頼んでくる依頼主は1人だけです。

SNS各社は、共有画面のURLやロゴ、呼び名を変えます。サイトの運営者は、どのボタンを大きく出すか、どんな言葉で出すかを変えたくなります。ブラウザと標準は、コピーの仕組みや端末の共有画面の呼び出し方を変えます。保守する開発者は、検査を足したり、書き方を整えたりします。4者は、変えたい理由も、変えたい時期も、ばらばらです。

このプラグインの4人の依頼主
依頼主変わるきっかけの例届け先
SNS各社共有画面のURL・ロゴ・呼び名が変わったdestinations/<key>.toml
サイトの運営者ボタンの並び・言い回し・色を変えたいconfig/share_config.toml
ブラウザと標準コピーや共有画面の仕組みが変わったweb/src/infrastructure/
保守する開発者検査を足したい・書き方を整えたいweb/test/・tools/

このプラグインでは、4者の依頼がそれぞれ別の場所へ届くように置き場所を分けています。表の「届け先」の列が、それぞれの依頼の届け先です。個別の定義や設定の編集先を分けるための、大まかな見取り図です。フォルダ全体に一種類の変更しか来ないことを保証するものではありません。

考えてみる:同じ人が、SNSの担当とサイトの運営を兼ねていたら、依頼主は1人になるの?

依頼主は、人の数ではなく、役割の数で数えます。同じ人が X の仕様変更に追従する作業と、サイトのボタンの並びを変える作業を両方しても、その2つの変更は別々の理由で、別々の時期にやって来ます。ファイルの側から見れば、2つの別の都合が届いていることに変わりはありません。Martin の記事でも、変更の出どころは1人の人というより、「1つの狭く定まった業務の役割を担う、ひとまとまりの人たち」と説明されています。

同じ人でも、承認する立場が二つなら分けて考える

先ほどの問いを、もう少し絵で追ってみましょう。一人で開発と運営をしているサイトでも、SNS側の仕様に合わせる仕事と、自分のサイトの見せ方を決める仕事はあります。次の図では、人の顔は一つのまま、手元の名札だけを二つに分けました。

図1.4-1 一人でも、判断する立場は二つ
図1.4-1 一人でも、判断する立場は二つ。説明例(役割と人数の違い)。人は一人でも、判断の基準が二つなら、二つの役割として考えます。図1.4-1 一人でも、判断する立場は二つ。説明例(役割と人数の違い)。人は一人でも、判断の基準が二つなら、二つの役割として考えます。

SNS仕様の名札を掛けているときは、共有画面へ渡す引数が仕様どおりかを確かめます。サイト運営の名札を掛けているときは、読者にどのボタンを先に見せたいかを判断します。図で二枚の名札から別々の承認印が出ているのは、片方が決まっても、もう片方まで決まるわけではないからです。

逆の場面もあります。三人の担当者が同じ運営方針に従って、交代で並び順を直しているなら、三人いるというだけで設定を三分割する必要はありません。三人が同じ基準で一つの判断を引き受けているなら、この記事では一つの役割として扱えます。人の数をそのままファイルの数へ移すと、この向きを取り違えてしまいます。

実際のレビューでは、「最後にこの変更をよしとするのは、どの立場か」と聞くと話が具体的になります。コードを書いた人の名前よりも、変更を必要にした判断へ戻るのです。SNS各社やブラウザは、このリポジトリに直接依頼票を送ってくるわけではありません。外部の仕様変更を、開発者が変更依頼として受け取っている、という見立てで読んでください。

では、名札の図を使って、三つの依頼を仕分けてみます。「SNS側が共有のURLを変えた」「今週だけXを先頭へ置く」「すべてのサイトで表示名を正しくする」。一つ目はSNS仕様の名札へ、二つ目はサイト運営の名札へ向かいます。三つ目は、どこで使う表示名かを確認する必要があります。依頼文を読んだだけでは、共通の定義の誤りなのか、特定のサイトの呼び方なのかが決まらないからです。

この迷いを、そのまま質問へ変えます。「変更した名前は、このサイトだけで使いますか。それとも配布先すべての既定値ですか」。答えが出れば、同じ表示名という項目でも、置き場所を選べます。SRPは、人の名前をファイルへ貼る作業ではなく、変更の適用範囲を確認する会話にも使えるのです。

仕分けた結果は、役割名と編集先を一行で対応させて残してみてください。SNS共通の意味を直すなら名札カード、サイトごとの採用や並びを直すならサイト設定、という形です。迷った依頼ほど、この対応表が役に立ちます。次に同じ言葉の依頼が来ても、前と同じ範囲の変更なのかを確かめてから作業できます。

この見立ては出発点であって、役割の一覧を永久に固定するためのものでもありません。サイトが増えたり、運営の担当が分かれたりすれば、同じ設定の中にも別々に変わる判断が見えてくることがあります。まず今の変更を説明できる名前を付け、履歴が増えたら見直す。そのくらいの使い方で、十分に設計の会話を進められます。

COMMENTS
コメント…

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

PASS IT ON

この気づきを、誰かにも。

この記事を書いた人

Takaomi Murasaki

Promari SNS Shareの開発を通して、Webの仕組みや設計の選び方を紹介しています。動く実装と、その判断に至るまでを記事に残しています。

公開記事 11 件

プロフィールを見る
THANK YOU FOR READING.すべての記事へ ↗

FROM INSIGHT TO IMPACT

「できたらいいな」を、
動く仕組みに。

記事で見つけたヒントを、あなたの事業へ。
新しいサービスも、手間のかかる業務も。
いまの課題から、つくるべきものを一緒に考えます。

開発・AI・研修の実績を見る

まだ、仕様書はいりません。

「何から始める?」から、ご一緒に。

課題がまとまっていなくても大丈夫。テーマを選ぶと相談文をご用意します。
連絡先の必須入力は、お名前とメールアドレスだけ。

まずは課題の整理から相談する

ご相談後の流れ

  1. 01 内容を確認
  2. 02 メールでご連絡
  3. 03 課題・進め方をご相談
送信だけで契約やお申込みが確定することはありません。