質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
MVC

MVC(Model View Controller)は、オブジェクト指向プログラミングにおけるモデル・ビュー・コントローラーの総称であり、ソフトフェア開発で使われている構築パターンとしても呼ばれます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

2123閲覧

ブラックジャックのプログラムにおいて、MVCを参考に入出力処理を分離したい

toromou

総合スコア30

MVC

MVC(Model View Controller)は、オブジェクト指向プログラミングにおけるモデル・ビュー・コントローラーの総称であり、ソフトフェア開発で使われている構築パターンとしても呼ばれます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

1グッド

0クリップ

投稿2019/10/11 02:31

オブジェクト指向の勉強として、JavaScriptでトランプを作成しています。
手始めにブラックジャックを作っていますが、今後別のゲームを作ることも考えて拡張性のある作りにしたいです。
これまで作ったプログラムは以下のようになっています。

javascript

1const $ = require('jquery'); 2 3// カードクラス 4class Card { 5 constructor(suit, rank = 0) { 6 this._suits = ['joker', 'spades', 'hearts', 'diamonds', 'clubs']; 7 8 this.suit = suit; 9 this.rank = rank; 10 this.isFront = false; 11 this.imgSrc = (suit === 0) ? `joker.png` : `${this._suits[suit]}_${this._convertRank(rank)}.png`; 12 } 13 14 // カードを表にする 15 faceUp() { 16 this.isFront = true; 17 } 18 19 // カードを裏にする 20 faceDown() { 21 this.isFront = false; 22 } 23 24 // ランクを適切な表示に変更 25 _convertRank(rank) { 26 const result = 27 rank === 1 ? 'A' : 28 rank === 11 ? 'J' : 29 rank === 12 ? 'Q' : 30 rank === 13 ? 'K' : 31 rank; 32 return result; 33 } 34} 35 36// 手札クラス 37class Hand { 38 constructor(cards) { 39 this.cards = cards; 40 } 41 42 // 受け取ったカードを加える 43 pushCard(card) { 44 this.cards.push(...card); 45 } 46 47 // 指定された枚数のカードを取り出す 48 popCard(count = 1) { 49 let result = []; 50 for (let i = 0; i < count; i++) { 51 result.push(this.cards.pop()); 52 } 53 return result; 54 } 55 56 // 指定したスート・ランクのカードを取り出す 57 selectCard(suit, rank) { 58 let result = this.cards.find((elm) => { 59 return elm.suit === suit && elm.rank === rank; 60 }); 61 return result; 62 } 63 64 // 全てのカードを表にする 65 faceUpAll() { 66 for (let card of this.cards) { 67 card.faceUp(); 68 } 69 } 70 71 // 全てのカードを裏にする 72 faceDownAll() { 73 for (let card of this.cards) { 74 card.faceDown(); 75 } 76 } 77 78 // カードをシャッフルする 79 shuffleCard() { 80 for (let i = this.cards.length - 1; i > 0; i--) { 81 let rand = Math.floor(Math.random() * (i + 1)); 82 [this.cards[i], this.cards[rand]] = [this.cards[rand], this.cards[i]]; 83 } 84 } 85} 86 87// 山札クラス 88class Deck extends Hand { 89 constructor(joker) { 90 let cards = []; 91 92 // ジョーカー以外を生成 93 for (let i = 1; i <= 4; i++) { 94 for (let j = 1; j <= 13; j++) { 95 cards.push(new Card(i, j)); 96 } 97 } 98 99 // 引数で指定した枚数のジョーカーを生成 100 for (let i = 0; i < joker; i++) { 101 cards.push(new Card(0)); 102 } 103 104 super(cards); 105 this.shuffleCard(); 106 } 107} 108 109// プレイヤークラス 110class Player { 111 constructor(name, hand) { 112 this.name = name; 113 this.hand = hand; 114 this._points = this._calculatePoints(); 115 } 116 117 // 合計点数を取得 118 get points() { 119 return this._calculatePoints(); 120 } 121 122 // 合計点数を計算 123 _calculatePoints() { 124 let points = 0; 125 let aceCount = 0; 126 for (let card of this.hand.cards) { 127 points += (() => { 128 switch (card.rank) { 129 case 11: 130 case 12: 131 case 13: 132 return 10; 133 case 1: 134 aceCount++; 135 return 11; 136 default: 137 return card.rank; 138 } 139 })(); 140 } 141 while (points > 21 && aceCount > 0) { 142 aceCount--; 143 points -= 10; 144 } 145 return points; 146 } 147} 148 149// ゲームクラス 150class Game { 151 constructor(...players) { 152 // カード1式 153 this.deck = new Deck(1); 154 155 // ディーラー 156 this.dealer = new Player('ディーラー', new Hand(this.deck.popCard(2))); 157 this.dealer.hand.cards[0].faceUp(); 158 159 // プレイヤー 160 this.players = []; 161 for (let playerName of players) { 162 const player = new Player(playerName, new Hand(this.deck.popCard(2))); 163 player.hand.faceUpAll(); 164 this.players.push(player); 165 } 166 167 // プレイヤー・ディーラー 168 this.everyone = [this.dealer, ...this.players]; 169 170 // 順番 171 this.nowTurn = 0; 172 173 this._outputScreen(); 174 this._modifyScreen(); 175 } 176 177 // ヒット時の処理 178 hit(player) { 179 player.hand.pushCard(this.deck.popCard()); 180 player.hand.faceUpAll(); 181 this._modifyScreen(); 182 } 183 184 // CPUのターン 185 cpuTurn() { 186 setTimeout(() => { 187 const turn = this.nowTurn; 188 if (turn < this.players.length) { 189 if (this.players[turn].points < 18) { 190 this.hit(this.players[turn]); 191 } else { 192 this.nowTurn++; 193 } 194 this.cpuTurn(); 195 } else { 196 this.dealerTurn(); 197 } 198 }, 500); 199 } 200 201 // ディーラーのターン 202 dealerTurn() { 203 setTimeout(() => { 204 this.dealer.hand.faceUpAll(); 205 this._modifyScreen(); 206 if (this.dealer.points < 17) { 207 this.hit(this.dealer); 208 this.dealerTurn(); 209 } else { 210 this._modifyResult(); 211 } 212 }, 500); 213 } 214 215 // コンテンツを挿入 216 _outputScreen() { 217 let content = ''; 218 this.everyone.forEach((player, index) => { 219 const className = index === 0 ? 'dealer' : 'seat' + index; 220 content += `<dl class="${className}">`; 221 content += `<dt class="name">${player.name}</dt>`; 222 content += '<dd class="hand"><ul></ul></dd>'; 223 content += `<dd class="points"></dd>`; 224 content += '<dd class="result"></dd>'; 225 content += '</dl>'; 226 }); 227 $('.game_screen').html(content); 228 $('.seat1 .points').after( 229 '<dd class="button"><button class="hit_button">ヒット</button> ' + 230 '<button class="stand_button">スタンド</button></dd>' 231 ); 232 } 233 234 // コンテンツを更新 235 _modifyScreen() { 236 this.everyone.forEach((player, index) => { 237 const className = index === 0 ? '.dealer' : '.seat' + index; 238 const $target = $(className); 239 let content = ''; 240 for (let card of player.hand.cards) { 241 const imgSrc = card.isFront ? card.imgSrc : 'back_blue.png'; 242 content += `<li><img src="img/${imgSrc}"></li>`; 243 } 244 $('.hand ul', $target).html(content); 245 $('.points', $target).html(`合計点数: ${player.points}`); 246 247 // バスト時の表示 248 if (player.points > 21) { 249 $('.points', $target).append(' BUST').css('color', '#f00'); 250 if (index === 1) $(`.seat1 .button`).remove(); 251 } 252 }); 253 } 254 255 // 勝敗を判定 256 _getResult(player) { 257 const dealer = this.dealer.points; 258 if (player > 21) return 'Lose...'; 259 if (dealer > 21) return 'Win!!'; 260 if (player > dealer) return 'Lose...'; 261 if (player < dealer) return 'Win!!'; 262 return 'Draw Game'; 263 } 264 265 // 結果を表示 266 _modifyResult() { 267 this.players.forEach((player, index) => { 268 $(`.seat${index + 1} .result`).html(this._getResult(player.points)); 269 }); 270 } 271} 272 273// メイン処理 274$(() => { 275 const game = new Game('プレイヤー', 'CPU1', 'CPU2'); 276 277 // デバッグ用にconsoleに山札の一覧をテーブルで表示 278 console.table(game.deck.cards); 279 280 // ヒット 281 $('.hit_button').on('click', () => { 282 game.hit(game.players[0]); 283 if (game.players[0].points > 21) { 284 game.cpuTurn(); 285 } 286 }); 287 288 // スタンド 289 $('.stand_button').on('click', () => { 290 game.cpuTurn(); 291 $(`.seat1 .button`).remove(); 292 }); 293});

