PROMARI JOURNAL

OCP――共有先を一つ足したときの差分を追う

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

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

丸い共有ボタンの裏側を掘る連載の第5回。SOLID の2つ目の原則 OCP を、本物の SNS(Bluesky)の追加を通じて確かめます。名札カード1枚で済む変化と、共通処理のコードを変更することになる変化。差分を1行ずつ追いかけ、閉じる向きは変化の来やすさで選ぶことを、図22点で読み解きます。

PASS IT ON

ひとつの発見を、次の会話へ。
自然光の差す木の机を斜め上から寄って撮った写真風のイメージ。白い延長タップの4つの口のうち3つに紫・青緑・青のプラグが差さり、空いた4つ目の口へ手が黄橙色の新しいプラグを差し込もうとしている。コードの先には壁のコンセントが柔らかくボケて見える

こんにちは、プロマリの紫です。連載「丸いボタンの裏側」の第5回、第2章の2回目です。SOLID は、変更に強いコードを書くための5つの原則を、頭文字でまとめた呼び名です。前回はその最初の原則 SRP を読み、「責任とは、変更を頼んでくる人の数」だと確かめました。今回は2つ目の原則、OCPです。テーマは足すときに、すでに動いているコードを直さずに済むかということ。本物の SNS を1つ実際に足し、そのときの差分を1行ずつ追いかけます。

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

この記事は連載「丸いボタンの裏側」の第5回(2.2)です。前回の2.1「SRP――一つの共有先の定義は、一つの理由で変わるか」で、依頼主ごとに置き場所を分けた形を先に読むと、今回の話がつながりやすくなります。連載全体の地図は、本編5ページ目の連載の目次からどうぞ。

この回は6ページあります。1ページ目で OCP の意味を確かめ、2〜3ページ目で Bluesky を実際に足して差分を追い、4ページ目で「どんな変化なら、今のコードを書き換えずに足せるのか」を整理し、5ページ目で、それをテストで確かめる方法と、備えるための手間を考え、6ページ目で前回の SRP とのつながりと宿題に進みます。図は22点です。ここで示す件数や行数は、すべて実際に手元で動かして測ったものです。

足すときに、すでにあるコードは直さない

OCP は、日本語では「開放閉鎖の原則」と訳されます。開いているのに閉じている、という言い方は、最初は矛盾して聞こえます。この原則が言っているのは、機能を足すことには開いていて、すでに動いているコードを書き換えることには閉じている、という形です。

図1-1 足すときに、壁の配線は直さない
図1-1 足すときに、壁の配線は直さない。説明。新しい家電は、空いた口に差すだけ。壁の中の配線は開けません。OCP はこの形をコードに求めます。図1-1 足すときに、壁の配線は直さない。説明。新しい家電は、空いた口に差すだけ。壁の中の配線は開けません。OCP はこの形をコードに求めます。

身近なもので言えば、延長タップです。新しい家電を買ってきたら、空いた口に差せば使えます。壁の中の配線を開け直したり、ブレーカーを付け替えたりはしません。壁の配線は「閉じて」いて、延長タップの口は「開いて」います。

共有ボタンでいえば、X・LINE・Facebook が差さった延長タップに、新しく Bluesky を差す場面です。OCP にかなった作りなら、Bluesky のプラグを差すだけで済み、X や LINE のために書いたコードは書き換えません。この回は、この「差すだけで済むか」を、実際に Bluesky を足して確かめていきます。

ここで、このプラグインが共有ボタンをどう作っているかを、先に確かめておきます。共有先ごとに、この連載で名札カードと呼んでいる小さな設定ファイルが1枚ずつあります。置き場所は destinations フォルダで、X なら x.toml、LINE なら line.toml です。次が X の名札カードです。

TOMLdestinations/x.toml(X の名札カード・icon の行は省略)
# X(旧Twitter)のポスト画面。
key = 'x'
label = 'ポスト'
brand_color = '#000000'
action = 'open'
endpoint = 'https://twitter.com/intent/tweet'

[params]
url = 'url'
text = 'text'
hashtags = 'hashtagsCsv'
via = 'via'

上から、共有先の名前(key)、ボタンに出す文字(label)、ボタンの色(brand_color)、押したときの動き(action)、開く共有画面の住所(endpoint)です。ここでは省いたアイコン(icon)を合わせて、名札カードの項目は7つあります。いちばん大事なのは、最後の [params] です。これは「SNS に渡す値の対応表」で、左側が SNS の受け取る名前、右側がこのプラグインの用意した値の名前です。たとえば url = ‘url’ は、「X が url という名前で受け取る欄に、いま読まれている記事の URL を入れる」という意味です。

ボタンが押されると、プラグインはまず、いま開いているページのタイトルや URL を1つにまとめます。コードでは ShareRequest という部品です。次に、名札カードの endpoint の後ろに、[params] の対応表どおりに値を並べて、共有画面の URL を作ります。こちらは ShareDestination という部品です。X なら、https://twitter.com/intent/tweet?url=(記事の URL)&text=(共有文)… という URL ができ上がります。この手順は、どの SNS でも同じです。SNS ごとに違うのは、名札カードの中身だけです。この記事では、どの SNS でも同じ手順で動くこの部分を、共通処理と呼びます。

