オブジェクト指向開発によるゲーム制作

概要

ゲームの種類としてはロールプレイングゲーム,ボードゲーム, シューティングゲームなどが代表的なものである。 これらの基本的なゲームを制作することによって、ゲームの構造を理解し、 さらにプログラム構造を、UMLを使ってモデリングする。 これらの作業を通して、現代的なプログラム開発手法である オブジェクト指向について学ぶ。

プログラムのアーキテクチャ(構造)には、4つの階層(分類)がある。 ひとつはコンピューターと人間のやりとりを扱う部分である。 これはユーザー・インターフェース層といわれるもので、従来からこの部分と 他のプログラム部分は別個に開発されることが推奨されてきた。 次はアプリケーションの論理(ロジック)を扱う部分である。 例えばボードゲームであれば、プレーヤが交互にプレーをするというような プログラムの流れ(制御)を記述する部分である。 この層はアプリケーション層といわれるもので、個々のゲームの制御を扱う。 次の層は、ドメイン層といわれるもので、ゲームであれば そのカテゴリ(ボードゲームやRPG)等に共通する登場人物や道具を オブジェクトとしてモデリングした部分である。 この部分で定義したオブジェクトは、同じカテゴリのゲームならば、他の ゲームでも再利用される可能性は高い。 最後の層は永続層と呼ばれるものである。ゲームを中断して後から再開する 時には、途中までのデータが必要であり、そのデータをゲームが終了しても 覚えておく仕組みの部分である。データベースを利用する場合もあるが、 ファイルに書き出せば十分な場合も多い。これらの機能の異なる層に プログラムを分割することで、保守性がよく再利用できるプログラムとなる. さらに,同時に異なることを考えないという原則が満足される。

ユーザー・インターフェース層

システムとユーザーのデータのやり取りを扱う層

アプリケーション層

その応用プログラムの流れを制御する層

ドメイン層

その応用分野の主要な登場オブジェクトを表現する層

永続層

データを記録する層

ここでオブジェクトという言葉が頻繁に出てきているが、この言葉について 説明する。オブジェクトという言葉は、例えばカードゲームであれば、 手札やテーブルやプレーヤーでありブロック崩しであれば、ブロックや 玉やプレーヤーさらに衝突等のものである。 今の例からも理解できるように、オブジェクトは「もの」という意味の 他にも、衝突等の「出来事」やテーブル等の「場」の意味も持つ。 つまり、現実の世界を表す「もの、出来事、場」をすべてオブジェクトと して扱うわけである。具体的にはオブジェクトには、属性データ群と 関数群が含まれている。関数は、データを覚えておくことが出来ないが、 オブジェクトは内部に覚えている属性データによって、関数の振る舞いを 変えることが出来る。また,データをカプセル化して外のオブジェクトから アクセス出来ないようにすることで,うっかりデータを変更出来ない仕組みを 提供する.

さて、これらの4つの層に分けて開発することと、 プログラムのオブジェクト分割は、大変相性がよいものである。 例えば、手続き型のプログラミングで、4つの層を実現することを 考えてみよう。関数は、データを覚えておくことが出来ないため 必要なデータにアクセスする手段が必要であり、この必要なデータには 4つの層から直接アクセスできることになってしまう. このような構成では、関数部分をアプリケーションごとに再利用する ことは困難になる。データと機能が4つの層で混ざり合ってしまうことが 再利用を難しくしている.また、時間の経過によってデータを修正したり 追加データを加えたくなる.その場合,データ構造を変更すると その影響が様々な関数に伝わるので,保守作業を非常に困難なものになる. また,関数はどこからでも呼び出せるため,4つの層に分けても簡単に 層を横切って呼び出せてしまい分割が曖昧になる. 例えば,ユーザー・インターフェース層を分離しても,ドメイン層から 簡単にアクセス出来るので,自然に各層が交じり合ったプログラムになってしまう.
オブジェクト指向ソフト開発は,習熟するための訓練が必要であり, 上達には経験が必要である.これは水泳の練習と似ていて,実際に手を 動かしながら考えて慣れていくわけである.しかしながら,開発の手順や 考え方は確立されてきており,ここでは筆者が推奨する猫頭式のソフト開発法 を紹介する.この方法は各階層毎に,具体的なオブジェクトを見つけ出し, それらの関係をクラス図にまとめ,主要な機能要求を満たすように プロトタイプを開発し,そこからのフィードバックを得ながら 開発を進めるというものである.

