Tips

C# 基礎 第14回 オブジェクト劇場へようこそ

オブジェクト劇場へようこそ(オブジェクト指向概論)

オブジェクト指向プログラミングって?
一言でいえば、演劇です。
皆さんはプログラムの世界で、登場人物を動かして、自由に劇を作るのです。

本日の内容
  1. オブジェクト指向プログラミング概論
  2. オブジェクト指向が必要な理由
  3. オブジェクト指向プログラミングの簡単なテンプレート
  4. 練習問題
  5. まとめ

オブジェクト指向プログラミング概論

『オブジェクト指向プログラミング』を、もう少し詳しくご説明します。

実は、前回までの内容は、事実上『命令型プログラミング』でした。
なぜならば、すべての処理や操作を、細かくMainメソッドという部分に書いてもらっていたからです。

典型的なオブジェクト指向では、すべての処理や操作をMainメソッドに書くのはNGです。
そうではなく、以下で述べるように、『プログラム世界の中で独立した登場人物が繰り広げる操作や処理の概略』と、『登場人物のあり方を定義した部分』に、プログラムを分離する必要があります。

オブジェクト指向の概略図のフォーマット

オブジェクト指向では、プログラムを二つの部分に分けます。
『登場人物(同士)の動きややり取り』と、『登場人物の定義』です。

登場人物同士の動きややり取り
プログラムの概略(Mainメソッド)
    『会社員』のTaroさんが登場
    Taroさんが起きる
    Taroさんが朝ごはんを食べる
    Taroさんが仕事をする
    Taroさんが昼ご飯をためる
    Taroさんが仕事をする
    Taroさんが夕ご飯を食べる
    Taroさんが寝る。
登場人物の定義
『会社員』というもののありかた(『会社員』クラス)
    属性
        名前
    活動
        登場する        
        起きる     
        朝ごはんを食べる        
        仕事をする
        昼ご飯を食べる     
        夜ご飯を食べる     
        寝る
オブジェクト指向のプログラミング例のソースコード

実際に上の内容を、C#のソースコード風に書いてみると、以下のようになります。

何が行われているか(MainクラスとMainメソッド)
namespace ExampleOfObjectOrientedProgramming
{
    // Mainメソッドのあるクラスの名前は、デフォルトではMainClassかProgramです。
	class MainClass
	{
        // Mainクラスです。ここにプログラムの概略が、登場人物のやり取りとして記載されます。
		static void Main(string[] args)
		{
			var taro = new OfficeEmployee (name: "Taro");

			taro.WakeUp();
			taro.EatBreakfast();
			taro.Work();
			taro.EatLunch();
			taro.Work();
			taro.EatDinner();
			taro.FallAsleep();
		}
	}
}
会社員を仮に定義したもの(OfficeEmployeeクラス)
namespace ExampleOfObjectOrientedProgramming
{
    class OfficeEmployee
    {
        // 会社員の属性『Name』を定義
        public Name {get;}

        // 会社員を新しく登場させるときの方法を定義
        public OfficeEmployee(string name)
        {
            this.Name = name;
        }

        public void Work()
        {
        }

        public void WakeUp()
        {
        }

        public void FallAsleep()
        {
        }

        public void EatBreakfast()
        {
        }

        public void EatLunch()
        {
        }

        public void EatDinner()
        {
        }
    }
}

(本日の内容一覧に戻る方はこちら)

なぜオブジェクト指向が必要か

オブジェクト指向プログラミングがなぜ必要なのでしょうか

直接コンピューターさんに指示せずに、
わざわざプログラム中に登場人物を設定し、行動してもらうのはナンセンスではないでしょうか。

C#ではオブジェクト指向が必須だと、第1回で説明しました。その時に述べたことを再掲します。

オブジェクト指向が義務のC#は、小さなプログラムを書くときには非効率に見えます。
しかし、大きなプログラムを書くときは有利さが出てきます。

実例として、前回の練習問題で出たFizzBuzzを使います。
オブジェクト指向がない世界とある世界で、プログラムの書き方がどのように違うかをお見せします。

  1. オブジェクト指向のない世界
  2. オブジェクト指向のある世界

オブジェクト指向のない世界

オブジェクト指向のないFizzBuzzその1

オブジェクト指向のないfizzbuzzです。第13回の練習問題の解答を少し変えただけのものです。

using System;

