第1幕 異変——何も壊れていない朝に
寄港基地の朝は、船の朝と匂いが違う。
〈オリオール号〉はフォマルハウト基地の第3ドックに入って四日目になる。補給と積み替えのための停泊だ。ドックには本物の重力があって、床は靴の裏を素直に押し返してくる。空気は船内より湿っていて、どこか土に近い匂いが混じる。遠くで荷役クレーンが唸り、コンテナの角が桟橋を擦る音が響く。船の機関室の、あの油と金属だけでできた乾いた世界に二十年余り漬かってきた身には、港の朝というのは毎回、少しだけ他所の家だ。
その朝、船長室から作業指示書が回ってきた。宛名の欄には機関士ダグとある。俺のことだ。
指示書の中身は、悪い話じゃなかった。むしろ久々の大きな仕事だ。——寄港先の惑星研究局から、新型探査プローブの運用受託が決まった。深部大気観測用。ガス惑星の大気の深いところへ降ろす耐圧型で、うちの船の発進管から射出する。ついては発進管の改修と、発進管制プログラムの改修見積もりを出せ、とある。
プローブというのは、船から撃ち出す無人の探査機のことだ。うちの船は貨物のついでにこの手の観測業務を請けていて、いま積んでいるのは三種類。観測用、通信中継用、サンプル回収用。船体側面の発進管——プローブを納めて撃ち出す筒の列——の整備は、機関士の持ち場だ。つまり俺の縄張りである。
発進管の物理改修のほうは、見積もるまでもなく楽しい仕事だ。新型の寸法図はもう届いていて、射出レールの受けを一本増やし、管を一本、耐圧型の外径に合わせて広げればいい。手を動かす仕事は、いくらでも歓迎する。
気が重いのは、もう一枚のほうだった。発進管制プログラムの改修見積もり。新型という「種類」が一つ増えたとき、コードのどこに手が入るのかを洗い出す仕事だ。
ドックに降りると、搬入予定のコンテナの前に、うちの航法士がいた。
ミラ。うちの船の航路を全部一人で引いている航法士で、寄港のたびに基地の海図局へ入り浸っては最新の重力図を漁ってくる、と船では専らの噂だ。今朝はコンテナの周りをゆっくり一周して、立ち止まっては首を傾け、また歩き出している。何をしているのか訊いたら、「射出のときの重心。図面より実物のほうが正直だから」と返ってきた。新型はまだコンテナの中で、姿も見えていないのにだ。この人の目は、たまにそういう順序で物を見る。
俺は搬入立ち会いのついでに、携帯端末で発進管制のコードを開いた。見積もりの下調べだ。プローブの種別名で検索をかける。組み立てをやっている場所を探して、そこに新型を一種類足したら何行になるか数えるだけの、軽い仕事のつもりだった。
検索結果を見て、手が止まった。
プローブを組み立てている case 文が——二つ、出てきた。
一つは通常発進の管制。もう一つは、緊急再発進の管制。別のファイルの、別のクラスだ。開いて見比べると、組み立ての部分は一字一句、同じ写しだった。
言っておくが、緊急再発進というのは飾りの系統じゃない。数年前の航海で、うちは長期の通信途絶を食らったことがある。中継プローブが深宇宙で沈黙して、船と航路管制をつなぐ声が途絶えた。あのとき予備の中継機を即座に撃ち上げたのが、この緊急再発進の系統だ。通常の管制手順を全部飛ばして、点検も省略で、とにかく撃つ。あの独立回線が動かなかったらと思うと、いまでも背中の後ろのほうが冷える。
その緊急系の腹の中に、通常系とまったく同じ組み立ての case が、もう一つ棲んでいる。
新型を一機積むなら、この写しの両方に一行ずつ。今回は俺が両方見つけたからいい。だが、次の新型のときに見積もるのが俺だという保証は、どこにもない。
コンテナの検分を終えたミラが戻ってきたところで、俺は端末を差し出した。
「見積もりは出せる。二箇所、各一行。大した工数じゃない。だがな——」画面を指で弾く。「新型を一機積むたびに、管制室のコードを二度開けるのか?」
ミラは端末を受け取って、しばらく黙ってスクロールしていた。それから画面を俺に返し、足元に置いてあった色札の束——発進管の整備で使う、種類分けのタグ札だ——を手に取った。
「ダグ、星図室で続きをやろう。これ、借りていくね」
第2幕 解析——写しの由来と、まだ起きていない事故
星図室に上がると、ミラは二つの case を並べて投影した。いま実機で動いている、現行の発進管制だ。
| |
| |
読み解きは難しくない。case kind は、種別を表す値で枝分かれする Ruby の分岐だ。航路計算機の一件で散々見た if/elsif の連なりと役目は同じで、ひとつの値をいくつもの候補と照らし合わせるときに使う、専用の書き方だと思えばいい。:survey の頭のコロンはシンボルといって、名前そのものを軽い値として使う書き方だ。文字列で "survey" と書いてもいいが、こういう「種類の名札」にはシンボルを使うのが Ruby の流儀らしい。種別が観測用なら SurveyProbe.new、中継用なら RelayProbe.new、回収用なら SampleProbe.new。new はクラスからインスタンス——実物のオブジェクト——を一機起こす呼びかけで、どの枝にも入らなければ else の門番が「未登録のプローブ種別」と警報を上げる。
組み立てたあとの仕事は、二つの管制で違う。通常発進は、発進準備の記帳をして、機体ごとの点検表を上から順に消化して、射出完了まで記録する。緊急再発進は点検を全部飛ばして、緊急射出の一行だけ記す。だから系統が分かれていること自体は、いい。問題は、組み立ての case だけが、二つの系統に同じ顔で写っていることだ。
呼ばれる側のプローブたちは、こういう小さなクラスだ。観測用のものを見せてもらった。
| |
attr_reader(持ち物の読み出し口を開ける書き方)も raise ArgumentError(不正な値をその場で弾く門番)も、これまでの引き直しですっかり馴染んだ型だ。freeze は「この値はもう書き換えない」という封印である。中継用も回収用も同じ形で、機体名と点検表の中身だけが違う。
「で、これを書いた人の肩を持つところから始めるね」
ミラは緊急側の case の少し上を指した。古いコメントが残っている。——緊急系は通常系から独立させること(安全基準準拠)。
「この人は、分かってて写したの」とミラは言った。「緊急再発進は、通常管制が落ちてても動かなきゃいけない独立回線。だから系統を分けた。それは正しいの、いまでも。で、当時プローブは観測用と中継用の二種類で、増える予定もなかった。二種類の組み立てなんて数行でしょ。数行なら、丸ごと写して独立させておくのが、いちばん安い保険だった」
写しは怠慢の産物じゃなく、安全設計の副産物だったわけだ。実際、あの通信途絶の夜に俺を救ったのは、この「独立して丸ごと持っている」構造そのものだったのかもしれない。
「変わったのは船じゃなくて、商売のほう」とミラは続けた。「サンプル回収用が増えて三つになって、今度は深部大気観測用で四つ。研究局との受託が続けば、まだ増える。プローブが、増え続けるものになったの」
そこまで言って、ミラは投影の脇に見慣れない画面を開いた。模擬航走の下書きだ。模擬航走——実機の代わりに、コードへ試験の入力を流して答えだけを検分する、恒例の試験航行である。だが今日は様子が違う。まだ何も壊れていないのに、何を検分するのか。
「見積もりにね、値段のつけようがない項目が一つあるの。それを見せる」
ミラが書いたのは、奇妙な版だった。通常発進の case には新型の一行を足してあり、緊急再発進の case は——わざと、元のまま。つまり、写しの片方だけ直した未来だ。次の改修で誰かがやらかすかもしれない形を、先に作ってみせたのである。
その版に、全種別を両方の管制へ流す照合を当てた。
| |
頭に付いている PartialFix:: は、この「片方だけ直した未来」の版を現行の版と同居させるための区分けの名前だ。
結果盤が示したのは、こういう未来だった。通常発進、全種別、緑。緊急再発進、新型だけ——赤。
嫌なのは、赤が出たことじゃない。赤の出るタイミングだ。
この漏れは、書いた時点では何も鳴らない。Ruby は動的型付けの言語だから、誤りが警報を上げるのは航行中——書いている間は黙りこくっていて、動かして初めて牙を剥く。それはこれまでの引き直しで骨身に染みている。だが今回のは、その中でも特に質が悪い。通常発進は元気に動くのだ。新型は毎日きれいに撃ち上がり、記録も揃い、誰も何も疑わない。緊急再発進の中の古い写しは、通信途絶の日まで一度も実行されない。鳴るのは、予備機を今すぐ撃たなきゃならない日——つまり、一番鳴ってほしくない日だ。
俺はあの夜の管制盤を思い出して、腕を組んだ。口から出たのは、機関室の愚痴だった。
「機関室なら、二重系統の弁は同じ棚の部品で組むぞ。系統の図面は二枚ある。だが部品はどっちも、同じ倉庫の同じ棚から出る。こっちのコードは——倉庫まで、二つに写してあるわけだ」
ミラは手の中の色札の束を、机の上でとん、と揃えた。散らばっていた札の角が、一列に揃う。この几帳面な手つきが出るときは、頭の中ではもう線が引き終わっている——それくらいは、隣で二十年も見ていれば分かる。
「ダグ、いまの倉庫の話。そのまま、名前のついた航法術になってるの」
第3幕 引き直し——生まれの窓口を一つに
「先に言っておくとね、二つの case を一つにまとめるんじゃないの」
ミラは投影の画面を送りながら言った。
「まとめるだけなら、どっちかの管制に寄せることになるでしょ。通常側に寄せれば、緊急側が通常側を呼ぶ形になって、独立回線じゃなくなる。逆でも同じ。だから、どっちでもない場所に出すの。組み立てだけを、管制の外へ」
投影に上がったのは、小さなモジュールだった。
| |
「オブジェクトの生成を専用のメソッドに任せて、『何を作るか』の決定を、使う側から隔離する。この航法術を Factory Method っていうの。工場のメソッド——生まれの窓口ね」
Factory Method。生成パターンと呼ばれる一族——「オブジェクトをどう作るか」を専門に扱う設計パターンの群れ——の代表格だそうだ。ミラは付け足した。「原典の形はもうひと工夫あって、継承で上書きするの。その話はあとで」。それから、こっちを見ずに言った。
「作り方を知っていい場所は、船に一つだけ」
コードを上から読む。module は、これまでの一件では「メソッドの詰め合わせをクラスに混ぜ込む」道具として出てきたが、今日のは使い方が違う。混ぜ込む相手はいない。インスタンスも作らない。ただの道具置き場——名前のついた棚として立っているだけだ。def self.create の self. は、その棚自身にメソッドを生やす書き方で、ProbeFactory.create(:survey, "第七軌道の気象観測") のように、棚の名前から直接呼べる。
で、棚の中身だ。REGISTRY——登録簿。全部大文字の名前は Ruby の定数、つまり「書き換えない前提」の名前だ。種別の名前をキーにした Hash であることは見れば分かる。survey: とコロンが後ろに付いているのは :survey => ... の省略記法で、キーはさっきの case に出てきたのと同じシンボルである。分からなかったのは、値のほうだった。
survey: SurveyProbe の右側。これは、何が入ってるんだ。文字列でもなければ、new した実物でもない。クラスの名前が、裸で置いてある。
「待て。またあれか。航路計算の選択表と同じだろ、棚から引くやつ」
デブリ回避の一件で見た PLANS の表が、頭にあった。あれも Hash に方針を並べて、キーで引く形だった。ミラは色札を一枚、指の間でくるりと返した。
「絵は同じ。棚に載ってるものが、違うの。あのときの選択表に載ってたのは、手順。呼べば計算が走って、答えの数字が返ってくる部品だった。今日の登録簿に載ってるのは——設計図そのもの」
「……こっちは、引いた札から機体が出てくるわけか」
「そう。あっちが隔離したのは『どう計算するか』の決定で、受け取るのは計算の結果。こっちが隔離するのは『何を生むか』の決定で、受け取るのは生まれたての機体。同じ棚の形をしてても、使い方の棚と、生まれの棚。しまってあるものが違えば、別の航法術なの」
分かったような、分からないような。俺が引っかかっていたのは、もっと手前の話だ。
「クラスってのは……設計図だろう。設計図を、棚に置けるのか。物みたいに」
「Ruby ではね、クラスも一個の物なの」
ミラは投影の隅に対話画面を開いて、一行叩いた。
| |
p は、値をそのまま表示するデバッグ用のメソッド。:: は「棚の中のこれ」と中身をたどる区切りだ。画面に返ってきたのは、SurveyProbe——クラスの名前そのものだった。
「変数に入る。Hash の値に置ける。引数で渡せる。SurveyProbe って書いたとき、そこにあるのは名前の書かれた設計図そのもので、.new は、その設計図に『一機起こして』って頼む合図。だから窓口の中身は、骨組みだけ抜き出すとこの二行なの」
| |
probe_class という変数の中にクラスが入っている。この一行を目で確かめて、ようやく腹に落ちた。文字列を頼りにどこかから探してくるんじゃない。設計図という物が、棚から手に渡って、仕事をする。機関部品の受け渡しと同じ手触りだ。
ミラは色札の束を持って立ち上がると、星図室の壁の投影を発進管の配置図に切り替え、管の一本一本に札を掛ける真似をしてみせた。観測用の管に青。中継用に白。回収用に橙。
「札が登録簿。管が、生まれを待ってる座席。窓口は札を見て、設計図を引いて、機体を起こして座席に渡す」
それから束の中から無地の札を一枚抜いて、俺に寄越した。色がまだ決まっていない、新型の分だ。掛けないのか、と思ったが、ミラは何も言わなかった。俺は札を作業着の胸ポケットに差した。
fetch の話も聞いておいた。Hash から値を引くなら REGISTRY[kind] でもいいはずだが、なぜ fetch なのか。「[] はね、無いキーを引くと黙って nil を返すの。fetch は無いキーを引くと KeyError——『そんな登録は無い』っていう実行時エラーで、その場で鳴る。黙られるより、鳴ってくれるほうがいい」。ただし今日の窓口は、素の KeyError ではなく、ブロック付きの fetch を使っている。fetch(kind) do 〜 end と書くと、キーが無いときだけブロックの中身が走る——つまり「無いときはこうしろ」という既定の動作を渡せる。ここで先人の警報文をそのまま継いで、ArgumentError の「未登録のプローブ種別」を上げる。警報の型も文言も、Before と寸分違わない。引き直しは挙動を変えない——組み立ての住所以外、何も。
利用側がどうなったかというと、これだけだ。
| |
| |
七行あった case の写しが、どちらの管制でも一行になった。発進準備も点検表の消化も緊急射出の記帳も、元のまま一文字も動いていない。
俺は端末の余白に、いま出来上がった形を配管図の流儀で描いてみた。管制の系統は二本、互いに呼び合わない。ただ、どちらの管も生まれの窓口に一本ずつ伸びていて、窓口の奥には台帳が一冊。線を引き終えて眺めると、なるほど、これは機関室の見慣れた絵だ。

