selecao3のブログ

技術系の備忘録

グローバル変数の問題点メモ

これ is 何?

よくグローバル変数は良くないと聞くがなぜそれがダメなのかの具体例

経緯

最近、バイトで「この書き方はクソコードだよ」とふんわりと教えてもらったので。

本題

よくグローバル変数はヤバいと聞く。結論から言うと、関数と関数の関係が密になりやすいからである。例えば

filename = "hoge.txt"

function init_file()
     local time = tostring(os.date("_%H%M%S"))
    filename = "hoge"..time..".txt"
    local f = io.open(filename, "w")
    if f == nil then
        io.stderr:write("failed open file")
        return nil
    else
        f:write("header hoge\n")
        return f
    end
end

function write_to_file(file, mes)
	file:write(mes.."\n")
end

function close_file(file)
	file:write("close file\n")
	file:flush()
	local ret = file:close()
        if ret == nil then
           local ret = false
        end
	return ret
end

function execute_cat_file()
    local ret = os.execute("cat "..filename)
    if ret == nil then
        local ret = false
    end
    return ret
end

こんなLuaコードを書いたとする。一見問題なさそう、むしろinit_file()したらグローバル変数のfilenameに格納されて、他の関数にfilenameを入れることなく使えて楽やん。と、思ってしまう(少なくとも自分は思っていた)
が、このコードには2点問題がある。

  1. init_file()しないと他の関数は使えない点。例えば、execute_cat_file()したいだけなのにわざわざinit_file()しないとダメ
  2. 他のfilenameが使えない。例えば、hoge_100001.txtを生成した後にhoge_100010.txtをinit_file()で生成した場合、hoge_100001.txtは上書きされてしまうので扱うことができなくなる(closeしてないやんっていう問題もあるが)

つまり、最初に話した通り密な関係になってしまう。じゃあどうすればええねんっていうと、

  1. グローバル変数削除
  2. init_file()内でファイルを開く処理を削除。
  3. write_to_file()以外の全ての関数の引数にfilenameを追加する。

以上の3点である。グローバル変数を引数にすることで各関数を疎の関係にする。
外部で生成したいファイルの名前を生成してもらうことになるが、こっちの方が使い勝手が良い。

まとめ

グローバル変数使った時は無意識に密な関係になっていてアレなコードになっている可能性がある。引数で使えるようにして疎な関係にしよう。