オブジェクト劇場へようこそ(オブジェクト指向概論)
オブジェクト指向プログラミングって?
一言でいえば、演劇です。
皆さんはプログラムの世界で、登場人物を動かして、自由に劇を作るのです。
オブジェクト指向プログラミング概論
『オブジェクト指向プログラミング』を、もう少し詳しくご説明します。
実は、前回までの内容は、事実上『命令型プログラミング』でした。
なぜならば、すべての処理や操作を、細かくMainメソッドという部分に書いてもらっていたからです。
典型的なオブジェクト指向では、すべての処理や操作をMainメソッドに書くのはNGです。
そうではなく、以下で述べるように、『プログラム世界の中で独立した登場人物が繰り広げる操作や処理の概略』と、『登場人物のあり方を定義した部分』に、プログラムを分離する必要があります。
オブジェクト指向では、プログラムを二つの部分に分けます。
『登場人物(同士)の動きややり取り』と、『登場人物の定義』です。
プログラムの概略(Mainメソッド) 『会社員』のTaroさんが登場 Taroさんが起きる Taroさんが朝ごはんを食べる Taroさんが仕事をする Taroさんが昼ご飯をためる Taroさんが仕事をする Taroさんが夕ご飯を食べる Taroさんが寝る。
『会社員』というもののありかた(『会社員』クラス) 属性 名前 活動 登場する 起きる 朝ごはんを食べる 仕事をする 昼ご飯を食べる 夜ご飯を食べる 寝る
実際に上の内容を、C#のソースコード風に書いてみると、以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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(); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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を使います。
オブジェクト指向がない世界とある世界で、プログラムの書き方がどのように違うかをお見せします。
オブジェクト指向のない世界
オブジェクト指向のないfizzbuzzです。第13回の練習問題の解答を少し変えただけのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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を、値を変えて3回繰り返すとどうなるでしょうか。
1回目は1から100まで、2回目は1から200まで、3回目は1から100までです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | 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の一覧を作る方法が定義されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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(); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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); } } } |
オブジェクト指向プログラミングの簡単なテンプレート
オブジェクト指向プログラミングを説明するためのいくつかの用語を紹介します。
メインクラスとメインメソッド
登場人物であるオブジェクト同士のやり取りとして、プログラム(の外枠)が記述されています。
具体的な細かい処理は、登場人物クラスや、登場人物の構成要素クラスを見る必要があります。
部品クラス
メインクラスでないすべてのクラス。
さらに二つに分かれます。
コンストラクター
オブジェクトを新しく登場させる特別なメソッドです。
public クラス名(必要な属性) { // 属性の設定 }
コンストラクターはメインクラスで呼び出します。
呼び出しのフォーマットです。
new クラス名();
1 2 3 4 5 6 | // 会社員を新しく登場させるときの方法を定義 public OfficeEmployee( string name) { // 名前(Name)を設定 this .Name = name; } |
1 | var taro = new OfficeEmployee(name: "Taro" ); |
実はすでに今までの連載でコンストラクターは登場していました。
(乱数生成装置を作るコンストラクター、リストを作るコンストラクター、文字列組み立て装置を作るコンストラクター)
それらをまとめてご紹介します。
1 2 3 4 5 6 7 8 | // 乱数を作る存在を登場させるコンストラクター var randomNumberCreator = new Random(); // 整数のリストを登場させるコンストラクター var odds = new List< int >(){1, 3, 5}; // 文字列を生成する存在を登場させるコンストラクター var stringBuilder = new StringBuilder(); |
メソッド
オブジェクトの行う操作や計算、またはその結果できたデータを表します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public void Work() { } public void WakeUp() { } public void FallAsleep() { } public void EatBreakfast() { } public void EatLunch() { } public void EatDinner() { } |
練習問題
問題1. 以下のようにメインクラスとメインメソッドを書いてください
名前空間名: ConsoleFairy (namespace 名前空間名) クラス名: 自由 メインメソッド内の操作: ConsoleFairyクラスの新しいオブジェクトを登場させ、 consoleFairyという名前の変数に代入。
問題2. 以下のクラスを作ってください
名前空間名: ConsoleFairy (namespace 名前空間名) クラス名: ConsoleFairy
まとめ
これでオブジェクト指向プログラミングの
第1歩が踏み出せましたね。
次回は、実習で作った
ConsoleFairyクラスを題材に、
名前空間について学んでいきましょう。
名前空間を学ぶと、プログラミングの整理整頓ができますよ。
ゲーム制作関連のオススメ連載リンク
とっても手軽なゲーム制作体験!
Unityゲーム開発基礎
実際のリリースゲームを題材にしたハンズオンゲーム制作連載
実践unityゲーム開発