Featured image of post コードウィザード【Null安全】棚にない本を探すたび、検索呪文が魔力空無に呑まれて凍りつく〜「空かもしれぬ」を型に刻み、何もしない安全な書で受け止める〜

コードウィザード【Null安全】棚にない本を探すたび、検索呪文が魔力空無に呑まれて凍りつく〜「空かもしれぬ」を型に刻み、何もしない安全な書で受け止める〜

蔵書検索で棚にない本を引くと、検索呪文が魔力空無で凍りつく。見習い魔術師ルークは全呪文を安全結界で囲って例外を握り潰し、無いのか壊れたのかを分からなくしてしまう。師匠の助言から、Kotlinの安全呼び出しで「空かもしれぬ」を型に乗せ、何もしない安全な書(Null Object)で受け止める設計へ。

凍りついた検索台

僕はテオ。アカデミーの麓町にある、小さな公共蔵書庫で司書をしている。仕事はいたって地味だ。届いた本に請求コードを振り、書架の正しい場所へ収め、訪ねてきた人の求める一冊を探し出して手渡す。派手なところは何ひとつない。それでも、ばらばらに見える蔵書がきちんと目録の上で整列して、誰かの「あの本が読みたい」に応えられたときの心地よさを、僕はひそかに誇りにしている。

その日々を支えているのが、受付に据えた検索台だった。利用者から請求コードを聞き、台にそれを唱えると、その本が蔵書庫のどこにあるかが浮かび上がる。先代の司書から引き継いだ、古い検索呪文だ。蔵書が少なかった昔は、探せば必ず棚にある、というのが当たり前だった。だから呪文も、本は「必ず在る」前提で組まれていた。

ところが、蔵書も利用者も増えた今は、事情が違う。貸出中だったり、そもそも蔵書していなかったりで、「探したけれど見つからない」ことが、日に何度も起きる。

その日も、ひとりの利用者が一冊を求めて来た。あいにく、うちには無い本だった。僕がいつものように請求コードを検索台に通したとたん——台の魔力が、すうっと空(くう)へ吸い込まれるように落ち、浮かびかけた光が消え、台はそのまま凍りついた。うんともすんとも言わない。

しかも、まずいことに、巻き添えがあった。後ろに並んでいた別の利用者の検索まで、同じ台が握っていたものだから、まとめて止まってしまったのだ。受付に小さな列ができ、僕はその前で立ち往生した。本を届けるのが仕事なのに、その入り口で詰まっている。

無い本を、一度探しただけだ。なのに、どうして検索台ごと黙り込む——。在る本ならちゃんと出せる。けれど、棚に無い本を一度引くと、検索の魔法そのものが不発になって凍りつく。その晩、僕は閉館後の暗い書庫で、長いこと考え込んでいた。これは、僕の手に負える種類の不調ではない気がした。

噂を頼りに、丘をのぼる

ひとつ、心当たりがあった。

少し前、常連の利用者が世間話のついでにこう言っていた。丘の上のアカデミーに腕のいい若い見習いがいて、先頃、街のある店の暴れていた会計呪文を見事に鎮めたらしい、と。そのときは聞き流していたが、今になって思い出した。藁にもすがる、というほど切羽詰まってはいない。ただ、その噂を頼りに、僕は検索呪文を写し取った符を抱えて、丘をのぼった。

アカデミーの工房は、天井まで本棚が届き、見たこともない魔導書がぎっしりと詰まっていた。商売人ならきっと気後れする眺めだろう。けれど僕は、本と棚に囲まれているというだけで、むしろ少し落ち着いた。勝手が違うのは魔術であって、本棚ではない。背表紙の並びを眺めているうちに、自然と肩の力が抜けた。

出てきたのは、ずいぶん若い見習いだった。ルーク、と名乗る。噂に聞いた「腕のいい見習い」を、僕は勝手に、もっと年嵩(としかさ)の落ち着いた魔術師だと思い描いていたらしい。目の前のルークは、握った杖もどこか手に馴染みきっていない様子で、思い描いていた像とはずいぶん違う。これが、その見習いか——気を取り直し、僕は検索呪文の符を見せ、症状を語った。棚に無い本を引くと検索台が凍ること。巻き添えで、ほかの利用者まで取りこぼしてしまったこと。

ルークは符を手に取り、上から順に目で追いながら、まず蔵書庫の「もの」の決まりを教えてくれた。

1
2
3
4
5
data class Book(val title: String, val author: String)