現状はGameクラスにあらゆる処理を詰め込んでおり、自分でも訳が分からなくなりつつあります。
そこで、MVCなるものを参考にして入出力処理を切り分けようと考えました。
しかし、サイトによって書いてあることが異なっていたり、参考になるコードが見つからなかったりでいまいち理解できていません。
自分なりの解釈ですが、以下のようなイメージで合っているでしょうか。


Model
各データと、それらを処理するメソッドを持つ

View
modelの参照、データを加工し出力するメソッドを持つ

Controller
model・viewの参照、それらのメソッドを順番に実行するメソッドを持つ

javascript

1// Modelクラス 2class Model { 3 constructor(data1, data2) { 4 this.data1 = data1; 5 this.data2 = data2; 6 } 7 hoge() { 8 this.data1++; 9 } 10 fuga() { 11 this.data2--; 12 } 13} 14 15// Viewクラス 16class View { 17 constructor(model) { 18 this.model = model; 19 } 20 piyo() { 21 console.log(this.model.data1); 22 console.log(this.model.data2); 23 } 24} 25 26// Controllerクラス 27class Controller { 28 constructor(model, view) { 29 this.model = model; 30 this.view = view; 31 } 32 clickBtn() { 33 this.model.hoge(); 34 this.model.fuga(); 35 this.view.piyo(); 36 } 37} 38 39// メイン処理 40$(() => { 41 const model = new Model(3, 6); 42 const view = new View(model); 43 const controller = new Controller(model, view); 44 45 $('.btn').on('click', () => { 46 controller.clickBtn(); 47 }); 48});