namespace ProceduralFizzBuzz
{
    class MainClass
    {
        static void Main(string[] args)
        {
            for (var futureFizzBuzz = 1; futureFizzBuzz <= 100; futureFizzBuzz++)
            {
                if (futureFizzBuzz % 15 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => FizzBuzz");
                }
                else if (futureFizzBuzz % 3 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Fizz");
                }
                else if (futureFizzBuzz % 5 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Buzz");
                }
                else
                {
                    Console.WriteLine($"{futureFizzBuzz} => {futureFizzBuzz}");
                }
            }
        }
    }
}
オブジェクト指向のないFizzBuzzその2: いっぱい繰り返そう

オブジェクト指向のないfizzbuzzを、値を変えて3回繰り返すとどうなるでしょうか。
1回目は1から100まで、2回目は1から200まで、3回目は1から100までです。

using System;

namespace ProceduralFizzBuzz
{
    class MainClass
    {
        static void Main(string[] args)
        {
            // 1から100まで
            for (var futureFizzBuzz = 1; futureFizzBuzz <= 100; futureFizzBuzz++)
            {
                if (futureFizzBuzz % 15 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => FizzBuzz");
                }
                else if (futureFizzBuzz % 3 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Fizz");
                }
                else if (futureFizzBuzz % 5 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Buzz");
                }
                else
                {
                    Console.WriteLine($"{futureFizzBuzz} => {futureFizzBuzz}");
                }
            }

            // 1から200まで
            for (var futureFizzBuzz = 1; futureFizzBuzz <= 200; futureFizzBuzz++)
            {
                if (futureFizzBuzz % 15 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => FizzBuzz");
                }
                else if (futureFizzBuzz % 3 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Fizz");
                }
                else if (futureFizzBuzz % 5 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Buzz");
                }
                else
                {
                    Console.WriteLine($"{futureFizzBuzz} => {futureFizzBuzz}");
                }
            }

            // 1から1000まで
            for (var futureFizzBuzz = 1; futureFizzBuzz <= 1000; futureFizzBuzz++)
            {
                if (futureFizzBuzz % 15 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => FizzBuzz");
                }
                else if (futureFizzBuzz % 3 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Fizz");
                }
                else if (futureFizzBuzz % 5 == 0)
                {
                    Console.WriteLine($"{futureFizzBuzz} => Buzz");
                }
                else
                {
                    Console.WriteLine($"{futureFizzBuzz} => {futureFizzBuzz}");
                }
            }
        }
    }
}

オブジェクト指向のある世界

オブジェクト指向を使って、上のコードを書き直します。
メインメソッドの中身が劇的に減りましたね。
代わりに、FizzBuzzesというクラスが定義されて、
ここに、FizzBuzzの一覧を作る方法が定義されています。

オブジェクト指向のあるFizzBuzz
using System;

namespace ObjectOrientedFizzBuzz
{
    class MainClass
    {
        static void Main(string[] args)
        {
            var fizzBuzzes_1_100 = new FizzBuzzes(minInteger: 1, maxInteger: 100);
            fizzBuzzes_1_100.ToConsole();

            var fizzBuzzes_1_200 = new FizzBuzzes(minInteger: 1, maxInteger: 200);
            fizzBuzzes_1_200.ToConsole();

            var fizzBuzzes_1_1000 = new FizzBuzzes(minInteger: 1, maxInteger: 1000);
            fizzBuzzes_1_1000.ToConsole();
        }
    }
}
FizzBuzzesクラス
using System;
using System.Text;

namespace ObjectOrientedFizzBuzz
{
    public class FizzBuzzes
    {
    // 綺麗にコマンドライン画面に表示するための改行付きの文字列
        private string MultiLinedDisplay { get; }

        public FizzBuzzes(int minInteger, int maxInteger)
        {
            /* 
             コンソール画面に表示するためのFizzBuzzの生成
             multiLinedDisplayBuilderさんが、コンソール画面に表示するFizzBuzzを組み立てる
             multiLinedDisplayBuilderさんは、new StringBuilder()。
       つまり、新しく登場した(new)文字列(String)を作る人(Builder)
            */
            var multiLinedDisplayBuilder = new StringBuilder();
            for (
                var futureFizzBuzz = minInteger;
                futureFizzBuzz <= maxInteger;
                futureFizzBuzz++
            )
            {
                if (futureFizzBuzz % 15 == 0)
                {
                    //\nとは改行を意味する特殊文字
                    multiLinedDisplayBuilder.Append($"{futureFizzBuzz} => FizzBuzz\n");
                }
                else if (futureFizzBuzz % 3 == 0)
                {
                    //\nとは改行を意味する特殊文字
                    multiLinedDisplayBuilder.Append($"{futureFizzBuzz} => Fizz\n");
                }
                else if (futureFizzBuzz % 5 == 0)
                {
                    //\nとは改行を意味する特殊文字
                    multiLinedDisplayBuilder.Append($"{futureFizzBuzz} => Buzz\n");
                }
                else
                {
                    //\nとは改行を意味する特殊文字
                    multiLinedDisplayBuilder.Append($"{futureFizzBuzz} => {futureFizzBuzz}\n");
                }
            }
            
            // コンソール画面に出力する形のセット完了
            this.MultiLinedDisplay = multiLinedDisplayBuilder.ToString();
        }

