フェイルセーフというのは、機器故障しても安全なほうに倒れるシステムの組み方で、原発の制御棒(BWRはフェイルセーフに反しているが、PWRはそれが解消されている)が重力で落ちるようになっていたり、原子炉自体が半分以上地下にあったり、冷却水が上から下へ流れるようになっていたり(これもBWRはフェイルセーフに反している)する仕組みです。
フェイルセーフ – Wikipedia
http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A7%E3%82%A4%E3%83%AB%E3%82%BB%E3%83%BC%E3%83%95
ちなみに、フォールレトラントのほうは、故障が起きても処理を継続できる仕組みで、フェイルセーフのように「問題が起きても大丈夫」という点では一緒なのですが、問題発生の後が異なります。
フェイルセーフは、問題発生→安全に機能停止
フォールレトラントは、問題発生→稼働率は下がるが処理を続行
という感じです。
# 情報処理の試験の解答がどうだったか覚えていないのですが…ここで解説するフェイルセーフは「安全に機能停止」のところに主眼を起きます。
さて、フェイルセーフなコードに関しては、過去にもいくつか書いていたりするのですが、再び。
// 指定年齢以上のカラムを取り出す List<DataRow> dest = SelectMoreAge( src, 20 )
// 指定年齢以上のカラムを取り出すメソッド
List<DataRow> SelectMoreAge( DataTable src, int age )
{
List<DataRow> dest = new List<DataRow>();
foreach ( DataRow r in src.Rows )
{
int a = (int)r.Item("age");
if ( a >= age ) {
dest.Add( r );
}
}
return dest ;
}
こんなソースがあったとき、一発で分かるのは、
・src が null だった場合はどうするのか?
・SelectMoreAge メソッドの戻り値は null になるのか?
のようなチェックです。他にも、パラメータ age の値が
・マイナス値のときは、どうなるのか?
・0 の場合はどうなるのか?
・10000 などの大きな値(年齢とは思えない値)のときはどうなるのか?
というチェックがあります。
# 数値なので、-1,0,10000 でも余り変わらないのですが、例として。
■引数の null をどのように処理するのか?
結論から言うと、フェイルセーフの考え方でいくと、「例外を出さずに、何もなかったように処理を続ける」のが正解です。
・引数が null だから null exception を発生させる。
・引数が null だから、エラーが分かるように戻り値を null にする。
ということは「しません」。
フェイルセーフの「安全に機能停止」を求めようとすると、SelectMoreAge メソッドは処理としては、異常だけれども大きな影響を出さずに止まる、という流れに乗せます。
// 指定年齢以上のカラムを取り出すメソッド
List<DataRow> SelectMoreAge( DataTable src, int age )
{
List<DataRow> dest = new List<DataRow>(); ※2
if ( src != null ) { ※1
foreach ( DataRow r in src.Rows )
{
int a = (int)r.Item("age");
if ( a >= age ) {
dest.Add( r );
}
}
}
return dest ;
}
まずは、※1 のところで、src の null チェックをします。このときメソッドを使っている側の対応が困らないように、戻り値を ※2 のところ、あらかじめ作っておきます。
SelectMoreAge の呼び出し目的は、指定した年齢以上のデータがほしい訳ですから、0 件を返してやれば、呼び出し側は安全に停止する(数件取得できることを期待するので)ことができます。
呼び出し側では、以下のように null チェックをせずに済みます。と言いますか、null チェックを忘れても動くようになります。
// 指定年齢以上のカラムを取り出す
List<DataRow> dest = SelectMoreAge( src, 20 )
if ( dest.Rows.Count > 0 ) {
// 数件あるときの処理をする
}
■引数が期待されたもの以外の場合は、どう処理するのか?
引数 src の値が null の時は、明らかにエラーなのですが、年齢が -1,0,10000 の場合は定かではありません。
なので、ある程度期待される範囲を制限して処理すると、より安全になります。
// 指定年齢以上のカラムを取り出すメソッド
List<DataRow> SelectMoreAge( DataTable src, int age )
{
List<DataRow> dest = new List<DataRow>();
if ( src != null ) {
if ( 0 <= age && age <= 100 ) {
foreach ( DataRow r in src.Rows )
{
int a = (int)r.Item("age");
if ( a >= age ) {
dest.Add( r );
}
}
}
}
return dest ;
}
こんな風に、SelectMoreAge メソッドが 0 以上 100 以下の年齢を対象にしていることを明確にします。
if 文のネストが深くなってしまうので、以下のように書き換えても ok です。
// 指定年齢以上のカラムを取り出すメソッド
List<DataRow> SelectMoreAge( DataTable src, int age )
{
List<DataRow> dest = new List<DataRow>();
// src は null でないこと
if ( src == null ) {
return dest ;
}
// 年齢は 0 以上 100 以下であること
if (!( 0 <= age && age <= 100 )) {
return dest ;
}
// 処理をする
foreach ( DataRow r in src.Rows )
{
int a = (int)r.Item("age");
if ( a >= age ) {
dest.Add( r );
}
}
return dest ;
}
■メソッドの構造を決める
このようなコードを「業務風」に仕上げていくことができます。
メソッド内をブロックに分けて、どのような手続きをしているかをプロジェクトのメンバーで決めておくと、相互にコードに手をいれやすくなります。
// 指定年齢以上のカラムを取り出すメソッド
List<DataRow> SelectMoreAge( DataTable src, int age )
{
/*******************************************/
/* 内部変数 */
/*******************************************/
List<DataRow> dest = new List<DataRow>();
/*******************************************/
/* パラメータチェック */
/*******************************************/
// src は null でないこと
if ( src == null ) {
return dest ;
}
// 年齢は 0 以上 100 以下であること
if (!( 0 <= age && age <= 100 )) {
return dest ;
}
/*******************************************/
/* 内部処理 */
/*******************************************/
// 処理をする
foreach ( DataRow r in src.Rows )
{
int a = (int)r.Item("age");
if ( a >= age ) {
dest.Add( r );
}
}
/*******************************************/
/* 戻り値 */
/*******************************************/
return dest ;
}
このように適度にコメント(飾り罫線)を入れて整形します。
ひとりで書くときは、この手の装飾は必要ないのですが(行数が多くなって、テキストエディタで表示できる処理行が少なくなってしまうので)、複数名で作業する業務コードの場合は別です。
という訳で、安全性を考慮したコードを書く場合には、できるだけ null を返しません。また、例外を返すことも少ないのです(例外を拾わないことにより、アプリケーションエラーになる確率が増えるので)。