また、他にもオブジェクト指向としておかしな部分があればご教授いただけたら幸いです。

dotnetuseryamag👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2019/10/11 02:57

個人的感想なのでこちらに書きますが「オブジェクト指向手始め」としてトランプゲームはかなりハードルが高いように思います。 設計はきちんとできてるのかな?とか、役割分担はどこまで考えてるのかな?とか、もう少し簡単なものからされたほうが良いと思います。 ゲームだとしても本当に簡単な数当てゲームとかtrue/falseがハッキリでるものですね。
toromou

2019/10/11 03:21

正直、作っていて自分にはまだ早いかもとは思っていましたが、 一応動くものはできたのでそのまま勢いでやっている状態です。 確かに、基本的なことを学ぶためにはもっと簡単なもので良いかもしれませんね… ご助言いただきありがとうございます。
m.ts10806

2019/10/11 04:09

はい。「なんとなく動くものが作れた」だけでは他のものを作るときにまた1からやることになります。 オブジェクト指向のメリットも加味すると、なるべく依存性の少ない作りにしておいた方が取り扱いやすくなります。
guest

回答1

0

ベストアンサー

MVC は、表示に関連する部分(V) とロジック(M) を分けるために使うと良いもので、
このプログラムは別に混在してないので、それを当てはめてもあまりメリットが無いような気がします。

オブジェクト指向の勉強として、作ったコードを直していくのであれば、
いったん MVC にはこだわらず、下記あたりの本を読むのが、やりやすいと思います。

  • リファクタリング
  • デザインパターン

投稿2019/10/12 12:59

tanishi_a

総合スコア484

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問