メソッドの中の人と外の人

プログラムにはメソッドというものがつきものです。言語によっては「メソッド」ではなく「関数」と呼ばれることもありますが、意味は同じです。

メソッドとは、よく使う処理を再利用できるように分離したもの。特に、入力された値に対して結果となる値を出力する場合は、まさに「関数」と呼ぶべき振る舞いをします。

プログラミングの最初のチュートリアルを終えると、このメソッドというものを自分で定義する機会が増えてきます。

スポンサーリンク

メソッドの中と外は別々の人

割り算を画面に表示するプログラムを考えてみます。

void Main()
{
    int a, b;
    Console.Write("aを入力 : ");
    a = int.Parse(Console.ReadLine());
    Console.Write("bを入力 : ");
    b = int.Parse(Console.ReadLine());
    if (b == 0)
    {
        Console.WriteLine("0で割ることはできません");
        return;
    }
    ShowDivResult(a, b);
}
 
void ShowDivResult(int a, int b)
{
    Console.WriteLine($"{a}/{b}={a/b}");
}

aとbの値を受け取り、割り算の結果をShowDivResultメソッドで表示するというプログラムです。0で割ることはできないので、bに0が入力されたときは「0で割ることはできません」と表示して終了します。

さてここで、MainからShowDivResultメソッドを呼ぶ人と、ShowDivResultメソッドの中に居て誰かから呼ばれるのを待っている人の2人が居るという風に考えてみます。もちろんプログラマーは自分1人ですが、1人2役で考えてみます。

Mainのほうに居るメソッドの外の人は、ShowDivResultの中身を詳しく知りません。わかっているのは、aとbをメソッドに渡せば、何らかの方法で割り算の結果が表示されるということだけです。

一方、ShowDivResultメソッドの中の人は、いつ誰から自分が呼ばれるかはわかりません。でも、aとbを「ほらよっ」と誰かから渡されれば、いつでも正確に割り算の結果を表示する責任があります。

メソッドの中と外は別々の人なんです。そしてその両者を繋ぐのは、aとbという引数だけです。

Mainのほうに居るメソッドの外の人は、bが0になると0で割り算することになってしまう可能性を考慮して、メソッドを呼び出す前に値のチェックをしています。

ではShowDivResultメソッドの中の人はどうでしょうか。中の人は、メソッドの外の人が事前にbが0でないことをチェックしているかどうかを知りません。乱暴な人は、チェックをせずにこのメソッドを呼ぶかもしれません。中の人は、外の人が丁寧な人なのか乱暴な人なのかを知りえないのです。だから、どんな人から呼ばれても対応できるよう、

void ShowDivResult(int a, int b)
{
    if (b == 0)
    {
        Console.WriteLine("エラー");
        return;
    }
    Console.WriteLine($"{a}/{b}={a/b}");
}

と、中の人が責任を持ってチェックすべきです。bが0なら、何らかの方法でエラーを伝えてあげましょう。

このように中の人と外の人で二重に値のチェックをするのは冗長に思えるかもしれません。しかし、このように「メソッドの中の人は、単体で独立して完璧に仕事をこなしてくれる」ということが保証されていれば、外の人はもはやメソッドの内部の詳細を気にする必要無く、単に「ShowDivResultというもの」という単純な認識だけで済むようになります。

それはつまり、1人でプログラムを作っているときも、脳のリソースを最小限に抑えるということにつながります。余計なことを考えなくて済むようになれば、ミスは減り、より多くの別のことを考えられるようになります。

引数で伝える

メソッドの中の人と外の人は別々の人です。お互いのことはよく知りません。

メソッドの外から中に情報を伝えるには、引数を使います。そして、引数以外を使わないようにします。

//引数以外で情報を伝えてしまった例
void Main()
{
    //中略・・・
    GLOBAL.outputLanguage = Language.Japanese;
    ShowDivResult(a, b);
}
 
void ShowDivResult(int a, int b)
{
    if (b == 0)
    {
        Console.WriteLine("Error");
        return;
    }
  if (GLOBAL.outputLanguage == Language.English)
        Console.WriteLine($"{a}/{b}={a/b}");
    else if (GLOBAL.outputLanguage == Language.Japanese)
        Console.WriteLine($"{a}割る{b}は{a/b}です");
}

この例では、なんらかのグローバル変数GLOBAL.outputLanguageを介して、出力を英語にするか日本語にするかを伝えています。こうしてしまうとメソッドの中の人は、GLOBAL.outputLanguageというどこか遠いところにあるものに従って自分の動作を変えなくてはいけないことになり、「それはどこにあるのだろう、他にも似たようなものがあるかもしれない」と、無駄な関心事が増えてしまいます。

これは、以下のように書き換えると、関心事が減らせます。

//引数で情報を伝える例
void Main()
{
    //中略・・・
    ShowDivResult(a, b, Language.Japanese);
}
 
void ShowDivResult(int a, int b, Language lang)
{
    if (b == 0)
    {
        Console.WriteLine("Error");
        return;
    }
  if (lang == Language.English)
        Console.WriteLine($"{a}/{b}={a/b}");
    else if (lang == Language.Japanese)
        Console.WriteLine($"{a}割る{b}は{a/b}です");
}

引数によって、出力を英語にするか日本語にするかを伝えるようにしています。このようにすることを心がけていれば、例えばこのShowDivResultの動作を決定付けるのは引数のaとbとlangの3つしか無いことがわかります。メソッドの中の人はその3つの引数だけを考えればよく、自分の仕事を全うしやすくなります。

柔軟に規則を破ることもまた必要

ここで紹介した規則「メソッドの中と外は別々の人」というのはあくまで、プログラムを書く際に使う脳のリソースを減らし、プログラムの規模が大きくなったときに破綻してしまうことを未然に防ぐ方法です。

規模の小さいプログラムの場合は、逆にこれらの規則が冗長で無駄だと感じることもあると思います。

そこは適材適所。

絶対の規則であるかのように硬く妄信するのではなく、ときには便利さを優先するために敢えて規則を破るという柔軟さもまた、忘れずに持っておきましょう。

スポンサーリンク