猫頭式ソフト開発法の基本:

具体的なことから抽象的なことを考え,一度に一つのことだけを実行する. プロトタイプのテスト駆動開発から具体的なフィードバックを得て, 目標に向けて柔軟に修正を加える.

具体的な開発手順について

  1. 具体的なゲームの場面からドメインオブジェクトを抽出し、 適当なオブジェクト図を作成する.(具体的なシチュエーションを考える)
  2. オブジェクト図から,ドメインの静的な構造を表すクラス図を作成する. この時点で明らかになったオブジェクトの属性や関数も,クラス図に書き込んでおく
  3. ゲームの流れを考えて、主要な機能をユースケースにまとめる
  4. ゲームの流れを実現する上で、そのゲームの流れを作るアプリケーション・オブジェクトと 入出力を受け持つユーザー・インターフェースオブジェクトを,それぞれ一つづつ定義する.
  5. ユースケースの骨格となるゲームの流れについて,アクティビティ図(フローチャート)に描く.
  6. 個々のドメイン・オブジェクトやコントロール・オブジェクトのプロトタイプを スクリプト言語等で作成する.この時,オブジェクトの属性が明らかになっていると 関数の割り当て(どのクラスに,どのような関数を定義するか)が容易になる. その機能を提供するために,必要な情報を最も多く持っているクラスに,その機能を割り当てればよい.
  7. 個々のオブジェクト(クラス)は,テスト駆動開発等を利用して個別に開発する


    プロトタイプはシステムを理解し,フィードバックを得るために開発するもので, 開発中に発想したことを速やかにフィードバックし,実現可能性をチェックする. 特に,オブジェクトへの機能の割付(責務の割り当て)は,シーケンス図だけでうまく設計するのは 難しく,実装しながら考えてフィードバックを得る方が早い. スクリプト言語を使用する意味は,設計を考えながらプログラミングする関係で やさしい文法で設計に集中できるようにということである. プログラムを実際に作ることで理解が進み,よいアイデア生まれて設計が洗練される. 実際に動くプログラムを作ることで,フィードバックが得られるわけであり, UMLだけでは動かないため,設計や分析のフィードバックを得ることは出来ない.

    機能を満足すれば、最も単純な設計を実現すること

  8. ユーザーインターフェース層を実装し,骨格がうまく機能することを確認する
  9. 以下同様にして,残りの骨格となるユースケースからアプリケーション・オブジェクトや インターフェース・オブジェクトを設計しプロトタイプを作成する. ドメインオブジェクトの機能拡張や,新しいオブジェクトを追加する必要があるかもしれない.
  10. 最後に全体のプロトタイプ設計を評価し,そこからのフィードバックを生かして ターゲット言語による実装を行う.

基本的な考え方は,4層を分割して設計する

具体的な設計の考え方


教育訓練の課題