class Catalog(private val books: Map<String, Book>) {
    fun find(code: String): Book? = books[code]
}

Book が一冊の本で、表題(title)と著者(author)を持つ。ルークによれば、頭についた data class は中身の比較などを最初から備えた“データ向けのクラス”で、丸括弧の中に値を並べて書くと、受け取ったものをそのまま持ち物にできるのだという。Catalog が蔵書庫で、抱えている books: Map<String, Book> は「請求コード(文字列)から本を引くための対応表」だという(Map<キーの型, 値の型> で、何から何を引く表かを表す)。その中の fun find(code) = books[code] にある = は、波括弧と return を省いて式ひとつをそのまま答えにする短い書き方だ。請求コードを渡せば、その本を探して返してくれる。魔術の文法は不慣れだが、こうして一つずつ教われば、僕にも分かる。

問題は、find という探す呪文の「戻り」の形だった。ルークはその一点を指した。

「テオさん。よく見ると、この戻りの型、Book? と書いてありますね。うしろに小さな ? が付いている。これは『探した本は、空(から)かもしれない』という印なんです。つまりこの蔵書庫は、最初から正直に『見つからないこともある』と言っている」

空かもしれない、と型そのものに書いてある。言われてみれば、当たり前のことだ。棚に無い本はある。

「……この検索は、本が必ず在る前提で組まれているんですね。棚に無いときのことが、抜けている」。ルークのその見立ては、僕が暗がりで薄々感じていたことと、ぴたり同じだった。

全部、結界で囲ってしまえば

ところが、符を順に追っていたルークが、不意に顔を上げた。何かを思いついたらしく、その表情がぱっと華やいだのが、僕にも見てとれた。

「テオさん、お任せください。不発が起きなければいいわけですから——いっそ全部、結界で囲ってしまいましょう!」

そう言うと、僕が止める間もなく、彼は、新しく身につけた守りの魔法をよほど使ってみたかったのだろう、検索台のひとつひとつの呪文に、透明な膜のような結界を、幾重にもかぶせ始めた。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class GuardedSearchDesk(private val catalog: Catalog) {
    fun titleOf(code: String): String =
        try { catalog.find(code)!!.title } catch (_: Exception) { "" }

    fun authorOf(code: String): String =
        try { catalog.find(code)!!.author } catch (_: Exception) { "" }

    fun recordLoan(code: String, ledger: MutableList<String>) {
        try { ledger.add("貸出: " + catalog.find(code)!!.title) }
        catch (_: Exception) { }
    }
}

ルークが、ひとつずつ説明してくれた。try で囲んだ中で何か失敗が起きても、catch がそれを受け止めるので、台ごと凍りつくことはなくなる。受け止めたときは空文字 "" を返す——try … catch のひとかたまりが、そのまま答えの文字列になる書き方だそうだ(catch (_: Exception)_ は、受け取った失敗を中では使わない、という印)。表題を引く呪文にも、著者を引く呪文にも、貸出を帳面(ledger)に記す呪文にも、ひとつ残らず同じ結界をかぶせる。

ひとつ、引っかかる印があった。catalog.find(code) のうしろの !! だ。ルークは少しばつが悪そうに、これは先代が遺したものだと言った。!! は「これは絶対に空じゃない」と言い張って、?(空かもしれぬ印)を黙らせる印なのだという。先代は「本は必ず在る」と信じていたから、空かもしれない戻りを、この !! で握りつぶして「在るもの」として扱っていた。だから空が来たとき、台はその場で不発していた——それが、凍りつきの正体だった。空(から)の値を本物のように触ろうとして起きるこの不発を、魔術師は NullPointerException(魔力空無、略してNPE。空=null の値を本物として扱おうとした瞬間に起きる不発)と呼ぶらしい。

「この結界なら、たとえ空無に落ちても、結界が受け止めます。台は、もう凍りません」とルークは胸を張った。確かに、試してみると、もう凍らない。

——そのとき、窓ぎわの揺り椅子のほうで、ことり、と音がした。見ると、湯気の立つカップを手にした白髪の老人が、薄目を開けてこちらを眺めている。「ほう……片端から、覆うてしまうか」。ひとりごとのような、低い呟きだった。ルークには聞こえなかったのか、満足げな横顔のままだ。僕も、書斎の主が寝ぼけて呟いたくらいに思って、気に留めなかった。老人は、また静かに目を閉じてしまった。

無いのか、壊れたのか

けれど、しばらく使ううちに、僕は妙な居心地の悪さを覚えはじめた。

