読みやすいコードを書くために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 

となります。

Share