書き忘れた一通
わたしはリタ。冒険者ギルドで、報せ係をしている。
ギルドは、冒険者にいろんな報せを送る。依頼の締切、急ぎの招集、討伐が済んだという完了の報せ。届け方も、いくつかある。文鳥便——文鳥に文(ふみ)を持たせて運ぶ、詳しいけれど少し遅い便り。早鐘の短信——鐘を鳴らして報せる、短くて速いやつ。それに、最近入れたばかりの光触れ。空に光の印をぱっと灯して、その場の全員に届く、いちばん急ぎの報せ。報せは、要る人に、要る速さで届けてこそ。わたしは、それだけを守ってきた。
ただ、ずっと困っていることがあった。届け方が一つ増えるたびに、わたしは、報せを送る窓口の全部に、同じ書き足しをして回らなきゃいけない。締切の窓口、招集の窓口、完了の窓口……どこも、「この合言葉なら、この届け方」という振り分けを、それぞれが抱えている。種類が増えれば、その全部に、同じ一行を書き写す。台帳を何冊も、同じ手で書き換えるみたいに。
そして、先日。新しい光触れを入れたとき、わたしは、やってしまった。
締切の窓口には、ちゃんと光触れを足した。なのに——招集の窓口に、足し忘れた。
その日、急ぎの討伐の招集を、光触れで出そうとした。なのに招集の窓口は、光触れなんて知らない、という顔で、わたしの報せを撥ねた。冒険者に急報は届かず、討伐隊は、定刻に集まらなかった。
わたしが、たった一箇所、書き忘れただけで。
きっちりやってきたつもりだった。でも、窓口の数だけ同じことを書いて回る限り、どこか一つ抜ければ、こうなる。報せの種類は、これからも増える。このままじゃ、また誰かに、急報が届かない。
丘の見習いの名
心当たりは、あった。
報せ係をしていると、いろんな礼状や口コミが、わたしのデスクを通る。それで近頃、ルークという見習いの名を、何度も見かけていた。雑貨屋の会計を直した、蔵書庫の検索台を直した、町の地図を直した——その礼が、みんな、ここを通っていったのだ。だから丘の上のアカデミーへ上るのに、迷いはなかった。ああ、噂のあの子のところへ行けばいい、と。
工房は、古い書物の匂いで満ちていて、見上げれば、壁という壁が書架で埋まっていた。報せ係のわたしの目は、それより先に、隅で山積みになった文机のほうへ吸い寄せられた。
その奥で、見習いらしい若者が一人、わたしの用も聞かないうちから、机いっぱいに広げた大掛かりな魔法陣に、夢中で手を入れていた。何かを組み上げている最中らしい。わたしが声をかけると、「あ、いいところに!」と顔を上げる。これが、噂のルークか。思っていたより、ずいぶん若い。それに——ずいぶん、忙しそうだ。
わたしは事情を話して、散らばった振り分けの符を見せた。ルークはそれを手元に引き寄せ、まず報せにまつわる「もの」から、順に説いてくれた。
| |
Notification というのが、報せの「形だけの約束」——send と頼めば報せを送る、ということだけを決めた取り決めだ。これを インターフェース(中身ではなく「こう呼べばこう応える」という形だけを取り決めた設計図)と呼ぶのだそうだ。文鳥便も早鐘も、その約束を守って、それぞれの送り方をしている。「ここは、いいんです」とルーク。「問題は、こっちでした」。
彼が指したのは、窓口のほうだった。
| |
when というのは、合言葉(type)によって行き先を振り分ける仕掛けだ。"mail" なら文鳥便、"bell" なら早鐘、という具合に。「締切の窓口にも、招集の窓口にも、まったく同じ振り分けが、そっくり書いてありますね」とルークが言う。そう、それだ。「届け方を一つ増やすたびに、わたし、全部の窓口に、同じ一行を書き足すの」。
ルークは小さく頷いた。「一つ直すために、あちこちの同じ箇所を、全部直して回らなきゃいけない。この状態には、名前があるんです」。
散弾銃手術——一箇所を変えるために、散らばった複数の箇所を、まとめて撃ち直す羽目になる状態のことを、魔術師はそう呼ぶのだという。光触れを足したとき、締切には書いたのに招集に書き忘れた。だから招集だけ、光触れを知らなかった。わたしのあの書き忘れには、ちゃんと名前がついていたのか。
究極の報せ生成陣
ところが、だ。
ルークの手が、うずうずと動いた。さっきまで夢中で手を入れていた、あの大掛かりな魔法陣のことが、頭から離れていなかったらしい。
「リタさん、ちょうどいい。これを機に——どんな報せにも、未来のどんな届け方にも応える、究極の報せ生成陣を、組み上げます!」
言うなり、彼は宙に、階また階を積み上げ始めた。報せを作る工房。その工房を作る、もう一段上の工房。
| |
abstract と付いたのは、それ自体では中身を持たない骨組みなのだと、後でルークが教えてくれた。報せを作る工房があり、その工房を選んで作る、もう一段上の工房がある。それだけでは終わらなかった。「報せに宛先や本文を、一画ずつ設える組立台も要りますね。それと、よく似た報せを雛形から写し取る、写し台も」。階の上に、また階。
わたしの目には、もう、何がどの階なのか、まるで追えなかった。「待って、ルーク。わたしが頼んだのは、報せを書き忘れずに届けたい、それだけなの。なんで……こんな、お城みたいになるの?」
一通送るだけで、ひと仕事
ルークは陣を起動して、試しに一通、文鳥便を送ろうとした。
ところが、その一通が、なかなか出ない。工房を選ぶ上の工房を呼んで、選ばれた工房に報せを作らせて、それからやっと送る。一通送るのに、階段を上って下りるみたいな大仕事だ。
しかも、だ。わたしは、いちばん肝心なことを尋ねた。「ねえ、それで。新しい届け方を足すときは、どうなるの?」
ルークは、説明しようとして、口ごもった。「ええと……新しい届け方ごとに、専用の工房クラスを一つ作って……それを、上の工房の振り分けにも足して……あと、報せのクラスも……」。
わたしは、思わず指を折って数えた。前は、窓口に一行ずつ書き足していた。それでも書き忘れた。なのに今度は、新しい届け方を一つ増やすのに、「報せのクラス」と「その専用の工房」と「上の工房の振り分け」と、前より多くの場所に、手を入れなきゃいけない。減らしたかったはずの手間が、増えている。お城を建てたのに、引っ越しがもっと大変になったみたいだ。
そのときだった。工房の奥の文机で、紙の擦れる、かすかな音がした。見れば、白髪頭の男がひとり、山と積まれた古い手紙を、一通ずつ静かに選り分けている。いつからそこにいたのだろう。顔も上げないまま、その人は、低くこう漏らした。
「一通運ぶのに……鳥は、一羽で、よかろうに」
ルークは、自分の建てた塔の中で迷子になっていて、聞こえていない。わたしのほうも、出入りの文使いが何か言ったのだろうと思い込んで、聞き流してしまった。
「明日もまた、招集を出すのよ」と、わたしは急かした。「これで、間に合うの?」。ルークは、自分が積み上げた階のどこで何が起きるのか、もう自分でも説明できなくなっていた。最初にわたしが持ち込んだ符より、ずっと大層なものになっている。とうとう、頭を抱えてしまった。
文を仕分ける老人
奥の老人が、手紙の束を、とん、と揃えて置いた。
そして、塔に埋もれたルークのほうへ、静かに声をかけた。その声に、ルークの背すじが、びくりと伸びた。積み上げた塔から慌てて顔を出すと、「……お、お師匠さま」と、消え入りそうな声で応える。わたしは、目をしばたたいた。文机で手紙を選り分けていた、あの人。出入りの文使いか何かと思い込んでいたその人こそ——この子の師匠、大魔術師マグナス様、その人だったのだ。
マグナス様は、積み上がった塔には、目もくれなかった。ただ、手の中の一通の手紙を、軽くかざして、問うた。
「お前さんは、立派な塔を建てた。だが——この報せを一つ、相手に届けるのに、その塔の、どれだけが要るのかね?」
ルークは、答えられない。
「“一番小さな道具で足りないか” と問うのが、いつでも先だよ」
マグナス様は、それだけ言うと、手の中の手紙へ目を落とし、また一通、選り分け始めた。
ルークは、しばらく自分の塔を見上げていた。それから、組み上げかけた巨大な生成陣から、ふっと手を離す。代わりに、机の隅に転がっていた、なんの飾りもない、一本の素な符を、つまみ上げた。
「一通の報せに、こんな大層な陣は……要らなかった。作る役目を、ひとつの場所に、ひとつだけ。それで、よかったんだ」
ひとつの場所に、ひとつだけ
ルークは、宙に積み上げた塔を、下の階から順に、ことごとく畳んでいった。階も、工房を作る工房も、跡形もない。代わりに、机の上に、一本の呪文を書いた。
| |
「どの届け方を作るか、を決める役目を、createNotification という一箇所の呪文に、ぜんぶ集めました」とルーク。「『ものを作る役目』を一箇所に閉じ込めて、種類を増やすときも、そこだけ直せばよくする。この定石を、そう呼ぶそうです」。
Factory Method——ものを作る役目を専用の窓口(呪文)に集約し、種類の追加を、その一箇所だけで済ませられるようにする設計の型なのだという。そして、窓口のほうは、こうなった。
| |
締切の窓口も、招集の窓口も、もう振り分けを持っていない。ただ、createNotification に「これ作って」と頼んで、出てきた報せを送るだけ。あの、そっくり重複していた when が、両方から、きれいに消えている。
ルークは、自分の頭の中のイメージを整理するように、指先で小さく魔法陣を描いた。 「言葉だけで関係性を追うと、何がどこに依存しているか、少し見えづらいですよね。ちょっと魔力投影で、今回の構造を書き出してみます」
彼の指先から放たれた薄紫色の魔力光が、空中にスクロール状の設計図を広げた。そこに金と青のラインで描かれたのは、すっきりと整理された魔法式(クラス関係図)だった。