無い本を引いても、検索台は凍らない。それはいい。代わりに、ただ「しん」と静まって、何も書かれていない空白を返してくる。「該当なし」とすら言わない。ただの、空白だ。

試しに、僕はわざと出鱈目な——空っぽの請求コードを、結界を張ったその検索台(コードでは guarded)に通してみた。すると、それも、まったく同じ空白で返ってきた。

1
2
guarded.titleOf("Z-999")  // 棚に無い本 → ""(空白)
guarded.titleOf("")       // 壊れた問い(空っぽのコード)→ ""(まったく同じ空白)

僕は、ぞっとした。

棚に無いだけの本も、そもそも問いとして壊れている要求も、結界はぜんぶ同じ沈黙で呑み込んでしまう。区別が、つかない。僕は思わず尋ねた。

「ルークさん。これは……無いから空白なんですか? それとも、どこか壊れていて空白なんですか?」

ルークの手が、止まった。

「えっと、それは……結界が受け止めたので……どちらか、は……」

彼にも、もう分からなくなっていた。結界は、失敗を受け止めると同時に、その失敗が何だったのかという手がかりまで、まとめて握りつぶしていたのだ。

司書として、これがいちばんこたえた。凍りついて止まるほうが、まだましだったかもしれない。あれは少なくとも「ここに異常がある」と叫んでいた。今のこれは、何事もなかったような顔で、嘘の静けさを返してくる。目録が黙って嘘をつく——僕にとって、これほど怖いことはない。利用者に「ありません」と告げるなら、せめてそれが本当に無いからだと、胸を張って言いたい。

眠っていたはずの老人

そのときだった。

書斎の窓ぎわの揺り椅子で、さっき寝ぼけた呟きを漏らしたあの白髪の老人が、今度ははっきりと両目を開けた。ハーブティーの湯気の向こうから、低く、ゆったりとした声がかけられた。ルークが弾かれたように背筋を伸ばし、「ぼ、僕の師匠の……マグナスです」と早口に紹介した。揺り椅子の主は、ただ昼寝をしている書斎の住人などではなかったらしい。

マグナスさまは、すぐには答えを言わなかった。代わりに、こちらへひとつ、問いを置いた。

「司書どのに、ひとつ訊こうかの。求められた本が、うちの棚に無いとする。さて——黙ってその場で固まってしまう目録と、『うちには無い』と正直に告げる目録。どちらが、人の役に立つかね?」

僕は、迷わなかった。

「……無いと、正直に告げるほうです。黙って固まられたら、無いのか壊れたのか、何も分かりませんから」

それは、司書なら考えるまでもないことだった。マグナスさまは「ふむ」と頷き、それから、ゆっくりとルークのほうへ視線を移した(ように見えた)。

工房が、しんと静まった。やがてルークが、ちいさく息を吸い込む。何かに思い当たった——そんな顔つきだった。

「……そうか。空(から)になるのを、隠しちゃいけないんだ。最初から『この検索は空かもしれない』と正直に認めて、空だったときに何を返すかを、ちゃんと決めておけばいい。そうすれば、慌てて結界で握りつぶさなくて済む」

マグナスさまは、それきり何も足さなかった。ただハーブティーを一口含んで、また静かに目を閉じる。答えそのものは、ついぞ口にしないままだった。気づきにたどり着いたのは、ルーク自身だった。

「空かもしれぬ」を、型に刻む

ルークは、検索台を覆っていた幾重もの結界を、端から手早く剥がしていった。台が、元の素っ気ない姿に戻る。そして彼は、まず一枚の符——蔵書庫から本を引く、あの戻りの符を、僕に見せた。

「さっきも言いましたが、この符は最初から正直なんです。戻りが Book?、つまり『空かもしれない』と書いてある。先代はそれを !! で握りつぶしていた。だから、握りつぶすのをやめて、空かもしれないものを、空かもしれないまま、丁寧に扱えばいいんです」

彼は、その書き方を見せてくれた。蔵書庫(catalog)から請求コード(code)で本を引き、あれば貸出の帳面(ledger)に記す——その一連を、空かもしれないまま素直に書くと、こうなるという。

1
2
3
4
val book: Book? = catalog.find(code)
val title  = book?.title  ?: "(該当なし)"
val author = book?.author ?: "(不明)"
book?.let { ledger.add("貸出: " + it.title) }

見慣れない印が、いくつか出てきた。ルークが一つずつ、ほどいてくれる。