一つ、先に確かめたいことがあった。あのコメントの件だ。
「緊急系の独立は、どうなる。先人はわざわざ写してまで系統を切り離したんだぞ」
「系統は別のまま」とミラは即答した。「緊急再発進は、いまも通常管制を呼ばない。通常側が丸ごと落ちてても、緊急側は自分の手順で動く。共有するのは、生まれの窓口だけ。——ダグの言葉で言えば、同じ倉庫から部品を取るだけ。二重系統の弁が同じ棚の部品なのと、同じことでしょ」
図面は二枚のまま、部品棚だけ一つになった。それなら機関室の流儀と同じだ。文句はない。
そこでミラは、投影の前の椅子に深く沈み込んで、荷物から瓶を出した。基地の売店で買ったらしい果実水だ。淡い赤で、振ると細かい果肉が舞う。寄港中のこの人は、船内では見ない物ばかり口にする。そして飲み食いが始まるのは、頭の中の大きな仕事が一段落した印だと、相場が決まっている。
俺は端末を持ったまま、さっきの倉庫の絵を、最後まで口に出して確かめた。
「機関部品なら、番号で倉庫に頼むだけだ。旋盤を回す場所は、倉庫の奥に一つありゃいい。系統ごとに旋盤を置く工場は、ない」
ミラは瓶の口を軽くこちらへ掲げて、それから一口飲んだ。それで通じたらしい。
一息ついたところで、さっき棚上げになっていた話を回収してもらった。「原典の形はもうひと工夫ある」というやつだ。
Factory Method の元々の形——設計パターンの原典に載っている形は、登録簿ではなく継承を使うのだという。継承というのは、親クラスの性質を受け継いだ子クラス——サブクラス——を作る仕組みで、子は親のメソッドを自分の中身で置き換え(上書き)できる。寄港手続きの骨格の一件で使った、あの親子の型だ。原典では、生成メソッドを持つ親クラス(クリエイターと呼ぶ)をまず立てて、その生成メソッドは中身を書かずに raise NotImplementedError——子が埋め忘れると実行時に鳴る、あの警報付きの空欄である。そして観測ミッション用のクリエイター、回収ミッション用のクリエイター、とサブクラスを作り、それぞれが生成メソッドを上書きして、返す機体を変える。
「そっちが伸びるのは、種類ごとに、生成以外の振る舞いも変えたいとき」とミラは言った。「たとえば観測ミッションと回収ミッションで、発進手順そのものが機体ごとに枝分かれしていくなら、クリエイターのサブクラスがそれぞれの手順ごと抱えられる。今日のうちの問題は、そうじゃないでしょ。手順は二系統とも決まってて、変わるのは『何を生むか』だけ。生まれの切り替えだけなら、継承の階段を増やさずに、棚で足りるの」
却下ではなく、使い分けだ。継承で上書きするか、棚から引くか。決め手は、種類ごとに変えたいものが「生まれ」だけか、「振る舞い」まで含むか。Ruby はクラスを物として棚に置けるから、後者が要らないうちは棚のほうが安く済む——そういう勘定だった。
第4幕 模擬航走——全数の網と、無地の色札
模擬航走の本番は、約束の確認から始まった。
同じ入力の列——全種別×両系統、それに門番向けの不正な入力——を、現行の写し版と、窓口を立てた引き直し版の両方に食わせる。そして発進記録が一字残らず一致することを照合する。
| |
%i[...] はシンボルの配列を並べて書くときの省略記法で、[:survey, :relay, :sample] と同じ意味だ。@before_launch と @after_launch は、試験の準備段階——各検分の前に必ず走る、setup と呼ばれる支度の場所——で、写し版と窓口版の管制を一機ずつ作って持たせてある変数である。
写し版と窓口版はクラス名が同じなので、試験のほうでファイルを分けて衝突を避けてある——警報系の一件でやったのと同じ段取りだ。通常発進、三種、緑。緊急再発進、三種、緑。未知の種別 :unknown は、どちらの版のどちらの系統でも、同じ文言の ArgumentError。任務名が空のときの門番も同じに鳴る。構造は変わり、挙動は変わっていない。引き直しの約束を数字で示してから、次へ進む。いつもの順序だ。
そして、今日の主役の検分が来た。新型の追加である。
ミラはまず、新型のクラスを書いた。
| |
それから、倉庫の台帳を開いた。登録簿のソースに、一行。
| |
模擬航走を流す。通常発進で :deep_atmo——通る。発進準備、耐圧殻の与圧確認、降下傘の展開試験、射出完了。緊急再発進で :deep_atmo——通る。緊急射出、点検省略、予備機。
ここまでの間に開いたファイルは、新型のクラスと、台帳の二つだけだ。LaunchControl のファイルも、EmergencyRelaunch のファイルも、開いてすらいない。試験の構成そのものが、そのことを証明している——両管制のソースは写し版の検分に使ったものと同一で、読み込む台帳だけが差し替わっている。
朝の問いの答えが、そこにあった。新型を一機積むたびに、管制室のコードを二度開けるのか。——一度も開けない。開けるのは、倉庫の台帳一枚だ。
見積書の裏紙に、写しの時代と台帳の時代を並べて描いた。左の絵では、開ける扉が二つあって、片方を忘れた誤りは緊急の日まで息を潜める。右の絵では、開けるのは台帳一枚で、忘れた誤りはどの管から来ても同じ窓口で鳴る。壊れなくなったわけじゃない。壊れ方が、一本の線で読めるようになった。

