JavaScriptデモ/CanvasBound002 オブジェクト指向プログラミング

 (図形が動くだけの)グラフィック表示のデモです、そして「継承(インヘリタンス)、多態性(ポリモーフィズム)、合成(コンポジション)」のデモでもあります。

このデモは「継承(インヘリタンス)、多態性(ポリモーフィズム)、合成(コンポジション)」を具象化しているので、オブジェクト指向プログラミングの教材として参考になるでしょう。

あくまでもデモなので、手抜きをしています。


下記は実際に動くページです。

https://ano1335-chan.github.io/web/demo/CanvasBound002.htm


参考


集約とコンポジションの例えについて

https://qiita.com/gatapon/items/5e3292f897ab4f817001


>コンポジション

>保持されているものが保持しているものの一部である場合

>この書籍では「車とエンジン」で例えられています。


>集約

>独立して存在できる何かのコレクションがある場合

>こちらは空港と飛行機で例えられています。


その他の要件としては親オブジェクトが消滅する時に、確実に子オブジェクトも消滅するなら合成(コンポジション)と考えて良いです。



オブジェクト指向プログラミングの1番の肝は多態性(ポリモーフィズム)であり、その多態性(ポリモーフィズム)の肝は「共通の機能」(メソッド)を抽出することです(2番目の肝は「集約(アグリゲーション)、合成(コンポジション)」です)。

ちなみに肝となる「共通の機能」が複数 存在する場合もあります。


この場合は「Drawing()、Move()」が肝となる「共通の機能」となりますが、その肝となる「共通の機能」が抽出できるか どうかが、多態性(ポリモーフィズム)を具現化できるか どうかの基準になります。


肝となる「共通の機能」(メソッド)は、その機能ごとに完全にシグネチャが一致していなければなしません、ただし コンストラクタや その他のメソッドは必ずしもシグネチャが一致しなくても良いです。

当然 継承関係も最適になるように考察する必要があります。


JavaScriptは動的型付け言語(変数に何でも入る)なので、静的型付け言語においてのオブジェクト指向の(厳密な)多態性(ポリモーフィズム)とは違いますが、動的型付け言語の「なんちゃって多態性(ポリモーフィズム)」でも「共通の機能」(メソッド)の抽出は必要になります。

下記プログラムは静的型付け言語で多態性(ポリモーフィズム)を具現化しやすいようにクラス設計を配慮しています。


(教材として参考にする場合は)とりあえず動きの部分は作らなくても良いので、図形の表示だけでも作ってみると良いでしょう。

動きの部分を作らなくても良いなら、「Move()」メソッドは不要です。


「継承(インヘリタンス)」で対応できない場合に「集約(アグリゲーション)、合成(コンポジション)、委譲(デリゲーション)」などを使います、オブジェクト内に機能拡張用オブジェクトを作ることを「集約(アグリゲーション)、合成(コンポジション)」と言い、自オブジェクト・メソッド内で「集約(アグリゲーション)、合成(コンポジション)」オブジェクト・メソッドを呼び出している場合は委譲(デリゲーション)と言います。



<html>

<head>

<mefta charset="UTF-8">

</head>

<body>

<canvas id="idCanvas" height="500px" width="500px" style='border: solid thin #000000;' ></canvas><br/>

<script>

  oCanvas = document.getElementById('idCanvas');

  ctx = oCanvas.getContext('2d');

  nCanvasW = oCanvas.width;

  nCanvasH = oCanvas.height;

  

  class Figure {

    x = 0;

    y = 0;

    dx = 0;

    dy = 0;

    bc = null;

    

    constructor(x,y,dx,dy,bc) {

      this.setPosition(x,y);

      this.dx = dx;

      this.dy = dy;

      this.backcolor = bc;

    }

    

    setPosition(x,y) {

      this.x = x;

      this.y = y;

    }

    

    Move( ) {

      let nx,ny;

      nx = this.x+this.dx;

      ny = this.y+this.dy;

      if(nx<0){

        nx = 0;

        this.dx = -this.dx;

      }

      if(nCanvasW<nx){

        nx = nCanvasW;

        this.dx = -this.dx;

      }

      if(ny<0){

        ny = 0;

        this.dy = -this.dy;

      }

      if(nCanvasH<ny){

        ny = nCanvasH;

        this.dy = -this.dy;

      }

      this.setPosition(nx,ny);

    }

    

    Drawing( ) { } // ダミー

  };

  

  class Square extends Figure {

    edge = 0;

    

    constructor(x,y,eg,dx,dy,bc) {

      super(x,y,dx,dy,bc);

      this.edge = eg;

    }

    

    Drawing( ) {

      let half = this.edge/2;

      console.log(half);

      ctx.fillStyle = this.backcolor;

      ctx.fillRect(this.x-half, this.y-half, this.edge, this.edge);

    }

  };

  

  class Ball extends Figure {

    radius = 0;

    

    constructor(x,y,r,dx,dy,bc) {

      super(x,y,dx,dy,bc);

      this.radius = r;

    }

    

    Drawing( ) {

      ctx.beginPath(); // パスの初期化

      ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI);

      ctx.closePath();

      ctx.fillStyle = this.backcolor;

      ctx.fill();

    }

  };

  

  class Pentagram extends Ball {

    constructor(x,y,r,dx,dy,bc) {

      super(x,y,r,dx,dy,bc);

    }

    

    Drawing( ) {

      ctx.beginPath(); // パスの初期化

      this.CreateStar(this.radius);

      ctx.closePath();

      ctx.fillStyle = this.backcolor;

      ctx.fill();

    }


    CreateStar(r){

      // 「Canvasで五芒星を書く」より。

      // https://qiita.com/nasum/items/a59f2ee4df4fd2f24227

      let degree = [-90,-234,-18,-162,-306];

      let cdt = this.CreateCordinate(r,degree[0]);

      ctx.moveTo(this.x+cdt.x,this.y+cdt.y);

      for(let i = 1; i<degree.length; i++){

        cdt = this.CreateCordinate(r,degree[i]);

        ctx.lineTo(this.x+cdt.x,this.y+cdt.y);

      }

    }

    

    CreateCordinate(r,angle){

      let x = r * Math.cos(angle / 180 * Math.PI);

      let y = r * Math.sin(angle / 180 * Math.PI);

      return {

        "x" : x,

        "y" : y

      };

    }

  }

  

  a1oFg = new Array();

  a1oFg.push(new Square(nCanvasW/2,0,100,0,1,"#00f"));

  a1oFg.push(new Ball(nCanvasW,nCanvasH/2,50,-1,0,"#0f0"));

  a1oFg.push(new Ball(0,nCanvasH/2,50,1,1,"#f00"));

  a1oFg.push(new Pentagram(nCanvasW/2,nCanvasH,50,1,-1,"#f0f"));

  

  var oInterval = setInterval(function () { 

    ctx.clearRect(0, 0, nCanvasW, nCanvasH);

    for(let i = 0; i<a1oFg.length; i++){

      a1oFg[i].Move(); 

      a1oFg[i].Drawing(); 

    }

  }, 7);

  

</script>

</body></html>


コメント

このブログの人気の投稿