引数の多い関数と表現と設計

引数が多い関数は悪、リファクタリングするべきのような言説を見て引っかかったので、メモ程度に書きとどめておきます。

引数の多い関数は全く悪くない

世の中には引数の多い関数は見にくいので悪!構造体にするなどして減らすべき!と言う意見がありますが、本質的ではありません。例えば、日時を表すクラスを考えるとします。コンストラクタの引数には、(西暦、月、日、時、分、秒)を指定できるとしましょう。

日時(西暦、月、日、時、分、秒)

果たして、これらの引数は減らすことができるでしょうか?もちろん、秒、分をオプショナルなものとして、省略を可能にすることはできると思いますが、もし、秒まで指定したいとするならば、このコンストラクタの引数を減らすことはできないでしょう。それは、このクラスが本来的に6つの変数が必要な自由度を持っていることに起因します。なので、このクラスのコンストラクタの引数は減らすべきではありません。

引数を構造体にする、あるいは Builder パターンを使う

とはいえ、見かけ上の引数を減らすことはできます。引数を構造体でまとめることもできますが、一般化すれば Builder パターンを使うことになるでしょう。Builder パターンでは、あるクラスAを作成するクラス、ABuilderを定義し、各引数やプロパティについて SetB(b) のようなメソッドを用意し、オブジェクトの生成を肩代わりします。しかし、これは1行に並んでいた引数を縦並びに変えただけで、本質的な解決にはなっていません。クラスそのものの複雑さは変わりませんし、作り方によっては引数の設定漏れも増えてしまいます(引数の並びの間違いはコンパイラが指摘してくれるのに!)。Builder パターンはオプショナルなパラメータが多い場合や、可変長の引数を手続き的に扱いたいときに用いるべきです。

引数を分かりやすくできるケース

しかし、引数を分かりやすくできるケースというのは存在します。例えば、将棋盤を作成するクラスを考えるとします。将棋盤は9×9の81マスですから、もし、駒を引数に将棋盤を表現するとしたら、次のようになるはずです。

将棋盤(駒1、駒2、駒3、……、駒81)

これは冗長に見えますが、実際のところ、将棋盤はそれだけの自由度を持つのですから、間違ってはいません。しかし、これを分かりやすくすることはできます。例えば、駒の種類とその位置をセットにした構造体を作り、それを可変長の引数で受け取るようにすることです。

配置された駒(駒、縦の位置、横の位置)
将棋盤(配置された駒1、配置された駒2、配置された駒3、……)

この形は、入力する情報は若干増えることになりますが、最初のコンストラクタよりは、幾分か人間に理解しやすい形になっているはずです。気取った言い方をすれば、将棋盤は空白が多く、そのスパース性に着目した表現とも言えるでしょう。しかし、このコンストラクタは、重複した位置に駒が置かれる可能性があることを留意するべきです(それでいったら二歩とかも考慮しなくちゃいけませんが)。 このようなトレードオフは、引数の設計に限らず、プログラムに多く見られます。プログラムの複雑性をどう紐解くか、という点において、引数の設計は非常に大切です(そして、それはプログラムの設計の本質でもあります)。このような問題をシンプルすぎる物差しで測るのは、あまり得策とは呼べないでしょう。