?. は安全呼び出しといって、左が空でなければ呼び出し、空ならそこで止まって空を返す。!! のように言い張らないから、空が来ても不発しない。?: はエルビス演算子といって、左が空だったら、右の値を代わりに使う。だから book?.title ?: "(該当なし)" は、「本があれば表題を、無ければ『(該当なし)』を」という意味になる。最後の ?.let { ... } は、左が空でなければ波括弧の中の処理をする書き方で、中の it は、その「空でなかった値」——つまり見つかった本そのものを指す。

「これで、空が来ても凍りません。無ければ無いで、ちゃんと『(該当なし)』と言える」

なるほど、と思った。けれど、ここでルーク自身が、ふと手を止めた。少し顔をしかめている。

「……でも。これだと、表題を出すところ、著者を出すところ、貸出を記すところ。空かどうかの確かめが、使う場所ごとに、いちいち散らばってしまう」

言われて符をよく見ると、確かに、同じ ?. の印が、あちらにもこちらにも、繰り返し現れていた。本を扱う箇所が増えるたびに、この「空だったらどうするか」の確かめを、また書き足さなければならない。僕は、自分の蔵書庫を思った。あちこちの帳面に同じ注意書きを書いて回るようなものだ。書き忘れたら、そこだけ穴になる。

該当なしの書

ルークは、しばらく散らばった符を眺めて、黙っていた。やがて、自分で答えにたどり着いたようだった。

「そうか。『空のときにどう振る舞うか』を、使う場所ごとに書くから、散らかるんだ。それを一冊の“書”にまとめて、最初から決めておけばいい。該当なしのときの『何もしない』を、まるごと引き受ける書を、一冊」

彼はまず、本の見せ方の「約束」を一枚、決めた。

1
2
3
4
5
interface SearchResult {
    fun title(): String
    fun author(): String
    fun recordLoanInto(ledger: MutableList<String>)
}

これは インターフェース(中身の実装ではなく、「こう呼べばこう応える」という形だけを取り決めた設計図)だという。表題を訊かれたら答える、著者を訊かれたら答える、貸出を頼まれたら帳面に記す。その三つに応える、という形だけを約束している。中で何をするかは、これを守る側が決めればいい。

そして、その約束を守る書を、二種類こしらえた。見つかった本の書と、見つからなかったときの書だ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class FoundBook(private val book: Book) : SearchResult {
    override fun title() = book.title
    override fun author() = book.author
    override fun recordLoanInto(ledger: MutableList<String>) {
        ledger.add("貸出: " + book.title)
    }
}

object NotFoundBook : SearchResult {
    override fun title() = "(該当なし)"
    override fun author() = "(不明)"
    override fun recordLoanInto(ledger: MutableList<String>) {
        // 該当なしは貸し出せない=安全に、何もしない
    }
}

FoundBookNotFoundBook の名前のうしろに付いた : SearchResult は、「さっきの約束(インターフェース)を、この書が守ります」という宣言だという。そして各メソッドの頭にある override は、「その約束を、自分のやり方で果たします」という宣言なのだそうだ。FoundBook は本物の本を抱えていて、表題も著者もちゃんと答え、貸出も帳面に記す。

僕の目を引いたのは、もう一方だった。NotFoundBook——いわば「該当なしの書」。表題を訊かれれば「(該当なし)」と答え、著者を訊かれれば「(不明)」と答える。そして貸出を頼まれても、安全に、何もしない。無い本は貸し出せないのだから、何もしないのが正しい。何もしないことを、ちゃんと仕事として引き受ける書なのだ。

「該当なしの書は、最初からこの世にひとつあれば足ります。だから object(最初からただ一つだけ作られる実体。シングルトンともいう)で作っておきます」とルーク。

この「本物と同じ形をした、何もしない安全な実体」を当てて、空のときの振る舞いを一箇所にまとめてしまう定石を、Null Object(本物と同じインターフェースを持つ「何もしない実体」で、不在のときの振る舞いを一箇所に集約し、呼び出す側から空の確かめを無くすパターン)と呼ぶのだと、ルークは教えてくれた。

最後に、彼は検索台そのものを組み直した。

1
2
3
4
5
6
7
8
class SearchDesk(private val catalog: Catalog) {
    fun search(code: String): SearchResult {
        require(code.isNotBlank()) { "請求コードが空です" }
        return catalog.find(code)
            ?.let { FoundBook(it) }
            ?: NotFoundBook
    }
}

