設計の確定しきらない部分をstd::unordered_map型に押し込もう


「std::unordered_map型を使って見通しのよい設計をしよう」を改題

  プログラムは、設計がなかなか確定しきらないものです。場合分けにしても、当初想定していたものから増えたり減ったりするものです。それにともなってswicth文やif else if文はそれにともなって、分岐が増えたり減ったりします。enum型やマクロ定数や配列の個数も変化します。それらのことは、プログラムの中で確定しきらない部分が残ってしまうことを意味します。
 
 そこで、本記事が主張することは、設計の確定しきらない部分をstd::unordered_map型に押し込むことで、プログラムの実装を楽にしようとするものです。

マクロ定数によるカスタマイズからstd::unordered_map型の値によるカスタマイズへ

 一方、std::unordered_mapのテンプレートを使ってコーディングしてswitch文やif else if文を用いなくした場合には、厄介な部分はstd::unordered_map型が面倒を見てくれる。ロジックの部分に対して変更を免れることができる。場合分けが当初想定していたものから変化しても、その部分はstd::unordered_map型のキーと値の組み合わせの追加・削除だけで済むようになる。そのため、場合分けの追加・削除への対応の際の不整合を生じにくくなります。#define マクロ定数による定義を追加する流儀では、switch文やif else if文では、ソースコード中の複数の箇所で変更を必要とすることも多いでしょうし、対処し忘れを生じやすくなります。

プログラムの中で確定しきらない部分は、プログラムが最終的に確定しない限り残るものでしょう。プログラムの中で確定しきらない部分は、移植をする際にも、移植した元のコードが絶えず変化してしまって、コードのメンテナンスに課題を生じやすくなります。

プログラムの設定パラメータへの利用

プログラムの設定パラメータで、カスタマイズ可能なことを期待するパラメータも絶えず変化していきます。私がとっている手法は、アプリケーションの設定パラメータを、std::unordered_map で管理することです(後述のC++プログラムを参照のこと)。しかも、設定パラメータを、ファイルから読み取ることで、設定パラメータの数が増えても、プログラムの枠組みを変更せずにすみます。設定パラメータを個々の変数として管理する場合だと、その変数を明示的に読み書きすることをソースコードに書かなくてはなりません。しかし、unordered_map型を使ってキーと値で管理すれば、キーと値のペアをファイルから読むようにすること、キーを元に値を参照することができる。設定パラメータをunordered_mapにして管理すれば、設定パラメータが、ソースコードの中で迷子になることもありません。設定ファイルを読み書きする部分は、設定パラメータの数が増えても、1行の変更をすることなく実装をcloseすることができます。
 このように、unordered_map型をプログラムの設計に利用すると、プログラムの見通しが大幅に改善します。unordered_map型を使って楽しいC++生活をしましょう。

 std::map型と書いていましたが、std::unordered_map型で十分であることの指摘をいただきました。そのとおりなので、修正いたしました。キーの並び替えをしない分処理が簡潔になるので、std::unordered_map型の方が今回の目的ではふさわしいと考えています。

参考:ファイルから、キーと値のペアを読み込むプログラム

std::unordered_map型を使ったコード

readMap2.cpp
#include <fstream>
#include <iostream>
#include <string>
#include <iterator>
#include <unordered_map>
int main(){
    std::ifstream ifs("test.txt");
    std::string key;
    std::string val;
    std::unordered_map<std::string, std::string> maps;

    ifs >> key >> val;
    while (!ifs.eof()){ // ファイルの最後まで読み込んだら終了
        maps[key] = val;
        ifs >> key >> val; // 最後に読み込む
    }

    // 要素を出力する
    std::unordered_map<std::string, std::string>::iterator it = maps.begin();
    while (it != maps.end()){
        std::cout << it->first << ":" << it->second << std::endl;
        ++it;
    }
    return 0;
test.txt
H hydrogen
He helium
Li lithium
Be beryllium
B boron
C carbon
N nitrogen
O oxygen
F fluorum
N neon

実行結果

H:hydrogen
O:oxygen
Li:lithium
He:helium
Be:beryllium
B:boron
C:carbon
F:fluorum
N:nitrogen

std::map型を使ったコード

readMap.cpp
#include <fstream>
#include <iostream>
#include <string>
#include <iterator>
#include <map>
#include <vector>

int main(){
    std::ifstream ifs("test.txt");
    std::string key;
    std::string val;
    std::map<std::string, std::string> maps; 

    ifs >> key >> val;
    while (!ifs.eof()){ // ファイルの最後まで読み込んだら終了
        maps[key] = val;
        ifs >> key >> val; // 最後に読み込む
    }

    // 要素を出力する
    std::map<std::string, std::string>::iterator it = maps.begin();
    while (it != maps.end()){
        std::cout << it->first << ":" << it->second << std::endl;
        ++it;
    }
    return 0;
}

実行結果

B:boron
Be:beryllium
C:carbon
F:fluorum
H:hydrogen
He:helium
Li:lithium
N:nitrogen
O:oxygen