例のごとくタイトルは釣りですね。
「グローバル変数の引数化」と「名前空間の多層化」の比較がこの記事の内容ですが、オブジェクト指向プログラミングと関数型プログラミングについては語りません。
ですが、グローバル変数の引数化と名前空間の多層化は、オブジェクト指向プログラミングと関数型プログラミングを対立させようとする人たちの中にある「何かひっかかるもの」を明らかにしてくれるのではないかと思っています。
前提条件と対象読者
誰しも、グローバル変数は憎むべきものだと思っています。その前提が違うのであれば、この記事は読まないほうがいいでしょう。
同意いただけるのであれば、そのグローバル変数を減らすアプローチとして、どうするべきなのか、一緒に考えていただければと思います。
ただ、一つ確認しておきたいのは、グローバル変数を絶滅させることはできません。
その前提で対応するべきは、トップレベルの名前空間以外の場所からグローバル変数へのアクセスだと思っています。
トップレベルでは、必ず何かしらの状態を定義する必要があるわけで、それが変数であり、スクリプト言語は多くの場合、トップレベル名前空間はグローバルです。
グローバル変数の引数化
各論に入ります。
以下のようなJavaScriptコードがあった場合に、
var a = 2;
function add(b) {
return a + b;
}
function sub(b) {
return a - b;
}
console.log(add(1)); // 3
console.log(sub(1)); // 1
以下のように修正することができます。
var a = 2;
function add(_a, b) {
return _a + b;
}
function sub(_a, b) {
return _a - b;
}
console.log(add(a, 1)); // 3
console.log(sub(a, 1)); // 1
単に、グローバル変数を引数に渡しているだけのシンプルな修正です。
これをグローバル変数の引数化と定義します。
ただ、addにもsubにもaを渡しています。
これがaだけならこのままでいいでしょうが、より引数が増えてきたら、煩雑に感じるかもしれません。
名前空間の多層化
グローバル変数の引数化で毎回引数に同じ変数を与えるのが面倒と思った先人は以下のようにしました。
var a = 2;
function calculation(_a) {
var add = function (_a, b) {
return _a + b;
}
var sub = function (_a, b) {
return _a - b;
}
return {
add: add,
sub: sub,
};
}
var calc = calculation(a);
console.log(calc.add(1)); // 3
console.log(calc.sub(1)); // 1
こうすれば、aというグローバル変数を何度も引数に与える必要がなくなります。
このアプローチを名前空間の多層化と定義します。
グローバル変数aは関数calculationの変数_aに束縛されて、関数calculationという名前空間のグローバル変数になっているからです。
関数calculationの内部で定義されている関数addやsubから見れば_aはローカル変数ではなく、外側の名前空間の変数です。
これをグローバル変数ととるか、ローカル変数ととるかは、視点によって違いますが、少なくともグローバル変数としての側面を持っていることは間違いありません。
つまり関数calculation名前空間のグローバル変数となったと言えると思います。
釣りの話題
もうお分かりだと思うのですが、名前空間の多層化を発展させていけば、オブジェクト指向的プログラミングなアプローチになります。
また、関数それぞれの参照透明性を重んじる関数型プログラミングにおいては、多層化させた名前空間であってもグローバル変数であるので、あまりよくないと考えるのではないでしょうか。
2015年ごろ流行った、オブジェクト指向プログラミングと関数型プログラミングを対立させようとする人たちの動きは、根源的にこういったことがあるのではないかと思っています。
ですが、この話題は本筋ではありませんので、これ以上は言及しませんし、私もこれ以上の意見は持ち合わせていません。
グローバル変数の引数化を優先
ですが、どちらを使うべきかは考える必要があると思っています。私は、名前空間の多層化よりも、グローバル変数の引数化を優先してコードを書くべきだと思っています。
名前空間の多層化では、一見aを一度しか使っていませんが、calcは2度使っています。calcの中にaを内包させてしまったのです。
calcの管理下に置いたaという変数をトップレベルの名前空間で変更してしまうと、calculationの名前空間にある_aに影響を及ぼしてしまいます。
(JavaScriptの基本型は値渡しなので、影響はないが、簡略のためその想定で)
そうでなくても、管理する名前空間が増えるのはプログラマを混乱させるような気がします。
グローバル変数の引数化であれば、トップレベルと、そこから呼び出す関数しか名前空間がないのでより理解しやすいと思っています。
したがってグローバル変数の引数化のほうがよいアプローチであると私は思っています。
次に名前空間の多層化
しかし、トップレベルの名前空間に変数が増えてくると、それを分類し、仕分けてしまいたくなります。そのほうが階層的に変数を分類管理できて、便利かもしれません。
また、calculation関数の作者が第三者であった場合はどうでしょう。
そのモジュール開発者は、addとsubを同じ基底数をもとに算出するべきと仕様定義するかもしれません。
そういった場合に、何度も同じ値を引数に与えさせるのは、バグを生みます。
また、calculation関数の作者にとっては、calculation関数自体がトップレベルの名前空間です。
そう考えると、名前空間の多層化も悪くないように思います。
私の考えとしては、まずはグローバル変数の引数化を優先してコードを書く。
そして、一人で管理するには責任範囲が多くなってしまう場合に、名前空間の多層化を行う。それでプログラマそれぞれが、その名前空間で自由に変数にアクセスできるようにしたほうが効率がいいかもしれない、とそのように思っています。
まとめ
グローバル変数の引数化と名前空間の多層化は対立する要素ですが、どちらも一長一短があり、プロジェクトの規模、特性に合わせてどのようにコードを書くべきか、考える必要があるのだと思っています。
皆さんはどうお考えでしょうか。
ご意見いただければと思います。