Skip to content

ボウリングプログラムをオブジェクト指向で作成しました。#11

Open
s-tone-gs wants to merge 11 commits intomainfrom
bowling_object
Open

ボウリングプログラムをオブジェクト指向で作成しました。#11
s-tone-gs wants to merge 11 commits intomainfrom
bowling_object

Conversation

@s-tone-gs
Copy link
Copy Markdown
Owner

@s-tone-gs s-tone-gs commented Nov 18, 2025

  • Gameクラス
  • Frameクラス
  • Shotクラス
    を実装しました。

正しい合計点数を返すことができるかをチェックするテストコードも作成しました

確認お願い致します

# ここでfirst_frame, second_frame・・・と宣言したら全体の記述が長くなり、
# むしろ可読性が落ちると判断したので配列で宣言
@frames = framed_scores.map do |score|
# 基本は10フレームだが、10フレーム目でスペアかストライクを出すと11フレーム生成される。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

オブジェクト指向で作るのであれば、現実世界のボウリングをできるだけ忠実に再現したいので11フレーム目を作成するのではなく10フレーム目に3投存在する形にしたいです 🙏

オブジェクト指向を用いる目的の一つに、「現実世界のルールやビジネスロジックを表現する」というものがあり、そこにギャップがある状態だとオブジェクト指向のメリットが薄れてしまうためです。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ボウリングの仕様を正確に表現するために修正&クラスの責務の見直しにて修正しました!他の修正と切り分けることができず、コミットに複数の修正が混ざってしまっています。確認にお手数をおかけします。

オブジェクト指向を用いる目的の一つに、「現実世界のルールやビジネスロジックを表現する」

この点は自分が理解できていない所でした。ありがとうございます!

frame_count.times do |index|
frame = @frames[index]
total_score += frame.total_score
total_score += calc_strike_bounus(index) if frame.strike?
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calc_strike_bounuscalc_spare_bounus もろとも Frame#toral_score に移動させてはどうでしょうか?

今の実装だとGameクラス内の処理で「このフレームはストライクなのかスペアなのか」といったことや、「ストライクの場合は次のフレームを参照して...」といったことを気にしないといけなくなっていますが、その責務は Frame に持たせた方が全体的にコードがスッキリするはずです。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ボウリングの仕様を正確に表現するために修正&クラスの責務の見直し
にて対応しました!コードがすっきりしたように思います。ありがとうございます!

def total_score
total_score = 0
frame_count = 10
frame_count.times do |index|
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enumerable#sum を使うと total_score = 0total_score += といったコードを書かなくて良くなるはずです。利用を検討ください。

Copy link
Copy Markdown
Owner Author

@s-tone-gs s-tone-gs Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private

def to_int_score(string_score)
return 10 if string_score == 'X'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここは三項演算子としても良いですね。
そうすると1行で書けるので to_int_score を定義する意味は薄れる(initizlizeに全部書ける)かもしれません。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shotクラスをより簡潔にするために修正にて対応しました🙇‍♂️


require_relative 'game'

def main
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自動テストが書かれているので安心してリファクタリングできますね 👍

end

def total_score
return [@first_shot.score, @second_shot.score, @third_shot.score].sum unless @third_shot.nil?
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここは次のように書くと少しだけコードの重複を減らせます 👍

[@first_shot, @second_shot, @third_shot].map(&:score).sum

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

記述の重複があったため修正にて修正しました!

after_the_next_frame = @referable_frames[1]
if next_frame.strike?
# 9フレーム目の場合
return next_frame.first_shot.score + next_frame.second_shot.score if after_the_next_frame.nil?
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1投目と2投目のスコアの合計のみを取得するメソッドを Frame に追加すると、ここや total_score の処理を共通化できそうですね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# 9フレーム目の場合
return next_frame.first_shot.score + next_frame.second_shot.score if after_the_next_frame.nil?

next_frame.first_shot.score + after_the_next_frame.first_shot.score
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここも mapsum を使うとコードの重複を減らせますね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

記述の重複があったため修正にて修正しました!

def parse_scores(row_scores)
scores = []
row_scores.split(',').each do |score|
scores.length < 18 && score == 'X' ? scores.push('X', '0') : scores << score
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

18 という数値は 2 * 9 という計算の結果導出された値だと思いますので、そのように記載した方がわかりやすいですね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そしてこの 18 という数値は複数箇所で使われているので、定数にまとめるとミスを減らせますし、定数名によって更に意図がわかりやすくなります。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

共用の数値を定数に修正にて対応しました!

18 という数値は 2 * 9 という計算の結果導出された値だと思いますので、そのように記載した方がわかりやすいですね

コードはできる限り仕様を直接表現した方が良いということですかね?ご指摘の通りだと思いました!

scores.length < 18 && score == 'X' ? scores.push('X', '0') : scores << score
end
tenth_frame_scores = scores[18...scores.count]
framed_scores = scores[0..17].each_slice(2).to_a
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0..17 のところは 0...18 と記載すると、ここも 18 という数値に統一できますね。

Copy link
Copy Markdown
Owner Author

@s-tone-gs s-tone-gs Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

共用の数値を定数に修正にて対応しました。

0..17 のところは 0...18 と記載すると、ここも 18 という数値に統一できますね

今回はtakeメソッドを用いて0..17の記述を無くしました


def parse_scores(row_scores)
scores = []
row_scores.split(',').each do |score|
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここを row_scores.split(',').take(18).each とすると、このブロックの内側の scores.length < 18 の判定や、 framed_scores = scores[0..17].each_slice(2).to_a[0..17] をなくせそうですね。

Copy link
Copy Markdown
Owner Author

@s-tone-gs s-tone-gs Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここを row_scores.split(',').take(18).each とすると

今回、row_scores.split(',')の結果返される配列の要素数に揺れがある(ストライク=Xと表現されていることが要因)ため、takeメソッドでの処理は難しいと判断しました。
現状の処理をより分かりやすく記述する方法が見つけられなかったため、この修正を保留しています。
もし何か良い方法があればご教授願いたいです🙇‍♂️

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど、確かにこの時点ではxが未処理でしたね💦失礼いたしました。現状のままとしましょう。

@frames.each_with_index do |frame, index|
# 9フレーム目であれば
if index.equal?(8)
frame.referable_frames = [@frames[index + 1]]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは予め渡すのではなく Frame#total_score に引数として渡す方針をとってはどうでしょうか?

Frame にとって、スコア計算のとき以外で次のフレームや次の次のフレームが欲しくなるシチュエーションがおそらくないため、インスタンス変数としてもたせる必要はないと考えます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘の通りです。不必要に変数のスコープが広いため要修正だと思いました。
必要以上に変数のスコープが広かったため修正にて修正しました!

total_score = 0
@frames.each_with_index do |frame, index|
# 10フレーム目の場合nil
next_frame = !index.equal?(9) ? @frames[index + 1] : nil
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここは三項演算子を使わずとも、最終フレームの場合は自ずとnilが代入されるはずです。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9フレーム目も10フレーム目も自ずとnilが代入されました👀
条件分岐が必要無いことが分かったのですっきりさせました

必要のない条件分岐であったため修正にて対応しています🙇‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants