複数のメンバーで開発していると、この関数の引数がこうだったらいいのにと思うことがある。そのことについて述べてみる。
何を当たり前のことを述べているのだと思うだろう。そう、当たり前のことしか書いていないから、貴重な時間を、この記事を読むのに使う必要はない。
##原則1:一番融通のきく変数を関数の引数としよう。
例:画像ファイル名を引数とするよりも、画像データそのものを引数としよう。
画像の中に顔があるかどうかを判定する関数を作るとしよう。
bool hasFace(const std::string fname);//ファイル名を引数とする設計
bool hasFace(const cv::Mat &img);//cv::Mat型を引数とする設計
この2つの設計のうち、どちらを実装するかは、一番融通のきく変数を実装するべきだと考える。
hasFace(cv::imread("lena.jpg"))
のように書けるのは後者である。
cv::VideoCapture()と相性よく処理できるのも後者である。
だから、常に
bool hasFace(const cv::Mat &img);//cv::Mat型を引数とする設計
の実装をすべきと考える。
##原則2:テストが容易なインタフェースを持つように関数を設計する。
例:次のような3種類の可能性があるときには、テストが容易なインタフェースを含めること。
bool isSmiling(int deviceID);//VideoCapture deviceの device ID
bool isSmiling(const std::string imgFileName);
bool isSmiling(const cv::Mat &img);
USBカメラからの入力があって、そこに笑っている顔が写っているかどうか判定する関数があるとしよう。
その場合には、テストが容易なインタフェースを含めるように設計すべきと考える。
bool isSmiling(int deviceID);//VideoCapture deviceの device ID
が必要なインタフェースと求められている場合に、このインタフェースしか実装しないと
この関数は単体テストができない関数となってしまう。
この関数をテストする場合には、USBカメラを接続して、その前で笑顔でいることが必須となってしまう。
bool isSmiling(int deviceID)が必須のインタフェースとして設計されている場合でも、
その内部は、
bool isSmiling(const cv::Mat &img);
を用いて実装しよう。
そうすれば、入力画像をファイルで用意しておいて、単体テストをすることが可能になる。
##原則3:関数は直交性よく設計する
bool isFace(const std::string url_of_imageFile);
を開発するのではなく、
bool isFace(const cv::Mat &img);
と協調的に動作する関数
cv::Mat getWebImage(const std::string url_of_imageFile);
を開発しよう。
そうすれば、相互に依存性がないため関数のメンテナンスも楽だし、
cv::Mat getWebImage(const std::string url_of_imageFile);
を別の目的にも利用できる。
bool isFace(const cv::Mat &img);
は、cv::Mat getWebImage(const std::string url_of_imageFile);
を知る必要もないし、
cv::Mat getWebImage(const std::string url_of_imageFile);
はbool isFace(const cv::Mat &img);
を知る必要もない。
そのため、余分なヘッダファイルのincludeも生じないので
メンテナンス性が向上する。
仕事で与えられる関数の仕様が、上記の原則1~3に該当していないときには、
上記の原則1~3に該当する関数を作って、それを利用して実装すべきと私は考える。
付記:
原則3は、単一責務の原理からも求められる。
bool isFace(const std::string url_of_imageFile);
bool isFlower(const std::string url_of_imageFile);
bool isCat(const std::string url_of_imageFile);
などを実装したときには、
web上から画像ファイルをもってくる機能が、それぞれの関数にコードとして書かれているようになってしまう。
付記:
直交性がよいとは、相互に無関係ということです。なぜ無関係なことを直交というのかは、少しだけ数学を説明させてください。
ある量と別な量が無関係なときは、相関係数は0になります。ある量のベクトルと別の量のベクトルの内積が0になることを、ベクトルの世界では直交といいます。a=(1, 0) と b=(0,1)では内積をとると0になります。グラフにaとbのベクトルを書いてみると、まさしく90度で直交していることが分かります。このため、2つの量の間が無関係であることを直交しているといいます。
追加
速度が重要なときには
cv::Mat someFunction(const cv::Mat &img);
ではなく、
void someFunction(const cv::Mat &img, cv::Mat &outImg);
のようにAPIを設計する。既に領域を確保済みの配列を利用することで、関数の中で新規に領域を確保しなおすことをなくす。
付記:
実際には、顔があるかどうかというbool型ではなく、顔検出の結果を返す関数であるべきでしょう。ここでは、物事の単純化のため、次のような案としました。
bool hasFace(const cv::Mat &img)