設定ファイルから true か false を読み出す。
例えば何らかの設定ファイルに true か false という値が書いてあって、それを読み出して拡張機能の有無を変更しなければいけないとします。いろいろ状況は違うだろうけど、とりあえず std::wstring GetProperty( const char* pch ) という関数で "EnableExtention" という Property を取ってくるものとします。
こういうのを何も考えないプログラマがこういうのを作るとこんなかんじでしょうかね。
if( GetProperty( "EnableExtention" ) == "true" ){ // ここで拡張機能をなんとかする。 }
とりあえずこれで問題はないかもしれません。
ですが、これだとおそらく半年ぐらいたってから、『EnableExtentionの設定が効かない』といって問題になります。
調べてみると、設定ファイルには TRUE と書かれていたりします。"TRUE" は "true" ではないから false 扱いです。
これを『そんな想定外の値を書くほうが悪い』とか、わけがわからないことをプログラマが本当に言い出すからたちが悪いです。誰が悪い、とかじゃなくて、現に問題が発生しているのだから、それはプログラマが責をおうべきです。さらには私に言わせりゃ『そんなことすら想定しないオマエがわるい。』
ちょっと考えてこうするとします。(Text という文字列関係の関数を集めた static クラスがあるものとします。)
bool to_enable = Text::EqualsIgnoreCase( Text::Trim( GetProperty( "EnableExtention" )), "TRUE" ); if( to_enable ){ ... }
これで True TRUE true 問題はなくなります、が、どう考えてもソースコードの可読性は落ちました。一見して何をやっているかがわからない。(これでも、Text というクラスがある想定だったり、一度変数に入れているからまだいいほうです。平気で stricmp をとって == 0 とかやったりします。)
こういう人がプロジェクト内に一定以上いたり、ごく一部でもその人がよくわからない政治的な力を持っていたりすると『細やかな考慮を行うとソースコードの可読性が落ちる』というイメージが浸透してしまいます。何も考えていないわけですから、それを表立って言うことはないのですけど、漠然としたイメージって大きいです。
で、ライブラリ作成者としては、世の中の大半のプログラマがこういうコードを書いてしまうことや、それによって問題が発生してしまうことは現に予測可能なわけで、誰が悪い、とかじゃなくて、それはライブラリ作成者が責をおうべきで、そんなことすら想定しないライブラリ作成者がわるい。わけですよ。
bool 設定を読み取る、ということは予測可能なので、(GetProperty は弄れないとして)一旦クラスに*1入れて、そういう関数を作りましょう。
class Property{ public: static string GetTrimed( const char* name ){ return Text::Trim( GetProperty( name ) ); } static bool GetBool( const char* name ){ string s = GetTrimed( name ); return Text::EqualsIgnoreCase( s, "true" ); } };
で、これで true TRUE True 問題はなくなり、使う側も
if( Property::GetBool( "EnableExtention" ) ){ ... }
比較的わかりやすいです。
"EnableExtention" 設定の読み込み、としてはこのくらいでほぼ問題はないでしょう、ですがライブラリとして、『設定ファイルから bool を読みとるもの』としては考慮が甘いです。設定ファイルの値が、例えば FASLE(FALSEの間違い)だったらどうしましょう?真じゃなかったら偽、で問題ないでしょうか?偽じゃなかったら真としたい設定もあるはずです。
すると、こうなります。
class Property{ public: static string GetTrimed( const char* name ){ 省略 } static bool GetBool( const char* name, bool def = false ){ string s = GetTrimed( name ); if( Text::EqualsIgnoreCase( s, "true" ) ){ return true; } if( Text::EqualsIgnoreCase( s, "false" ) ){ return false; } LOG_WARN( "%s が予期しない値なためデフォルト値 %s を使用します。", name, def ? "true", "false" ); return def; } };
で、ここで『デフォルト値』という概念が出てきたしまったので、考慮すべきことがちょっと増えます。
プログラマが値を取ってくるたびにデフォルト値を意識しないといけないようにすると、それを間違える可能性があります。
if( Property::GetBool( "EnableExtention", true ) ){ ... } 別のところで、 if( Property::GetBool( "EnableExtention" ) ){ // ←第二引数を忘れてる。 ... }
これを回避するためには、『デフォルト値のマップ』みたいなものをもたせて、正規の方法で取れなかったらそっちから取ってくるようにして、プログラムの初期化時にデフォルト値のマップに一通り値を入れておく、とか。プロパティーのキーの方を何らかのオブジェクトにしてしまって、それ自体がデフォルト値を知っているようにする、とか。
あるいは厳密に設定させたいのなら起動時に一通り設定をナメて、想定外のものだったら例外吐いて落とすような機構を作ってしまってもいいかもしれません。けど、その場合その『想定』をなんらかの言語で表現しなくてはいけない煩雑さが出てきますし、予め用意したキーしか使えないことになります。それは『厳密にする』という意思決定から来るトレードオフなわけですけど、それが良い場合もあるし、悪い場合もある。一時期の『何でもXMLで設定すりゃいい』って言うような風潮は、このトレードオフをちゃんと意識して決定しているとは言いがたいものでしたね。
人間って極端に走りがちで、こういう問題を発見すると『○○を導入してその問題をすべて潰そう』とか、やっちゃいがちで、一時期のJavaコミュニティのXML偏重とかはその最たるものだったと思うんですけど、変な話なんですが、こういうのってどんな機構を導入しても『考えなくていい』ということにはならないんです。何が最適か、ということはそのプロジェクトやモジュールの性質によって違う*2。こういうトレードオフを考えないで『○○を使ってるから○○は全部OK』とかやるのは問題を先送りするだけです。(それでも何も対策しないよりは100倍マシなんですけど)
考えなくてはいけないことがあるならばそれは考えなくてはいけない。
けれども、上手に考えることで、特定の領域においてはそのことを忘れていいようにプログラムを持っていくことは可能で、ライブラリを作る人はそれを目指していくべきなんじゃないかなって思います。