クラス継承で「基本/例外」を意思表示できる?

プログラミング設計に関して、多くの原則が謳われていると思います。最近、業務でも設計原則について考えることが多く、コードレビューをしている中で意外と気づいていなかった観点がありました。それがクラス継承で「基本/例外」を表現できる、というある意味では当たり前の内容です。

クラス継承そのものが悪い文化であるように言われ始めて、もうどれぐらい経つんでしょうね。あんなに持て囃されていたのに、ひどい話です。と言いつつ、やっぱり継承を使って設計するのは難しい。詳しく言うと、変更に対する制限が強すぎるんじゃないかと思います。その割に委譲と比較してメリットがそこまでないと歴史に判断されてしまったような気がしています。

一方でECMAScriptにはclass構文が入ったわけだし、他の言語もクラスの存在自体を消しているわけではない感じです、少なくとも今のところ(Rustにはもうないですけど)。また、トレイト(言語によって意味が違いますがまあざっくりで)やインターフェースに関しては継承を許している言語も多く、「継承」自体が悪というよりは、「クラス継承」がよくなかったんですかねえ(この点に関して自分の意見はありません、すいません)。

さて、コードレビューをしている時にわたしが結構気にする観点は、部品同士の関係性をプログラマーの意図どおりに表現できているか、ということです。ソースコードで同じファイルに書くかどうか、というのは動作上関係がなかったとしても、その人の意図を示す大切な手がかりになり得ます。例えば、モジュール変数がどのファイルに書かれているのか、によってその変数がどの内容に属している(と実装者が考えている)のかを伝えられます。願わくばソースコードだけでそういったものが伝えられればいいんですが、コメントは効率上の問題などが原因で、実装そのもののドキュメントとしての効力が弱い場合に使うものです。

関数分割も同様かと思います。関数の分け方を見れば、実装者がどのカタマリを「同じグループ」だとみなしているのかが伝わってきます。凝集度は、それらを別の観点で分類していますが、結局「読む人」「あとで変更する人」のための概念なのは同じです。

ある時コードレビューをしていて、自分の想定と違う関数の作り方をしていたので質問したところ、その認識の違いは「基本/例外」だったことがありました。具体的には、数値に意味づけをして、分類ごとに識別子を返す関数を追加して欲しいという内容です。数値を引数に、文字列を戻り値にする関数を1つ作ればいいな、と思っていたのですが、実装者は2つの関数を作ってきました。どうも噛み合わないので意図を探っていくと、「今回やりたい処理は、ほぼ2つのユースケースA・Bに収束され、しかも普段使われるのはAの場合が多い。なのでまずAを行う関数を作り、例外的にBのユースケースを行いたい場合の関数も、別に用意した」とのことで、なるほどと膝を打ちました。伝わってます?

わたしは行いたい処理をざっくり1つのユースケースとしてまとめあげてしまっていました。一方、実装者はそれらのユースケースを詳細に検討して、AB両方で使う2つの関数に分けた。すばらしい。ただ、わたしも含め、レビューワ側は関数2つが並んでいるのを見て、すぐにはその「意図」を理解できませんでした。そこで「ああ、継承だったらキレイに表現できるかも」と思いついたわけです。

これがもし、AとBの関数がそれぞれ違うクラス(AA、BBと呼ぶ)に実装されていて、AAがBBを継承していたら、上記の意図ってすぐ伝わったと思うんですね。しかもこれは委譲では表現できない。実際には、今回の例で実際にクラスを使うかどうかは微妙なところですが、大切なのは「継承」という考え方でうまく伝えられるユースケースが確実に存在する、ということです。だから、言語機能として、(クラスに限らず)言語要素を「継承」できるプログラミング言語とかを想像すると楽しいのかもしれないですね。すでに存在していたらすいません…