「わあ……相変わらずルークの魔力投影は綺麗ね」と、わたしは目を輝かせた。「ええと、一番上が窓口(Client)で、左が生成(Factory)ね」
「はい!」と、ルークは嬉しそうに頷いた。「ポイントは、窓口が具体的な届け方(MailFamiliar や LightSign)を一切見ていない点です。窓口は『作る呪文(createNotification)』を呼び出し、返ってきた『報せの境界(Notification インタフェース)』に向けて送信の合図を送るだけ。具体的な具象クラスは、すべて右下の境界線の向こう側に隠されています」
「なるほど……。窓口と届け方の間に、一枚の遮蔽板があるようなものね」
「その通りです! この遮蔽板(インタフェース)と、生成を一手に引き受ける窓口(Factory)のおかげで、依存のねじれが解消されているんです」
「じゃあ」と、わたしは身を乗り出した。「新しい届け方を足すときは?」
「この一箇所に、一行です。窓口には、もう、触りません」。
わたしは、ゆっくりと呑み込んだ。「……それって。あの、わたしの書き忘れが、起きる場所が——無くなる、ってこと?」
「はい」とルークは頷いた。「振り分けが一箇所だけなら、書き忘れようがありません。締切にも招集にも完了にも、同じ振り分けを書き写す必要が、もうないんです」。
ルークは、少し決まり悪そうに、付け加えた。
「ついでに、白状します。さっき僕は、報せに宛先や本文を一画ずつ設える『組立台』や、雛形を写す『写し台』まで、組もうとしていました。でも——もし報せに宛先や本文を持たせたくなっても、Kotlin なら、器を data class にして、作るときに名前を添えれば、一息に書けます。専用の組立台なんて、要らない。写しのほうも、その data class に備わった copy で済む。言語がもう肩代わりしてくれている道具を、僕は、わざわざ自分で組もうとしていたんです」。
大仰な台が、彼のひと言で、すっと消えた。
「もう一つだけ、いいですか」とルーク。「今は、届け方を "light" みたいな文字の合言葉で渡しています。これを、文字じゃなく型で表すと、さらに堅くなるんです」。
| |
頭の sealed interface は、「取りうる届け方は、この一族だけ」と、型で出し切って封じる宣言なのだそうだ。文鳥便・早鐘・光触れ。この三つきり、と決めてしまう。それぞれの届け方は object——たった一つきりの実体で、名前のついた目印のようなもの——で表すから、MailFamiliar() のように作るのではなく、Channel.Mail とそのまま指す。文字版の窓口では、どんな文字が紛れ込むか分からないぶん、「その他」を受け止める else が要った。けれど sealed で届け方の一族を出し切ってしまうと、取りうる種類をコンパイラが残らず把握できる。だから三つの枝をすべて書けば「その他」はもう存在せず、else は要らない——裏を返せば、どれか一つでも書き忘れれば、その場で「足りない」と見抜かれる。これは、合言葉の文字を最初から型に置き換えた別の設計で、さっきの文字版の窓口にあとから接ぐ話ではない。窓口が "light" という文字を持ち回るか、Channel.Light という型を持ち回るか——その違いだけだ。
「こうすると」とルーク。「もし新しい届け方を一族に足して、この呪文に枝を書き足すのを忘れたら——魔法を編む段で、『枝が一つ足りない』と、はじかれます。送ってみるより前に、組み上がるより前に、気づける」。
「じゃあ……もう、誰も、忘れられないのね」。わたしは、思わず呟いた。あの日の、たった一箇所の書き忘れ。あれを、そもそも起こせなくしてしまう。
「散らばりを直したのは、さっきの集約のほうです」と、ルークは念を押した。「こっちは、その上に重ねる、念のためのもうひと押し。文字のままでも、集約だけで、散弾銃手術はちゃんと直っています」。
結界で、試しの報せを
「ギルドに本物の報せを流す前に、結界の中で、宛先のない試しの報せを何通か出して、狙いどおり届くか検めます」。
ルークは、そう言って模擬詠唱に取りかかった。わたしの目の前で、試しの報せを一通ずつ送ってみせ、出てきた答えを、その都度わたしに見せてくれる。
| |
assertEquals は「出てきた報せが、刷り見本どおりか」を照らし合わせる道具。assertIs は「出てきたのが、狙った種類の報せか」を確かめる道具。assertFailsWith は「無い届け方を頼んだら、ちゃんと撥ねるか」を検める道具だという。
一つずつ、わたしの目の前で答えが出た。文鳥便、早鐘、光触れ。どれも刷り見本どおり。fax なんていう無い届け方は、ちゃんと撥ねられる。そして——最後の一つ。光触れを足すのに、招集の窓口(noticeSummon)には、わたしは一行も書き足していない。なのに、光触れの招集が、ちゃんと出た。あの日、撥ねられたあの一通が、今度は、出る。
結界の中に、「ALL TESTS PASSED」——みな、狙いどおり——と灯った。
一箇所だけ、足せばいい
整え直された報せ場を、わたしは受け取った。お城みたいな塔も、積み上がった階も、跡形もない。机の上にあるのは、生成を集めた一本の呪文と、驚くほど短い窓口だけ。
これなら……届け方を増やしても、もう、窓口を全部書いて回らなくていい。一箇所だけ。たった、一箇所。窓口の数だけ同じ振り分けを書き写して、どこか抜けていないかと毎晩見直していた、あの日々が、嘘みたいだ。肩の荷が、すっと下りた。
わたしも、自分の手で、もう一つ新しい届け方を足してみることにした——狼煙(のろし)。集約の呪文に一行、報せのクラスを一つ。それだけで、締切も、招集も、完了も、窓口には一切触れないまま、狼煙で出せるようになった。あの書き忘れは、もう、起こりようがない。
奥のマグナス様が、選り分けの手の合間に、こう寄こした。
「道具は、大きいから偉いのではない。手の中で、過不足ないのが、いちばん偉いのだよ」
わたしは礼を言って、丘を下りた。明日の招集は、もう、誰の手からもこぼれ落ちない。届けるべき報せが、届けるべき人のもとへ——あの日撥ねられた一通を思えば、それだけで、胸の奥が、静かに凪いでいた。
〔エピローグ〕見習いルークの覚え書き
その夜、工房の灯を細めて、僕は魔導書を開いた。
この季節、訪ねてくれた人たちのことを、書き留めておこうと思ったのだ。雑貨屋の会計のときは、割引のために、枝分かれする系統樹を盛り上げた。蔵書庫のときは、空かもしれない本を、幾重もの結界で囲った。地図のときは、座標を見張る伝令の網を、宙いっぱいに張った。そして今日は、報せ一つのために、工房を作る工房の塔を建てた。
並べて書いてみて、気づいてしまう。僕の手は、いつも、習った中でいちばん強い魔法、いちばん大きな仕掛けのほうへ、まっすぐ伸びていた。どこかで——強力な魔法ほど、偉い。そう、思い込んでいたのだ。そのたびに師匠は、たった一言で、僕をいちばん小さな道具のところへ、連れ戻した。
ペンを止めて、ふと思う。なぜ、僕の手は、いつも大きいほうへ伸びるのだろう。
次は——盛りたくなる、その手を止めて。誰かに言われるより先に、自分で一度、問うてみよう。「一番小さな道具で、足りないか」と。それができたとき、僕はきっと、ほんの少し、見習いから遠ざかる。
📖 見習い魔術師ルークの魔導書メモ(Luke’s Spellbook Notes)
- 修復した呪文(クラス/モジュール名):
createNotification(生成を集めた呪文)とNotification一族(文鳥便MailFamiliar・早鐘ShortBell・光触れLightSign) - 乱れの要因(アンチパターンの課題):
- 「どの届け方を作るか」の振り分け(
when)が、締切・招集など複数の窓口に、そっくり重複していた(散弾銃手術)。新種を足すには全窓口に書き足す必要があり、一箇所書き忘れて、急ぎの招集が光触れで出せず、討伐隊が集まらなかった。
- 「どの届け方を作るか」の振り分け(
- 過剰設計の反省(背伸びの過ち):
- 「究極の報せ生成陣」と意気込み、工房を作る工房の塔に、組立台(Builder)と写し台(Prototype)まで積んだ。だが種類ごとに専用の工房クラスが要るため、新種追加は「報せ+専用工房+上の工房の振り分け」と、かえって触る箇所が増え、一通送るのに階を上り下りするだけになった。第1部の背伸びが頂点に達した。宛先や本文を添える組立台は名前付き引数が、雛形の写し台は
copy()が、言語の地力でもう肩代わりしていた。
- 「究極の報せ生成陣」と意気込み、工房を作る工房の塔に、組立台(Builder)と写し台(Prototype)まで積んだ。だが種類ごとに専用の工房クラスが要るため、新種追加は「報せ+専用工房+上の工房の振り分け」と、かえって触る箇所が増え、一通送るのに階を上り下りするだけになった。第1部の背伸びが頂点に達した。宛先や本文を添える組立台は名前付き引数が、雛形の写し台は
- 魔術の定石(学んだ設計パターン):
- Factory Method。「ものを作る役目」を
createNotification一箇所に集め、各窓口は作って送るだけにした。新種追加はクラス1つ+集約の1分岐で、窓口は不変(修正に閉じ、拡張に開く=開放閉鎖の原則)。さらに届け方をsealed interfaceの型で表すと、種類を足して枝を書き忘れた瞬間にコンパイルで弾かれ、散弾銃手術の再発(書き忘れ)を型で封じられる。直したのは集約、sealedはそのだめ押し。
- Factory Method。「ものを作る役目」を
- 模擬詠唱の記録(検証のポイント):
- 各届け方が刷り見本どおり生成・送信されること(
assertEquals/assertIs)、無い届け方は撥ねること(assertFailsWith)、新種を足しても窓口の呼び出しは一文字も変えずに通ること(局所性)を確認した。sealed版は、コンパイルが通ること自体が「種類を漏れなく拾えている」証になる。
- 各届け方が刷り見本どおり生成・送信されること(
- 師匠マグナスの格言:
- 「“一番小さな道具で足りないか” と問うのが、いつでも先だよ。」