俺は胸ポケットから無地の札を抜いて、机のペンを取った。深部大気、と書く。壁の投影の、広げたばかりの発進管の位置に、札を掛ける真似をする。ミラは果実水の瓶を膝に置いたまま、目を細めてそれを見ていた。
「でね、ダグ。いまの、『開ける場所が減った』って話だけじゃないの」
ミラは写し漏れの未来——第2幕で見た、片方だけ直した版の赤い結果盤を、もう一度呼び出した。
「写しの時代の登録漏れは、片方だけ直すっていう形で潜り込んだ。通常系は元気だから、誰も気づかない。鳴るのは緊急の日。——いまの形で登録を忘れたら、どうなると思う?」
考えてみた。台帳に新型の行が無ければ、窓口は :deep_atmo を引けない。通常発進から呼ぼうが、緊急再発進から呼ぼうが、同じ窓口の同じ fetch が、同じ「未登録のプローブ種別」を上げる。
「どっちの系統から来ても、同じ場所で、同じ警報か」
「そう。漏れ方が一種類になったの。潜伏しない。初回の模擬航走で必ず捕まって、直す場所も一つ。——壊れなくなった、じゃないのよ。壊れ方が、読めるようになったの」
検分は全部で二十三本、照合の項目は五十三。赤は一つも出なかった。ミラは緑の並びを端から端まで確かめてから、発進管制を窓口版に載せ替えた。実機に載せるのは、模擬航走を通り切ってから。うちの星図室でこの順番が崩れたところを、俺は見たことがない。
店じまいの前に、この引き直しが守ってくれないものを確かめておいた。うまい話をそのまま持ち帰らないのは、機関士の習い性だ。
「三つ」とミラは指を立てた。「一つ。登録漏れそのものは、消えてない。窓口は漏れを消したんじゃなくて、鳴る場所を一つにしただけ。鳴るのは相変わらず実行時——航行中。だから新型を積んだら、全種別×全系統の模擬航走を必ず回すこと。網の張り方は変わらない。二つ。台帳は、載せていいかどうかを審査しない。登録簿に書きさえすれば、窓口は何でも生む。その機体を本当にうちの発進管から撃っていいのかの判断は、窓口の仕事じゃなくて、受託審査と安全審査の仕事。三つ。増えないものに、窓口は要らない。種類が三つで固定なら、case の直書きのほうが、読む人には親切なことも多いの。今日引き直したのは、受託でプローブが増え続けることが決まったから。増えない棚のために倉庫を建てるのは、荷のない倉庫を建てるのと同じ」
もう一つ、機関屋として聞いておくべきことがあった。
「窓口を一つにしたってことは、そこが詰まったら、通常も緊急も共倒れだろう。写しには写しの、系統ごとに独立してるって取り柄があったんじゃないのか」
「止まるよ」あっさり認めた。「窓口のコードに誤りが入れば、両系統に効く。それは引き受けたの。写しの独立は、片方の誤りがもう片方に染みない代わりに、どこが腐ってるか誰にも見えない独立だった。窓口は、詰まったら両方止まる代わりに、詰まりが必ず一箇所で見える。どっちを取るかの話で、うちは見えるほうを取った。だから模擬航走の網は、窓口に一番厚く張ってある」
数日後——まだ停泊中の朝に、新型の初発進が組まれた。
研究局の立ち会いつきだ。発進管の脇が俺の持ち場で、広げたばかりの管には、深部大気と書いた俺の札が本当に掛かっている。ミラは管制卓の端末の前。カウントダウンは基地の管制と合同で進んだ。ゼロ。射出の反動が、床を伝って靴の裏に届く。
観測窓に寄ると、基地の空を航跡が一本、まっすぐに昇っていくところだった。やがて機影は見えなくなり、管制卓の軌道表示に、予定軌道への投入を示す印が灯る。研究局の連中が短く拍手をして、それで初発進は終わった。撃ち上げというのは、うまくいくと呆気ないものだ。
片付けの段になって、ミラが寄ってきた。手には色札の束。その中から無地の一枚を抜いて、俺に差し出す。
「次の分」
それだけ言って、束を抱えて海図局のほうへ歩いていった。あの様子だと、戻りは夕方だろう。
俺は札を、胸ポケットに差した。次の新型が何になるかは知らない。だが、来たときにやることはもう決まっている。管を一本広げて、クラスを一枚書いて、台帳に一行。管制室のコードは、もう眠らせておけばいい。
🚀 オリオール号 航海日誌(Navigator’s Log)
- 航法術(パターン名): Factory Method(オブジェクトの生成を専用のメソッドに委ね、「何を作るか」の決定を利用側から隔離する生成パターン)
- 発生事象(症状): 新型プローブの運用受託に伴う改修見積もりで、プローブを組み立てる case 文が通常発進・緊急再発進の2系統に写しで散在していると発覚。事故は未発生だが、写しの片方だけ直す修正漏れは通常運用では無症状のまま潜伏し、緊急再発進の日にだけ実行時エラーとして爆発する
- 原因(旧航路の問題): 利用側の2系統が具象クラスの new と種別分岐を丸ごと抱え、種類の追加が全写し箇所の修正を連鎖させる。写し自体は「緊急系の独立」という当時正しい安全判断の副産物で、前提(種類が増え続ける商売への転換)のほうが動いた
- 処置(引き直しの要点): 生成を専用窓口
ProbeFactory.createに隔離。内部は種別→クラスの登録簿(Ruby ではクラスもオブジェクト——Hash の値に設計図そのものを置ける)。新型の追加はプローブクラス一枚と台帳への1行で完結し、2系統の管制は無改変。登録漏れはどの系統から来ても同じ窓口で同じ警報として鳴る(漏れ方の一元化) - 保証外事項: 登録漏れ自体は消えない(鳴る場所が一つになっただけで、鳴るのは実行時——全種別×全系統の模擬航走が網)/台帳は種類の妥当性を審査しない(載せていいかの判断は別の仕事)/種類が増えない現場には過剰(case 直書きが読みやすい場面もある)/窓口の誤りは両系統に効く(独立の代わりに、詰まりが一箇所で見える構造を選んだ)
- 模擬航走結果: 全23テスト・53アサーション、全灯緑(全種別×2系統の記録一致・写し漏れの未来の隔離検分・未知種別と空任務名の門番・新型の管制無改変追加・窓口が返す機体の型照合)
- 次の針路: 研究局の受託が続けば、種類はまだ増える。無地の色札は俺の胸ポケットに一枚——次に開けるのも、台帳だけでいい
