読みやすいコードを書くためにFunc型と匿名メソッドを利用する。

最初に断っておきたいこと


C#3.0の話です。
Func型や匿名メソッド自体の説明はしていません。

まず最初に、私が考える読みやすいコードの要素について断片的に提示します。


1.処理のスコープを最小にする。
2.今、読みたい粒度のコードしか読まなくても良い。


というものがあります。


どれも、いくつかある読みやすいコードの要素の断片でしかありませんが、この1と2は矛盾を孕んでいます。


それでは、どんな矛盾を孕んでいるかサンプルを交えて説明します。


001 public class サンプル1
002 {
003 public void サンプルメソッド()
004 {
005 if (鼻毛抜きの在庫 == 0 && 鼻毛カッターの在庫 == 0)
006 {
007 return;
008 }
009
010 鼻毛処理();
011 }
012 }
013


鼻毛処理のできる道具があれば、鼻毛処理ができる。


という仕様を実現しています。
しかしながら、「鼻毛処理のできる道具があれば」の詳細部分まで同じメソッド内に記述されてしまっています。


そこで、



001 public class サンプル2
002 {
003 public void サンプルメソッド()
004 {
005 if (!鼻毛処理の道具がある())
006 {
007 return;
008 }
009
010 鼻毛処理();
011 }
012
013 private bool 鼻毛処理の道具がある()
014 {
015 if (鼻毛抜きの在庫 > 0)
016 {
017 return true;
018 }
019
020 if (鼻毛カッターの在庫 > 0)
021 {
022 return true;
023 }
024
025 return false;
026 }
027
028 public void その他の処理()
029 {
030 throw new NotImplementedException();
031 }
032
033 }
034



これで、「2.今、読みたい粒度のコードしか読まなくても良い。」という要素が満たされた実装になりました。
しかしメソッドアウトする前は、処理のスコープがメソッド内にしかなかったのに、メソッドアウトした事で処理のスコープがクラス全体に広がってしまいました。
実装したばかりの今なら、まだサンプルメソッド内でしか使われていないとわかりますが、将来このコードを見たときに、サンプルメソッド内でしか使われていないと断定することができません。
これでは、「1.処理のスコープを最小にする」という要素が満たされなくなってしまっています。


そこで、以下のようにFunc型と匿名メソッドを利用してサンプルを修正してみます。


001 public class サンプル3
002 {
003 public void サンプルメソッド()
004 {
005 Func<bool> 鼻毛処理の道具がある =
006 delegate
007 {
008 if (鼻毛抜きの在庫 > 0)
009 {
010 return true;
011 }
012
013 if (鼻毛カッターの在庫 > 0)
014 {
015 return true;
016 }
017
018 return false;
019 };
020
021 if (!鼻毛処理の道具がある())
022 {
023 return ;
024 }
025
026 鼻毛処理();
027 }
028


これで、匿名メソッドという形でメソッドアウトし「2.今、読みたい粒度のコードしか読まなくても良い。」という要素を満たした上に、処理のスコープもメソッド内にとどまりますので、「1.処理のスコープを最小にする。」という要素も満たされます。
「鼻毛処理の道具があれば」という仕様の詳細まで知る必要が無いときは匿名メソッドを読み飛ばせば良いのです。


以上のように「ひとつのメソッドの中でしか利用しないが、粒度が違うためメソッドアウトする。」といったような局面において最小のスコープを維持したままメソッドアウトする道具として匿名メソッドを使う例の紹介でした。

ちなみに、サンプル3をラムダ式で書くと


001 public class サンプル3
002 {
003 public void サンプルメソッド()
004 {
005 Func<bool> 鼻毛処理の道具がある =
006 () =>
007 {
008 if (鼻毛抜きの在庫 > 0)
009 {
010 return true;
011 }
012
013 if (鼻毛カッターの在庫 > 0)
014 {
015 return true;
016 }
017
018 return false;
019 };
020
021 if (!鼻毛処理の道具がある())
022 {
023 return ;
024 }
025
026 鼻毛処理();
027 }
028

となります。