ちょっと前にあがったnullという値は必要か、を読んで。
PHPの場合は連想配列を使うことが多いので、例外は0件の配列を
返してやればうまくいくなぁと思ってました。
業務のコードはこれくらいコメント書きますよね。
なんかみんな書かないしネット上のソースもコメントないから
それが普通なのかと思ってしまう。
というかPHPにはphpDocumentorがあるので、それに則って書くべきだし。
変数をどこでも宣言できてしまうので、これも見づらくなる原因だと最近
思います。
CakePHPが制約を設けて秩序を維持しているので、なんでも自由というのも
逆に考えものかなあと。
null は、明示的に「何もない」ことを指すのが良いかと思っています。
例えば、0 個の配列を返す場合は、単に 0 個なのか、何らかの意図があって 0 個なのか、が区別できないときに、あえて null を使うというやり方です。
ただ、フェイルセーフの話で書いたように、エラーのときに null を返すと決めてしまうと、呼び出し側の判別が多くなって、運用時のエラーが増える。なので、0 を返す、ってのが安全なコードの書き方ですね、ということなのです。
・・・とか言う話は、なかなか理解して貰えないんですよね~。なぜだろう?
そうそう、コメントの飾り罫はあまり好きではないけど、業務コードではよくやります。
適度な空行を入れるか、飾り罫を入れるかという感じ。
あと、変数の位置は、本当は使う直前で宣言したほうがいいんだけど、分かりづらいっていう人が多いので、関数の先頭に宣言するかなぁ(私はやらんけど)、というテンプレートを使ってます。