美しいプログラムコードとは-2。

「コメントと能書きを少々。」

まずは、コメントを入れたソースコードをご覧いただきたい。
# Ruby のままだけど…。

class Player 
  attr_accessor :games
  attr_reader :name
  def initialize(name) 
    @name = name 
    @games = 0 
  end 
  def won
    @games += 1 
  end 
end 

# ルール定義 (勝利条件定義 [COM:ゲーム数 ADV:ゲーム差])
CONDITIONS = [{:COM=>7,:ADV=>1},
              {:COM=>6,:ADV=>2}] 
# メッセージ
MSG = {:OVER=>"%s wins the set %s - %s",
       :TIE=>"Set is tied at %s",
       :PLAYING=>"%s leads %s - %s"}

def games_as_string(player1, player2)
  # 順位付け
  temp   = [player1, player2].sort{|a, b| a.games <=> b.games} 
  other  = temp.shift 
  leader = temp.shift 

  # ゲーム状態を評価
  status = :PLAYING 
  status = :TIE if leader.games == other.games 
  CONDITIONS.each {|con| 
    if con[:COM] == leader.games &&
       con[:ADV] <= (leader.games - other.games)
      status = :OVER
    end
  } 

  # メッセージオプション編集
  if status == :TIE 
    options = leader.games
  else 
    options = [leader.name, leader.games, other.games]
 end

  # メッセージ出力
  return MSG[status] % options 
end

# テスト

p1 = Player.new("Player1")
p2 = Player.new("Player2")

(0..7).each {|i|
  (0..7).each {|j|
    p1.games = i
    p2.games = j
    
    puts "[#{i}:#{j}] #{games_as_string(p1, p2)}"
  }
}

もう一つも。

# プレイヤー名
NAMES = ["Player1", "Player2"]

# メッセージ
MSG = { 
  :OVER => "%s wins the set %s - %s", 
  :TIE => "Set is tied at %s", 
  :PLAYING => "%s leads %s - %s"
} 

def get_set_score(games1, games2)
  # ルール定義 (ゲーム状態マトリックス)
  statuses = 
  8.times {|i| statuses << Array.new(8, :PLAYING)}
  (0..6).each {|i| 
    statuses[6][i] = :OVER if i < 5 
    statuses[i][6] = :OVER if i < 5 
    statuses[7][i] = :OVER if i >= 5 
    statuses[i][7] = :OVER if i >= 5 
    statuses[i][i] = :TIE 
  } 

  # ゲーム状態を評価
  status = statuses[games1][games2] 

  # メッセージオプション編集
  options = 
  if status == :TIE 
    options = [games1] 
  else
    options << NAMES[games1>games2 ? 0 : 1]
    options << [games1, games2].sort.reverse
  end

  # メッセージ出力
  return MSG[status] % options.flatten
end

# テスト

(0..7).each {|i|
  (0..7).each {|j|
    puts "[#{i}:#{j}] #{get_set_score(i, j)}"
  }
}

上のプログラムコードは、"ルール定義"、"判定"、"メッセージ出力" と、複雑な問題を段階的に解決している。一つ一つの段階が小さな問題領域に絞られているから、おそらく理解は難しくないだろう。しかし、@IT の件の記事は、一塊の条件分岐ブロックですべてを表現している。これは、"異なる問題領域の条件分岐を混ぜている" のだから、場合分けが多くなり、頭の中で整理するのに集中を要することになる。

今回は、参照元に習って、あえて一つの関数で処理するような書き方をしたが、クラス設計としては、段階毎に別々のメッソッドとして定義する方が、より分かりやすく、使いやすくなると思われる。ま、どういうクラス設計をするのが望ましいかは、ここでは大事な話題ではないので、これ以上は触れないけれど。

さて…。

もし、「片方が 5 ゲームを勝利した時点で、相手の勝利ゲームが 0 の場合にコールド勝ちとする。」と云うルールが追加されたとすると、どのプログラムコードが一番楽に対応できるだろうか?

こーゆー仕様変更が容易くできることが、プログラムコードが "見やすかった/分かりやすかった" ことの目安になる。見やすさにこだわる理由は、デバッグがやり易く、変更し易いことなのだから。