例題「じゃんけんゲームの開発」

  1. ゲームの場面からオブジェクト「もの,こと,場」の関係を導き出す
  2. 登場するオブジェクト説明
    3人でじゃんけんを行う場面を考えてみる.
    左図は3人のプレーヤーが対戦しているところをいい加減なオブジェクト図に描いている.(クラス図が不明なので正確に描けないし,現時点でその必要は無い)
    まず,人はプレーヤーとして参加するのでオブジェクトとして認識できる. 各プレーヤーはじゃんけんの手を出しているので,じゃんけんの手というオブジェクトが登場する.
    さらに,じゃんけんに参加するには,同じ場所に集まっている必要があり,その場所がじゃんけんの場というオブジェクトになる
    この場がプレーヤーを保持していることになり,場で対戦するわけである.
    対戦という出来事はイベント・オブジェクトとして扱われ,対戦結果を記録する.

      考え方のポイント

    • 手続きの流れは考えない
    • 登場オブジェクトを列挙することに集中する
    • 登場するオブジェクトの関係を考える
    • 実世界の構造に忠実に従う
    • 具体的に考える
    プレイヤー
    名前
    じゃんけんの手
    じゃんけんに参加する人オブジェクト
    名前の属性を持ち
    じゃんけんの手を保持する
    じゃんけんの手:ぐー,ちょき,ぱー
    手の値:ぱー
    勝負の判定()
    じゃんけんの手を表すオブジェクト
    じゃんけんの場
    参加者リスト
    参加()
    勝負の判定()
    じゃんけんに参加する場所を示すオブジェクト
    じゃんけん勝負(イベント)
    日時
    参加者の勝敗
    じゃんけん勝負という出来事(イベント)を記録するオブジェクト
    何回戦かの勝負を行うときに,勝敗の記録を保持する



  3. オブジェクト図からドメインの静的な構造を表すクラス図を作成する
  4. 登場するオブジェクト説明
    クラス図
    数人でじゃんけんする場面を思い浮かべて,じゃんけんに必要な情報を オブジェクトとして抽出し,オブジェクトの関係をオブジェクトの雛型である クラスとして抽象化する.

    オブジェクト図からクラス図に抽象化する.

      考え方のポイント

    • オブジェクトのインスタンスをクラス(雛形)に抽象化する
    • 関係の多重度を記述する


  5. ゲームの流れを考えて、全体の機能をユースケースにまとめる
  6. プレーヤーが名前を登録し,コンピュータとじゃんけん一回勝負を行う

    1. システムは,プレーヤーに名前の入力を要求する
    2. じゃんけん勝負を開始する
    3. システムはプレーヤーにじゃんけんの手を決めさせる
    4. システムはコンピュータにじゃんけんの手を決めさせる
    5. システムは,ルールに従って対戦の勝敗を判定する
    6. 引き分けの場合は,もう一度対戦する
    7. じゃんけんの勝敗を出力する


  7. ゲームの流れを実現する上で、そのゲームに便利なオブジェクトを定義する
  8. 登場するオブジェクト説明
    入出力オブジェクト
    入力()
    出力()
    ユーザーインターフェースを受け持つ境界オブジェクト
    ユーザーはこのオブジェクトと対話する
    ユーザーインターフェース層はひとつ
    じゃんけんコントローラ
    オブジェクトの生成
    制御の流れ
    じゃんけんゲームの流れを記述するオブジェクト
    アプリケーション層のコントロール・オブジェクトもひとつ


  9. ユースケースの骨格となるゲームの流れを,アクティビティ図(フローチャート)に描き、コントロールオブジェクトに記述する. どのクラスにどのような関数を割り当てるか,とても迷うところであるが, オブジェクトの枠組みが決まっているので,制御の流れを実装しながら修正した方が早い
  10. ここでは,フローチャートの代わりに手順を列挙する

    1. じゃんけんコントローラを起動する
    2. コントローラは,じゃんけんの場を生成する
    3. コントローラは,入出力オブジェクト経由でプレーヤーの名前を取得し,プレーヤーオブジェクトを生成する
    4. コントローラは,プレーヤーをじゃんけんの場に参加させる
    5. コントローラは,じゃんけんを開始する
    6. コントローラは,じゃんけんの場からプレーヤー配列を取り出す
    7. コントローラは,各々のプレーヤーの手を決定させる
    8. コントローラは,じゃんけんの場に,プレーヤーの勝敗を判定させる
    9. じゃんけんの場は,じゃんけん勝負オブジェクトを生成し勝敗を記録する
    10. コントローラは,じゃんけん勝負オブジェクトの内容を入出力オブジェクト経由で表示する


  11. 個々のドメイン・オブジェクトやコントロール・オブジェクトの プロトタイプを作成する.ここではJavaScriptで実装する例を示す.
    コンパイル型言語では,文法が厳密なために,設計しながらプログラムを書いていると 文法に気を取られて,思考が妨げられてしまうことがよくある. そのためプロトタイプ開発では,文法が簡単なスクリプト言語の使用を推奨している.
  12. クラス図JavaScriptによる実装例
    じゃんけんの手:ぐー,ちょき,ぱー
    手の内:ぱー
    勝負の判定()
    • 典型的な「もの・オブジェクト」
    • 他のクラスを知らず,他から使われる
    • 再利用しやすい基本クラス
    • 手を知っているので,勝負けの判定ができる
    Hand=function(val){  //じゃんけんの手
      var value=val;     //ぐー,ちょき,ぱー
      this.getValue=function(){//手の値を得る関数
        return value;
      }
      this.setValue=function(val){
        if(val=="ぐー" || val=="ちょき" || val=="ぱー"){
          value=val;
          return true;
        }else{
          return false;
        }
      }
      this.isStrong=function(hand){//2者の勝ち負けの判定
        var yours=hand.getValue(); //相手の手
        var mine=value;            //私の手
        if(mine==yours){           //引分け
          return 0;
        }else if((mine=="ぐー"   && yours=="ちょき") ||
                 (mine=="ぱー"   && yours=="ぐー" )  ||
                 (mine=="ちょき" && yours=="ぱー")){
          return 1;                //勝ち
        }else{
          return -1;               //負け
        }
      }
    }
    プレイヤー
    名前
    じゃんけんの手
    • 名前の属性を持つ
    • じゃんけんの手を保持する
    • じゃんけんの手のアクセッサ
    Player=function(na){  //プレーヤー
      this.name=na;       //名前
      var  hand=new Hand("ぐー");//じゃんけんの手
      this.getHand=function(){
        return hand;
      }
      this.setHand=function(hd){//手を設定する
        hand=hd;
      }
    }
    
    じゃんけんの場
    参加者リスト
    参加者の追加()
    勝負の判定()
    • 場所を示す「場・オブジェクト」
    • 参加プレーヤーを保持する
    • じゃんけんの勝敗(イベント)を保持
    • 勝敗判定をするためのすべての情報を持っている
    • ドメイン層からアプリケーション層は呼び出さない
    • ドメイン層は,アプリケーション層から呼び出される
    • ドメイン層では,ドメイン層のオブジェクトを相互に呼び出す
    JyankenGame=function(){//じゃんけんの場
      var players=[];      //プレーヤー配列
      var records=[];      //じゃんけんの勝敗の記録配列
      this.addPlayer=function(player){
        players.push(player);
      }
      this.getPlayers=function(){
        return players;
      }
      this.judge=function(){//じゃんけんの判定
        var results=[]
        for(var k in players){
          var myHand=players[k].getHand();
          var sum=0; //勝ち負けの和
          for(var m in players){
            var yourHand=players[m].getHand();
            var win=myHand.isStrong(yourHand);
            if(win<0){
              sum=-1
              break;
            }else{
              sum+=win;
            }
          }
          results[players[k].name]=sum;
        }
        records.push(new Record(results));
      }
      this.getResult=function(){//記録を取り出す
        return records.pop();
      }
    }
    
    じゃんけん勝負(イベント)
    日時
    参加者の勝敗配列
    • イベントを表す「こと・オブジェクト」
    • 出来事を記録する
    • イベントは,データベース等に永続化される場合が多い
    Record=function(record){//じゃんけん結果の記録
      this.date=new Date().toString();
      var record=record;
      this.getRecord=function(name){
        var st="勝ち";
        if(record[name]==0){
          st="引分け";
        }else if(record[name]<0){
          st="負け";
        }
        return st;
      }
    }
    
    
    入出力
    (ユーザーインターフェース層)
    出力
    入力
    • アプリケーション層から呼び出される
    • ドメイン層から直接呼び出してはいけない
    InputOutput=function(){//ユーザーインターフェース層
      this.prt=function(st){
        alert(st);
      }
      this.inp=function(st, example){
        return prompt(st, example);
      }
    }
    
    
    じゃんけんコントローラ
    (アプリケーション層)
    初期化()
    じゃんけん開始()
    結果の表示()
    • アプリケーションの流れを記述する
    • ドメイン層を呼び出して,じゃんけんの流れを制御する
    • 入出力クラスを,このクラスが使う
    • このクラスはアプリ固有のものだから,再利用は出来ない
    • この層とドメイン層の分離が,多人数でのn回戦じゃんけんへの拡張を容易にしている
    JyankenCntl=function(){//アプリケーション・コントローラ
      var jyanken=new JyankenGame();
      var io=new InputOutput();
      this.init=function(){
        jyanken.addPlayer(new Player("computer"));
        var na=io.inp("プレーヤーの名前を入力", "太郎");
        jyanken.addPlayer(new Player(na));
        this.start();
      }
      this.start=function(){//じゃんけんの開始
        var players=jyanken.getPlayers();
        var hands=["ぐー","ちょき","ぱー"];
        var no=1;
        for(var k in players){
          if(players[k].name!="computer"){
            no=1*io.inp(players[k].name +"手を入力",1);
          }else{
            no=Math.floor(3*Math.random());
          }
          players[k].setHand(new Hand(hands[no]));
        }
        jyanken.judge();
        var rec=jyanken.getResult();
        this.showResult(rec);
      }
      this.showResult=function(rec){
        var players=jyanken.getPlayers();
        io.prt(rec.date);
        for(var k in players){
          io.prt(players[k].name
                +players[k].getHand().getValue());
          io.prt(players[k].name+"="
                +rec.getRecord(players[k].name));
        }
      }
    }
    


  13. 個々のオブジェクト(クラス)は,テスト駆動開発等を利用して個別に開発する

  14. プロトタイプ開発中に発想したことを速やかにフィードバックする



  15. ユーザーインターフェース層を実装し,骨格がうまく機能することを確認する


  16. 以下同様にして,残りの骨格となるユースケースからアプリケーション・オブジェクトや インターフェース・オブジェクトを設計しプロトタイプを作成する. ドメインオブジェクトの機能拡張や,新しいオブジェクトを追加する必要があるかもしれない.


  17. 最後に全体のプロトタイプ設計を評価し,そこからのフィードバックを生かして ターゲット言語による実装を行う.ここではJavaによる実装の例を示す
    Javaによる実装例の長さが示すように,コンパイル型言語の文法の厳密さのために プログラムが長く複雑になっている.
    プロトタイプ開発では,思考が妨げられない簡単なスクリプト言語を使用し, 最後に機械的にコンパイル言語へ変換することを推奨している. 二度手間のように見えるが,一度に一つのことを着実に実行する方が,はるかに単純で速い.
  18. クラス図Javaによる実装例
    じゃんけんの手:ぐー,ちょき,ぱー
    手の内:ぱー
    勝負の判定()
    • 典型的な「もの・オブジェクト」
    • 他のクラスを知らず,他から使われる
    • 再利用しやすい基本クラス
    • 手を知っているので,勝負けの判定ができる
    class Hand {
      String value;      //ぐー,ちょき,ぱー
    
      Hand(String val){  //じゃんけんの手
        value=val;  //ぐー,ちょき,ぱー
      }
      String getValue(){ //手の値を得る関数
        return value;
      }
      boolean setValue(String val){
        if(val.equals("ぐー") || val.equals("ちょき")
                              || val.equals("ぱー")){
          value=val;
          return true;
        }else{
          return false;
        }
      }
      int isStrong(Hand hand){        //2者の勝ち負けの判定
        String yours=hand.getValue(); //相手の手
        String mine =value;           //私の手
        if(mine.equals(yours)){       //引分け
          return 0;
        }else if((mine.equals("ぐー")   && yours.equals("ちょき")) ||
                 (mine.equals("ぱー")   && yours.equals("ぐー" ))  ||
                 (mine.equals("ちょき") && yours.equals("ぱー"))){
          return 1;                //勝ち
        }else{
          return -1;               //負け
        }
      }
    }
    
    プレイヤー
    名前
    じゃんけんの手
    • 名前の属性を持つ
    • じゃんけんの手を保持する
    • じゃんけんの手のアクセッサ
    class Player{  //プレーヤー
      String name; //名前
      Hand   hand; //じゃんけんの手
    
      Player(String na){
        name=na;
        hand=new Hand("ぐー");
      }
      String getName(){
        return name;
      }
      Hand getHand(){
        return hand;
      }
      void setHand(Hand hd){//手を設定する
        hand=hd;
      }
    }
    
    じゃんけんの場
    参加者リスト
    参加者の追加()
    勝負の判定()
    • 場所を示す「場・オブジェクト」
    • 参加プレーヤーを保持する
    • じゃんけんの勝敗(イベント)を保持
    • 勝敗判定をするためのすべての情報を持っている
    • ドメイン層からアプリケーション層は呼び出さない
    • ドメイン層は,アプリケーション層から呼び出される
    • ドメイン層では,ドメイン層のオブジェクトを相互に呼び出す
    class JyankenGame{             //じゃんけんの場
      Vector players=new Vector(); //プレーヤー配列
      Vector records=new Vector(); //じゃんけんの勝敗の記録配列
    
      void addPlayer(Player p){
        players.addElement((Object)p);
      }
      Vector getPlayers(){
        return players;
      }
      void judge(){  //じゃんけんの判定
        Hashtable results=new Hashtable();
        Enumeration en=players.elements();
        while(en.hasMoreElements()){
          Player p=(Player)en.nextElement();
          Hand myHand=p.getHand();
          int sum=0; //勝負けの和
          Enumeration em=players.elements();
          while(em.hasMoreElements()){
            Hand yourHand=((Player)em.nextElement()).getHand();
            int win=myHand.isStrong(yourHand);
            if(win<0){
              sum=-1;
              break;
            }else{
              sum+=win;
            }
          }
          results.put((Object)p, new Integer(sum));
        }
        this.records.addElement(new Record(results));
      }
      Record getResult(){ //記録を取り出す
        return (Record)this.records.firstElement();
      }
    }
    
    じゃんけん勝負(イベント)
    日時
    参加者の勝敗配列
    • イベントを表す「こと・オブジェクト」
    • 出来事を記録する
    • イベントは,データベース等に永続化される場合が多い
    class Record{   //じゃんけん結果の記録
      String date=new Date().toString();
      Hashtable record;
    
      Record(Hashtable rcd){
        record=rcd;
      }
      Integer getRecord(Player p){
        return (Integer)this.record.get((Object)p);
      }
    }
    
    
    入出力
    (ユーザーインターフェース層)
    出力
    入力
    • アプリケーション層から呼び出される
    • ドメイン層から直接呼び出してはいけない
    class InputOutput{ //ユーザーインターフェース層
      BufferedReader input=new BufferedReader(
                           new InputStreamReader(System.in));
      void prt(String st){
        System.out.println(st);
      }
      String inp(String st){
        String str=null;
        try{
          prt(st);
          str=input.readLine();
        }catch(IOException e){
          prt("IOException : "+e);
        }
        return str;
      }
    }
    
    
    じゃんけんコントローラ
    (アプリケーション層)
    初期化()
    じゃんけん開始()
    結果の表示()
    • アプリケーションの流れを記述する
    • ドメイン層を呼び出して,じゃんけんの流れを制御する
    • 入出力クラスを,このクラスが使う
    • このクラスはアプリ固有のものだから,再利用は出来ない
    • この層とドメイン層の分離が,多人数でのn回戦じゃんけんへの拡張を容易にしている
    import java.applet.*;
    import java.util.*;
    import java.io.*;
    
    
    public class JyankenApplet extends Applet{//アプリケーション・
      public static void main(String[] args){ //コントローラ
        JyankenApplet ap=new JyankenApplet();
        ap.init();
      }
    
      JyankenGame jyanken=new JyankenGame();
      InputOutput io=new InputOutput();
    
      public void init(){//初期化
        jyanken.addPlayer(new Player("computer"));
        String na=this.io.inp("プレーヤーの名前を入力");
        jyanken.addPlayer(new Player(na));
        this.start();
      }
      public void start(){//じゃんけんの開始
        Vector players=this.jyanken.getPlayers();
        String[] hands={"ぐー","ちょき","ぱー"};
        int no=1;
        Enumeration en=players.elements();
        while(en.hasMoreElements()){
          Player p=(Player)en.nextElement();
          if(p.getName()!="computer"){
            no=Integer.parseInt(this.io.inp(p.getName()
                                +"手を入力1, 2, 3"));
          }else{
            no=new Random().nextInt(hands.length-1);
          }
          p.setHand(new Hand(hands[no]));
        }
        jyanken.judge();
        this.showResult(jyanken.getResult());
      }
      void showResult(Record rec){
        io.prt(rec.date);
        Vector players=jyanken.getPlayers();
        Enumeration en=players.elements();
        while(en.hasMoreElements()){
          Player p=(Player)en.nextElement();
          io.prt(p.getName()
                +p.getHand().getValue());
          io.prt(p.getName()+"="
                +rec.getRecord(p).toString());
        }
      }
    }
    