なぜ、動いているコードを直したくないのか

そもそも、どうして「すでに動いているコードを直さない」ことが大事なのでしょうか。理由は、直した瞬間に、そのコードに頼っていたすべてのものを確かめ直す必要が出てくるからです。X のボタンのために書いたコードを、Bluesky を足すついでに1行でも書き換えれば、X のボタンが今までどおり動くかを、もう一度確かめなければなりません。LINE も、Facebook も同じです。拡張のたびに、それまでに作ったすべてのボタンを確かめ直すことになります。

逆に、足すものが新しいファイルとして独立していれば、確かめるのは新しく足した Bluesky のボタンだけで済みます。すでにある X や LINE のボタンは、今までのテストをそのまま流して、通ることを見れば十分です。OCP が目指しているのは、この「新しく足したところだけを確かめれば済む」形です。

「閉じている」は、「もう触れない」ではない

OCP を最初に言葉にしたのは、Bertrand Meyerです。1988年の本『Object-Oriented Software Construction』で、よいモジュールは「開いていて、かつ閉じている」べきだと書きました。

図1.2-1 Meyer の「開いている」と「閉じている」
図1.2-1 Meyer の「開いている」と「閉じている」。説明(Meyer の定義・1988)。「閉じている」は「もう触れない」ではなく、「ほかが安心して頼れる」という意味です。図1.2-1 Meyer の「開いている」と「閉じている」。説明(Meyer の定義・1988)。「閉じている」は「もう触れない」ではなく、「ほかが安心して頼れる」という意味です。

Meyer の定義では、「開いている」とは、あとから機能を足せることです。データに項目を足したり、できることを増やしたりできる状態を指します。一方の「閉じている」は、ほかのモジュールから使ってよい状態として仕上がっていることです。具体的には、そのモジュールのインターフェース、つまり外から使うときの名前・渡す値・返ってくる値が決まっていて、もう変わらないことを指します。

ここが、よく誤解されるところです。「閉じている」は「もう誰も触れない」という意味ではありません。「閉じている」とは、ほかが安心して頼れるほど、使い方の約束が安定していることです。使い方が変わらなければ、それを使うコードは書き換えずに済みます。機能を足せることと、ほかのコードが安心して頼れること。この2つを1つのモジュールが同時に満たしている状態が、Meyer の言う「開いていて、閉じている」です。

共通処理はプラグインを知らない。プラグインが共通処理を知っている

Meyer から25年あまりのちの2014年、Robert C. Martinは、OCP を今の言葉で説明し直しました。Martin の記事の中心にあるのは、次の一文です。「システムを変更せずに、その振る舞いを拡張できるべきだ(You should be able to extend the behavior of a system without having to modify that system.)」。そして、その形の手本として、テキストエディタやゲームのプラグインを挙げています。

図1.3-1 プラグインが共通処理を知り、共通処理はプラグインを知らない
図1.3-1 プラグインが共通処理を知り、共通処理はプラグインを知らない。現在のしくみ(考え方は Martin の記事から)。共通処理がプラグインを知らないので、プラグインを何枚足しても共通処理は変わりません。図1.3-1 プラグインが共通処理を知り、共通処理はプラグインを知らない。現在のしくみ(考え方は Martin の記事から)。共通処理がプラグインを知らないので、プラグインを何枚足しても共通処理は変わりません。

プラグインで拡張できる仕組みには、決まった向きがあります。Martin は「システムはプラグインを知らない。プラグインがシステムを知っている(The system doesn’t know about the plugins. The plugins know about the system.)」と書いています。この記事のプラグインに当てはめると、名札カードの1枚1枚がプラグインで、共通処理がシステムです。名札カードは、共通処理が決めた7つの項目の書き方に従って書かれます。これが「プラグインがシステムを知っている」側です。一方で、共通処理のコードには、x や bluesky といった SNS の名前が1つも書かれていません。これが「システムはプラグインを知らない」側です。だから、名札カードを何枚足しても、共通処理は変わらないのです。

考えてみる:共通処理が SNS の名前を知らないなら、X のボタンと LINE のボタンはどうやって見分けているの?

見分けていません。共通処理が知っているのは「名札カードには key・action・endpoint・params がある」という書き方だけです。X の名札カードには X の共有画面の URL が、LINE の名札カードには LINE の URL が書いてあるので、共通処理は同じ手順で URL を組み立てるだけで、それぞれ正しい共有画面へたどり着きます。違いはすべて名札カードの値に入っていて、共通処理の手順は1つです。連載1.2で読んだ「URL を組み立てるのは1か所だけ」も、この形のことです。

COMMENTS
コメント…

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

PASS IT ON

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

この記事を書いた人

Takaomi Murasaki

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

公開記事 10 件

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

FROM INSIGHT TO IMPACT

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

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

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

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

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

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

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

ご相談後の流れ

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