蔵書庫から引いた「空かもしれぬ」戻りを、?.let で——見つかれば FoundBook に包み、?: で——空なら「該当なしの書」に。

「こうすると、検索台が返すのは、もう絶対に空じゃありません。必ず、どちらかの“書”です。だから、これを受け取る側は、空かどうかをいちいち気にせず、ただ同じように訊けばいい」

1
2
3
val result = searchDesk.search(code)
println(result.title())          // 該当なしなら「(該当なし)」
result.recordLoanInto(ledger)    // 該当なしなら、安全に何もしない

なるほど、と僕はうなった。さっきまであちこちに散らばっていた ?. の確かめが、受け取る側からは、きれいに消えていた。「空だったらどうするか」は、もう「該当なしの書」が一手に引き受けている。

ルークは、机の上に広げられた羊皮紙に、杖の先で光の魔力を走らせた。宙に浮かび上がったのは、今しがた彼が組み上げた新しい魔導式の構造だった。

「テオさん、この魔導式のつながりを見てください」

Class diagram showing SearchDesk producing SearchResult, implemented by FoundBook and NotFoundBook (Null Object)

「検索台(SearchDesk)は、本が見つかろうと見つかるまいと、ただ『約束された書(SearchResult)』を返すことだけを決めればいいんです。本があれば実体のある『見つかった書(FoundBook)』を、無ければ何もしない『該当なしの書(NotFoundBook)』を包んで返します。受け取る側は、それがどちらかを知らなくても、ただ同じように呼び出せば、それぞれの書が自分の役割を果たしてくれます」

光の図表を見つめながら、僕は頭の中の霧が晴れていくのを感じていた。複雑な条件分岐の鎖が、二つの「書」の振る舞いの中に、きれいに吸い込まれて消えている。

ひとつだけ、見慣れない見張りが台の入り口に増えていた。require だ。ルークが補う。「require は、決めた条件を満たさなければ、その場で声を上げて——IllegalArgumentException(引数が不正だ、という例外)を投げて——止める見張りです。空っぽの請求コードみたいに、そもそも問いとして壊れているものは、握りつぶさずに、ちゃんと声を上げて弾きます」

無い本を、「無い」と言える

「組み上げたら、いきなり本番ではなく、まず結界の中で確かめます」

ルークはそう言って、模擬詠唱に取りかかった。本番の蔵書庫に放つ前に、閉じた結界の中で何度も唱え、狙いどおりに働くかを見ておくのだという。今度は僕の手元でも追えるよう、ひとつずつ結果を読み上げながら進めてくれた。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

fun main() {
    val catalog = Catalog(mapOf(
        "A-001" to Book("Kotlinの呪文書", "魔法技術者ルーク")
    ))
    val searchDesk = SearchDesk(catalog)

    // ① 見つかる本は、これまで通りに
    val found = searchDesk.search("A-001")
    assertEquals("Kotlinの呪文書", found.title())

    // ② 棚に無い本は、凍らず「(該当なし)」と返す
    val notFound = searchDesk.search("Z-999")
    assertEquals("(該当なし)", notFound.title())

    // ③ 該当なしの本の貸出は、例外でなく、安全に何もしない
    val ledger = mutableListOf<String>()
    notFound.recordLoanInto(ledger)
    assertEquals(0, ledger.size)

    // ④ 壊れた問い(空コード)は、握りつぶさず声を上げる
    assertFailsWith<IllegalArgumentException> {
        searchDesk.search("")
    }

    println("ALL TESTS PASSED")
}

蔵書庫には、ためしに「A-001」という請求コードの本を一冊だけ入れてある("A-001" to Book(...)to は、コードと本を一組にする書き方だ)。在る本は、これまで通り、ちゃんと表題が出る。棚に無い本は、もう凍らず、「(該当なし)」と静かに返ってくる。その本の貸出を頼んでも、帳面は一行も増えない——無い本は貸し出せないのだから、何もしないのが正しい。assertEquals は「答えが思ったとおりか」を、assertFailsWith は「狙いどおりに、ちゃんと失敗するか」を確かめる道具だそうだ。最後の一つでは、わざと空っぽのコードを通すと、今度は黙って呑み込まず、ちゃんと声を上げて弾かれた。

組み直された検索台を、僕は受け取った。

無い本を引いても、もう凍らない。代わりに、台は静かに、はっきりと「(該当なし)」と告げる。当たり前のことのようで、これがどれほど安心か。無い本を、ちゃんと「無い」と言える。司書にとって、目録が正直であることほど、大事なことはない。