judge=function(h1, h2){
  if(h1==h2) return 0
  if(h1=="ぐー" && h2=="ちょき" ||
     h1=="ちょき" && h2=="ぱー" ||
     h1=="ぱー" &&   h2=="ぐー"){
     return 1
  }else{
     return -1
  }
}
var playerName=prompt("名前を入力してください","太郎");

for(var k=0; k<5; k++){
  var no=1*prompt("プレーヤーの手を入力してください,"
           +"0=ぐー,  1=ちょき, 2=ぱー", 3);
  var hand=["ぐー","ちょき","ぱー"];
  var playerHand=hand[no];
  var computerHand=hand[Math.floor(3*Math.random())];
  if(judge(playerHand, computerHand)==1){
    alert(playerName+" あんたの勝ち");
    break;
  }else if(judge(playerHand, computerHand)==-1){
    alert(playerName+" あんたの負け");
    break;
  }
}
左のプログラムは,二人でじゃんけんの一回勝負をするものである. このように単純な実装をすれば,驚くほど短いプログラムで済む.
じゃんけんのような単純な問題について,現実の複雑な問題に対処するためのオブジェクト指向という プログラミング・スタイルを取ったために,プログラムの長さに大差がついた.
しかし,複雑な問題はわかりにくく教育的ではないし,現実の構造に合わせてプログラムを組み立てる オブジェクト指向は,現実がうまく動いているように安定して機能する. じゃんけんも複数人数で何回戦ものじゃんけん勝負を行うとか,団体戦を行う 等の発展を考えると,プログラムはすぐに複雑になってきて,オブジェクト指向の威力がわかる.

ところで,関数だけでプログラムを作るとプログラムが短くなる原因は,どこからでも 関数が呼び出せるからである.関数名はグローバル変数となっているのである.
オブジェクト指向では,オブジェクトへのポインタを得ないと関数が使えない. このために手間がかかるが,再利用しやすく堅牢なプログラムになる.