今回は前回解説した「メソッドはできるだけ早く抜ける」の別のパターンとして,「対象外の時はすぐに抜ける」というお話をしていきたいと思います。
BAD:チェックを多用する
次のようにProduct型のオブジェクトを引数で受けているメソッドがあったとします。このproductのProductNameに対してSubstringを行いたい場合を考えます。Substringをする場合は,必要な文字数が無い場合は例外になってしまうので,例外を出さずに処理しようと思うと,次のように,ProductNameに必要なだけの桁数が存在するかをチェックする必要があります。さらには,それ以前の問題として,productやProductNameもnullの可能性がある場合は,それもチェックが必要です。その場合,必要な条件を満たしているかをチェックするif文を書くと,次のようになります。
//メソッドはできるだけ早く抜ける2 //BAD:チェックを多用する private bool チェックを多用する(Product product) { if (product != null) { if (product.ProductName != null) { if (product.ProductName.Length > 2) { if (product.ProductName.Substring(2, 1) == "A") //① { //BAD:チェックが多くてネストが深い return true; } } } } return false; }
実際にやりたいことは①の3文字目が”A”なのかどうかを判定したいのですが,そこにたどり着くまでに,いくつものif文が必要となり,かなりネストされた状態になります。このように,多くのif文があると,すべてのif文を脳みそに覚えながら読まないといけないし,階層が深くなり,コードが右にずれていき,物理的に読みづらくなります。
GOOD:対象外の時はすぐに抜ける
ネストが深くならないようにする対処法としては,対象外の時はすぐに抜けるということです。都合が悪いケースを排除するという考え方です。
前述したif文の階層が深くなる書き方は,やれる条件を目指してコーディングしていたのですが,やれないときは抜けるということにフォーカスをしてコーディングすると,次のようになります。
//GOOD:対象外の時はすぐに抜ける private bool 対象外の時はすぐに抜ける(Product product) { if (product == null) //① { return false; } if (product.ProductName == null) //② { return false; } if (product.ProductName.Length <= 2) //③ { return false; } if(product.ProductName.Substring(2, 1) == "A") //④ { return true; } return false; }
前回同様④の判定を行いたいのですが,そのために不都合な状態の場合は,さっさとメソッドを抜けるという書き方です。それが①から③のif文です。④のチェックができない要素はすべて排除されるように,不都合な場合はメソッドを抜けています。④のチェックが整っている場合のみ,④までたどり付きます。このようにすれば,トレースする際も,productがnullの場合は,以降のコードを読まなくてもよいので,以前解説した通り,読み手に考慮した記述となります。このように,if文のネストを深くして,必要な状態にたどり着くのではなく,不都合な場合はすぐに抜けるということを意識してみてください。
書き方の変更
ちなみに, Substringの部分は,そのままboolで返却できるので,さらにスマートに,そのまま返却することもできます。
//GOOD:対象外の時はすぐに抜ける private bool 対象外の時はすぐに抜ける(Product product) { if (product == null) { return false; } if (product.ProductName == null) { return false; } if (product.ProductName.Length <= 3) { return false; } return product.ProductName.Substring(2, 1) == "A"; }