- ブログ
君たちはどう区切るか。〜無名クラスについて〜
2026.07.08 Wed

$value = null;
try {
// 長めの処理
$value = $service->create();
// 長めの処理
} catch ($e) {
throw $e;
}
try {
$param = [
'hoge' => $value->hoge_hoge,
];
} catch ($e) {
throw $e;
}上記のようなコードに遭遇した。
下側のtry-catchブロック内の処理を変更しようとして$valueの存在に気づいたのだが、2つの問題があった。
・$valueは変数名として普遍的すぎてforEachなどの別のスコープで同じ変数名が定義されていた
・$valueに何が入っているのか知るために長めの処理を読むことになった
例えばこんな書き方だったらどうなるか
TypeScriptだったらこのように書いただろう。
const value: Item = (() => {
try {
// 長めの処理
return service.create();
} catch (error) {
throw error;
}
})();
const params = {
hoge: value.hoge_hoge,
};もしこう書かれていたら、ほんの少し理解しやすい。
valueは定数だし、そのvalueを作っているのは長い処理だけど、少なくともその処理の最終的な出力であることはわかる。
value.hoge_hogeの参照においてもvalueがnullでないこと、「hoge_hoge」プロパティがあることもわかっている。
PHPだとどうか。
$value = nullがいつ上書きされるのかわからない。
最終的な出力かもしれないし、ifで分岐したところで上書きされてそれっきりかもしれない。
ちなみに、PHPにもconstはあるがグローバルを汚染する。
https://www.php.net/manual/ja/language.constants.syntax.php
幸い、PHPにも無名関数はある。
それを使うとこうなる。
$create = function (): Item {
return $service->create();
};
$value = $create();
try {
$param = [
'hoge' => $value->hoge_hoge,
];
} catch ($e) {
throw $e;
}これでも結局、$valueは変数なので、もし処理を追加すれば上書きされる可能性は依然としてある。
さらにPHPの無名関数もグローバルを汚染する。
どうなれば読みやすいか
「作って、作った結果からある値を取得する」これが書きたいのだ。
なのに「作って」の部分が長くなるといつどこで作られたのか読み取りづらくなる。
極論、こうなってほしいのだ。
$value = create();
fugafuga(
item: $value,
);
function fugafuga (Item $item) {
$param = [
'hoge' => $item->hoge_hoge,
];
}fugafugaはこれまで$paramを引数にとっていた何かの処理である。
こう書けば「作って、作った結果はItemという型であり、それを使って何かする」ことがわかる。
functionの引数は型指定ができるので$valueが上書きされていようが気にすることはないのだ。
そしてこうなった
function sample () {
$innerMethods = new class {
function create (): Item {
$value = $service->create();
return $value;
}
function fugafuga (Item $item) {
$param = [
'hoge' => $item->hoge_hoge,
];
}
};
$item = $innerMethods->create();
$innerMethods->fugafuga($item);
}こうすると、各処理の境目がfunctionという区切りになり、functionは引数の型を指定できるので型が担保される。
さらに「処理」に名前がつくことでそこで何をしているかが一目瞭然になる。
最初はどこからどこまでが「作成」処理なのかすら曖昧だったのだ。
そして、無名クラスというところにもポイントがある。
先ほど「PHPの無名関数もグローバルを汚染する」と述べたが、実は関数内クラスもグローバルを汚染する。
一つだけクロージャのスコープ外に出ないものがある。変数だ。
この場合、$innerMethodsはsample関数外を侵さない。
補足
というように無名クラスを使うことで
「処理に名前をつけ」
「処理の入出力を型で言語レベルで明確に」
「他の関数に影響を与えない」
ことができた。
のだが、これはそもそも設計段階で「作る処理」「作ったものを使う処理」が分かれていれば無名クラスなんて使わずに普通にクラスを分ければいい。
だが、ウォーターフォール型の開発と違い、アジャイル型(もしくはそれに近い)場合、稼働している状態での設計変更はコストが高い。
そういう場合には無名クラスを使うことで1つのメソッドの責任範囲内に区切りを設けることができる。
終わりに
私が無名関数や無名クラスを使うようになったのは最近なのだが、この書き方をすることで学びがあると感じている。
この学びはPHPに限った話ではなく、ありとあらゆる言語に通じるものだと思っている。
・「ある処理」に名前をつけることで「create」の中に紐づく他のテーブルの更新は入れてはいけないと分かる。
・スコープが狭いので変数名が簡潔で済む。結果コードが短くなって読む際に読解コストが低い
・「ある処理」が区切られているので後から別クラスとして定義し直すのも容易
などのメリットがあった。
あとはコメントアウトの数が劇的に減った。なんならほぼ書いてない。
だって変数名と関数名が全てを表しているからだ。
それによって本当に読ませたいコメントアウトが際立つ。
際立つことで古いコメントアウトが置き去りにされることもない。
このように「端的で簡潔な名前で処理を区切る」というのは程度の違いこそあれどエンジニアの必須技能だと思うのだが、この書き方をするようになったことで
「どこからどこまでが一つの処理か」
「どのような名前なら一発で理解できるか」
を常に意識して書くことができる。
……んじゃないかなぁ
と思っています。