        public void ToConsole()
        {
            Console.WriteLine(this.MultiLinedDisplay);
        }
    }
}

(本日の内容一覧に戻る方はこちら)

オブジェクト指向プログラミングの簡単なテンプレート

オブジェクト指向プログラミングを説明するためのいくつかの用語を紹介します。

メインクラスとメインメソッド

登場人物であるオブジェクト同士のやり取りとして、プログラム(の外枠)が記述されています。
具体的な細かい処理は、登場人物クラスや、登場人物の構成要素クラスを見る必要があります。

部品クラス

メインクラスでないすべてのクラス。
さらに二つに分かれます。

  • 登場人物クラス
  • メインクラスに直接登場するオブジェクトを定義するクラスです。
    OfficeEmployeeクラスも、登場人物クラスです。

  • 登場人物の構成要素クラス
  • 登場人物クラスの持っている属性のうち、基本的なデータ型
    (文字列整数小数真偽値コンテナ)
    ではうまく表せないものを定義するクラスです。

    複雑な登場人物クラスを作るときに必要ですが、この連載では使いません。

コンストラクター

オブジェクトを新しく登場させる特別なメソッドです。

コンストラクターの定義フォーマット
public クラス名(必要な属性)
{
    // 属性の設定
}

コンストラクターはメインクラスで呼び出します。
呼び出しのフォーマットです。

コンストラクターの呼び出しフォーマット
new クラス名();
OfficeEmployeeクラスのコンストラクターの定義
        // 会社員を新しく登場させるときの方法を定義
        public OfficeEmployee(string name)
        {
       // 名前(Name)を設定
            this.Name = name;
        }
var taro = new OfficeEmployee(name:"Taro");
今までに出てきたコンストラクターの実例

実はすでに今までの連載でコンストラクターは登場していました。
乱数生成装置を作るコンストラクターリストを作るコンストラクター文字列組み立て装置を作るコンストラクター
それらをまとめてご紹介します。

// 乱数を作る存在を登場させるコンストラクター
var randomNumberCreator = new Random();

// 整数のリストを登場させるコンストラクター
var odds = new List<int>(){1, 3, 5};

// 文字列を生成する存在を登場させるコンストラクター
var stringBuilder = new StringBuilder();

プロパティ

オブジェクトの属性であるデータを表します。

OfficeEmployeeクラスでのプロパティ定義
        // 会社員の属性『Name』を定義
        public Name {get;}

メソッド

オブジェクトの行う操作や計算、またはその結果できたデータを表します。

OfficeEmployeeクラスでのメソッド定義
        public void Work()
        {
        }

        public void WakeUp()
        {
        }

        public void FallAsleep()
        {
        }

        public void EatBreakfast()
        {
        }

        public void EatLunch()
        {
        }

        public void EatDinner()
        {
        }

(本日の内容一覧に戻る方はこちら)

練習問題

問題1. 以下のようにメインクラスとメインメソッドを書いてください

名前空間名: ConsoleFairy (namespace 名前空間名)
クラス名: 自由
メインメソッド内の操作: 
ConsoleFairyクラスの新しいオブジェクトを登場させ、
consoleFairyという名前の変数に代入。
解答例
[csharp] namespace ConsoleFairy
{
public class MainClass
{
static void Main(string[] args)
{
var consoleFairy = new ConsoleFairy();
}
}
}
[/csharp]

問題2. 以下のクラスを作ってください

名前空間名: ConsoleFairy (namespace 名前空間名)
クラス名: ConsoleFairy
解答例
[csharp] namespace ConsoleFairy
{
public class ConsoleFairy
{
}
}
[/csharp]

(本日の内容一覧に戻る方はこちら)

まとめ

これでオブジェクト指向プログラミングの
第1歩が踏み出せましたね。
次回は、実習で作った
ConsoleFairyクラスを題材に、
名前空間について学んでいきましょう。
名前空間を学ぶと、プログラミングの整理整頓ができますよ。

(本日の内容一覧に戻る方はこちら)

(最初から読み直したい方はこちら)

ゲーム制作関連のオススメ連載リンク

とっても手軽なゲーム制作体験!
Unityゲーム開発基礎

実際のリリースゲームを題材にしたハンズオンゲーム制作連載
実践unityゲーム開発

Recent News

Recent Tips

Tag Search