ルークが、要点を一言でまとめてくれた。「空かもしれないことを、隠さず型に書いておく。空だったときにどう振る舞うかは、“該当なしの書”に一度だけ決めておく。だから、もうどこでも握りつぶさなくていいんです」。これが Null安全(空〔null〕かもしれない値を型ではっきり示し、空のまま扱うことを許さずに、扱う前の対処を促す仕組み)と、Null Objectの合わせ技なのだという。

いつ「無い」で済ませ、いつ声を上げるか

帰り支度をしながら、僕はひとつ、気になっていたことを尋ねた。

「何でもこの“該当なしの書”にしてしまえば、安全なんでしょうか」

ルークは、首を横に振った。

「いいえ。表題をひとつ出すだけなら、?: で『(該当なし)』と書けば、それで十分なんです。わざわざ書を仕立てなくていい。“該当なしの書”が値打ちを出すのは、空のときにも“振る舞い”が要るとき——今回みたいに、表題も、著者も、貸出も、空のときの作法をぜんぶ一箇所に決めたいときだけです」

そして彼は、少し決まり悪そうに、さっき自分が検索台に張り巡らせた、あの幾重もの結界のことを思い出したようだった。

「何でも囲んで、空にしてしまえばいい——というのは。……さっきの僕の、ちょうど逆ですね」

無いものは、無いと静かに受ける。けれど、壊れた問いには、ちゃんと声を上げる。その二つは、似ているようで、まるで別のことだ。何もかも同じ沈黙で握りつぶしてしまっては、いけなかった。ルークは、自分の失敗を回り道にして、そのことを掴んだらしかった。

ルークが、今日の学びを魔導書に書きつけている。揺り椅子のマグナスさまは、ハーブティーをもう一口含んで、ひとことだけ、静かに言い置いた。

「空(から)であることは、隠すべき疵(きず)ではない。隠したとき、はじめて疵になるのだよ」

僕は、その言葉を胸の中で繰り返した。蔵書庫に「無い本」があるのは、疵ではない。無いものを、無いと正直に告げられない目録——それこそが、疵だったのだ。

整え直された検索台を抱えて、僕は丘を下りた。明日からも、棚に無い本を訊かれることはあるだろう。けれど、もう怖くない。台は凍らず、「ありません」と、はっきり、正直に告げてくれる。本を届けるのも、無いものを無いと告げるのも、どちらも司書の、大切な仕事だ。今は、そう思える。


📖 見習い魔術師ルークの魔導書メモ(Luke’s Spellbook Notes)

  • 修復した呪文(クラス/モジュール名): SearchDesk(検索台)と SearchResult 一族(FoundBook と、該当なしの書 NotFoundBook
  • 乱れの要因(アンチパターンの課題):
    • 先代の検索呪文が「本は必ず在る」前提で、空かもしれぬ戻り(Book?)を !! で握りつぶしていた。該当なしのとき、魔力空無(NullPointerException)でその場で不発し、検索台が凍りついて、巻き添えに他の検索まで止めた。
  • 過剰設計の反省(背伸びの過ち):
    • 「とにかく囲めば安全」と、全呪文を多重の try-catch で包んで例外を握りつぶした。台の凍りつきは止まったが、正当な「該当なし」と、壊れた要求(空コード)が、同じ空白にされてしまい、「無いのか、壊れているのか」が誰にも分からない沈黙にすり替わった。隠すことは、直すことではなかった。
  • 魔術の定石(学んだ設計パターン):
    • Null安全とNull Object。Catalog.findBook?(空かもしれぬ印)を握りつぶさず、?.?: で安全な実体へ変換した。該当なしの「何もしない振る舞い」は NotFoundBookobject のシングルトン)一箇所に集約し、呼び出す側から空の確かめを無くした。一方、壊れた入力は require で声を上げて弾き、正当な不在(Null Object)とはっきり区別した。
  • 模擬詠唱の記録(検証のポイント):
    • 該当なしで台が凍らず「(該当なし)」を返すこと、その本の貸出が例外でなく安全に何もしないこと、空コードは require で声を上げて弾かれること(assertFailsWith)を確認した。見つかる本の表題・著者は、Before から After まで一貫して変えていない(直したのは「不在の扱い方」だけ)。
  • 師匠マグナスの格言:
    • 「空(から)であることは、隠すべき疵(きず)ではない。隠したとき、はじめて疵になるのだよ。」
comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。