C++ や C# では、プログラミングの根底にオブジェクト指向という考え方があります。これを意識できているか否かで、プログラム全体の見通しがかなり変わります。今回はこのオブジェクト指向という考え方を解説してみます。


  • まずは構造化プログラミング
ここで仮に自動車を走らせるプログラムを組むとします。クルマは動力として、加速、減速、停止という状態があります。また移動方向として前方を0度とすると、左右に移動する方向があったりします。

これをそのままプログラムの関数で表すと、だいたい次のような感じになるかと思います。

void CarSpeed(ulong carID, float spd); void CarStop(ulong carID); void CarAxel(ulong carID, float scc, float time); void CarDir(ulong carID, float dir);

これはこれで問題ないのでしょうか。いいえ、大変な問題があります。これ、全て動力や旋回性能が同じです。性能の違うクルマを用意しようと思ったら、それぞれ個別に動作する関数を用意するか、引数に性能情報を与える必要があります。これはバグを生みやすいですし、また、パッと見も分かりにくいです。

この動作や構造に応じてプログラムを組み立てたのが、構造化プログラミングなのです。古き良き時代のプログラミングスタイルですね。


  • 最初にオブジェクト分解する
オブジェクトとは、無理矢理日本語に直すとモノと翻訳できます。私は部品だと思っています。そう、オブジェクト指向とは、対象物をモノに見立てて、部品単位に分解して構成する考え方を指します。

先ほどと同様にクルマで考えてみます。クルマは走るためにタイヤがあります。このタイヤは前輪と後輪があります。ここでは後輪駆動のクルマを意識してみます。前輪はハンドル切れ角で向きが変わります。ハンドル切れ角とタイヤの切れ角は同一ではないです。後輪はエンジンからの回転数で回転数が変わります。エンジン回転数とタイヤの回転数は同一ではなく、途中にギヤボックスを挟みます。これらはシャーシに乗っかっています。ボディも乗っかっています。まとめます。
  1. クルマはシャーシとボディから出来ている。
  2. シャーシはエンジンとギヤボックスと前輪タイヤ2本と後輪タイヤ2本を持っている。
  3. ギヤボックスはエンジン回転数を受け取りタイヤの回転数を出力する。
…なんか忘れていますね。そう入力部分です。ハンドルアクセルブレーキも必要です。これ、どこにあるかと言えば操縦席です。操縦席もシャーシに取り付けます。これらに名前を付けましょうか。

CCar 自動車
CBody ボディ(見た目とか空力とか)
CFrame シャーシ
CCockpit 操縦席(ハンドル、アクセル、ブレーキ)
CEngine エンジン
CGearbox 変速機
CTire タイヤ

今、何をやっているのかって?オブジェクト分解です。ふざけているように感じるかもしれませんが、これがオブジェクト指向には必要な考え方なのです。

※ 会社にあると便利シリーズです。自宅だとタンスに仕舞いますよね?…えっ!?

  • クラスはオブジェクトの設計図
さて、もう少し具体的に記述していきます。なるべく細かい部品から考えていきます。末端の部品はタイヤですね。

public class CTire { public float Rpm; // 回転数 public float Angle; // タイヤの取り付け角度 }

タイヤに最低限必要な構成要素は回転数とシャーシからの角度としました(異論はあると思います)。この要素の事をプロパティと呼びます。プロパティをどう決めるかはプログラマが必要に応じて、となります。例えばここに取り付け軸を用意して、前輪か後輪かを振り分けるのも良いかもしれません。

次に変速機(ギヤボックスの定義です)

public class CGearbox { public enum EGear { Low, Slow, Cruise, Fast, Top } public EGear Gear; // ギヤの状態 public float InputRpg; // 入力された回転数 public float OutputPrm; // 出力される回転数 }

ここで悩むのが現実との違いです。実際にはギヤを入れ替えると、エンジン回転数は一旦落ちます。その後、ギヤの構成によって加速が決まります。この複雑な動きを全てシミュレートするのも良いと思いますが、私はここで現実とは違う選択をします。それは、エンジン回転数はアクセルに応じて瞬間的に変動するが、ギヤが加速と減速を受け持つという考え方です。そのため、出力される回転数は、入力された回転数とギヤ比から導き出された値とすぐには一致しません。ギヤの構成で決まる加減速によって、徐々に出力回転数が修正されていくとして設計しました。

この辺りの考え方はプログラマそれぞれです。私の考え方で重要なのは、その処理が全てギヤボックスの中で行われる事です。入力された値はエンジン回転数とギヤです。そこから自動的にタイヤの回転数が得られるような作り。これこそがオブジェクト指向であり、カプセル化というプログラミング手法なのです。

残りもざっくり定義してしまいます。

public class CEngine { publib float OutputPrm; // エンジン出力回転数 }

エンジンはシンプルに回転数の出力だけとしました、入力はアクセルペダルの開けた率とします。0% で停止、100% でべた踏みです。このエンジンから得られた出力値(回転数)をギヤボックスの入力値とします。

public class CCockpit { public float Handle; // ハンドル切れ角 public float Axel; // アクセル踏み込み量 public float Brake; // ブレーキ踏み込み量 }

運転席では実際の操作に近いプロパティを用意しました。これらの値をエンジンや前輪の切れ角に送って、結果を得るようにします。

public class CFrame { public CCockpit Driver; // 運転席 protected CEngine Engine; // エンジン protected CGearbox Shift; // シフトレバー/変速機 protected CTire Tire[4]; // タイヤ4本 }

シャーシはボディ以外の全てを持っています。
※ あれ、ボディもシャーシの上に乗っかっているような…

public class CCar { protected CFrame; // シャーシ protected CBody Body; // ボディ }

クルマはボディとシャーシを持っています。…うーん、こうして実際にクラス化してみると、今回はシャーシは不要だったですね(汗

オブジェクト指向に不慣れな人からすると、ここまでの説明は何をやっとんじゃと言う説明です。ですが、慣れると大変便利です。加速性能を変更したければ、ギヤボックスを入れ替えるだけで済みます。それ以外の使い方は一切変わりません。グリップとか旋回性能とか変えたければ、タイヤを変更すれば事足ります。

今回はこの先の具体的な説明はしませんが、理解して欲しかったのは、オブジェクト指向に慣れるとプログラマはラクが出来ると言う事なんです。また、あとから見ても何をしてるか理解しやすいはずです。過去の自分は他人かもしれませんが、オブジェクト指向に徹したプログラミングスタイルであれば、内容を思い出すのも比較的簡単だと思います。是非身につけて頂きたいですねー


  • 考えてみよう!
さて、今回は皆さんに課題をば。但し、私から答えの提示はありません。なんか出来たら、Twitterとかで、#オブジェクト指向とタグ付けて公開してみてください。という事でオブジェクト分解するのは、誰でも知ってるもしかしたら世界一有名なゲーム、パックマンです。なにをどうオブジェクト指向して分解するのか。分かる人は継承とか好きに使ってみて貰ってかまいません。
main_logo
ゲームプレイ
PAC-MAN™ ©1980 1984 BANDAI NAMCO Entertainment Inc

ヒントはこちらのページにあります。レッツらチャレンジ!
※ お時間あるときにでもどうぞー
※ 妻がコレを買ってきて使っています。確かにラクですー