コードリーディングから学ぶ!(React.js公式チュートリアル編)


学習工程

  1. 先に完成プログラムのコードリーディング(code): 気づき
  2. プログラム写経(気づき,疑問点): 理解
  3. プログラム改変(code, 理解): new_code の順で行います。 コードリーディング(以下、CR)では、コードを読んで不明な点を 「疑問点」として。 また、普段実装している時に自分が困りやすいところを実装している箇所があれば「気づき」としてメモ(結果返答配列に追加)してその結果をCR の結果として返します。

プログラミング写経では、CRで得た「疑問点」や「気づき」を写経しながら解決していきます。詳しくは、次稿で説明したいと思います!

プログラム改変では、プログラミング写経で得た「理解」から現在のプログラムを改変(改造)していきます。詳しくは、次次稿で説明したいと思います!

本稿では、「1.コードリーディング」を行います!

前後関係

  • 背景 React.js(以下、React)を使ったアプリを作ってみたく、プログラミング勉強兼ねてReact公式ホームページに掲載されているチュートリアルを敢えてコードリーディング(以下、CR)しました。
  • Reactチュートリアルを選んだ理由 React公式チュートリアルは、順を追ってアプリを作成しつつ進めると、学びを得ることができます。 しかし、私は順を追って学習するのが苦手で、先に完成品を眺めたり分析をした後解説を読んだ方が、頭への入りが幾分マシなのでこの方法をとっています。 ## 1. コードリーディング

取り組み方

‘ソースコードを読むための技術’を参考に取り組み方を考える。

  1. 対象プログラムを動かして機能の洗い出し
  2. 略語の調査
  3. データ構造を考える
  4. クラス・関数の関係を把握する(クラス図・コールグラフ作成) 終了条件(気づき>0 && 疑問点>0)でなければ、1からやり直し。

CR後の成果物

気づき(=既にある疑問に対しての答え)、
疑問点(=CRしたことで発生した疑問)、
改善点(=既存の知識での改良)

なぜこの取り組み方法なのか

私は、
製品=要求機能+クラス・関数+データ
が成り立つと考えています。
ですので、CRは実際に要求仕様を実装に落とし込むまでの手順の逆をすればいいと考えています。
例)実装まで
1. 実装機能を考える
2. コードの大まかな流れをかく
3. クラス図を作成する(クラスはプログラム全体の地図)
4. DFDを作成する(データについて考える)
5. 関数実装・単体テスト
6. 関数統合して実装・結合テスト
7. 完成、動かして受け入れテスト(機能を確かめている)

これを逆から読むと今回の取り組み方に近い内容になっています。

想定読み方

完全初心者〜初心者の方

想定読者です。
プログラミング教室に学びに来た同期のまとめ資料を見る感覚でご覧ください。
内容を学ぶというより、CRを行う流れを「こういうやり方でやる奴もいるのかー」程度に参考にしていただけると幸いです。
もし、筆者のやり方を気に入っていただけましたら、一緒に順をおってCRやっていただけると、我非常喜!(本当は、「我很高兴(とっても嬉しい)」です笑)

初心者〜中級者の方

「あ、未経験の子ってこういう考え方するのかー。」と、暖かい目で厳しくしていただけると幸いです。

中級者〜上級者の方

ご指導、ご鞭撻のほどよろしくお願いいたします…!

CR

対象プログラム

See the Pen MWJMjrJ by kinoko dake (@kinoko_dake) on CodePen.

1. 対象プログラムを動かして機能の洗い出し

機能

表示に関わる機能

  1. マス目をクリックしたら、XやOが表示される
  2. 「Next player:X」と、現在の手番の状態を表示
  3. 「1.Go to game# 2」と、履歴ボタンをクリックすると、それに紐付いている履歴にジャンプする。 ###### 見た目でわからない機能
  4. ゲーム勝敗判定

2. 略語の調査

特になし!
React公式の初心者向けラーニグコードなため、難しい略語がないのかも。

3. データ構造

// Game/constructor
    this.state = {
      history:[
        {
          squares: Array(9).fill(null)
        }
      ],
      stepNumber: 0,
      xIsNext: true
    };

Gameクラスで全てのコンポーネントデータと状態を管理している。
ゆえに、この箇所が対象プログラムの全てのデータである。
ハッシュにhistory(履歴)、st epNumber(手数)、xIsNext(どちらの手番か)を保持している。
historyは、盤上データを手数順に配列として管理している。
history:{squares:[Array(9)]}

図1

図2
図1の状態の実際のhistoryです。
[疑問点]historyは多次元配列ではダメなのか。

4.クラス・関数の関係を把握する

クラス図は、プログラムの全体像を俯瞰しているイメージ。
関数のコールグラフ(的なの)はプログラムの部品を詳しく見るイメージ。

クラス図

ー 各クラスごとのコールグラフ

Gameクラス内の関数同士関係

handleClickは、Boardがクリックされた時に呼び出されるイベントハンドラ。
[気になったコード]

// handleClick関数
// 気になったコード 後で実装しながら確認

//疑問1
// current.squares[0]ではダメ?
const squares = current.squares.slice();// 59行目

//疑問2
// history.concat!? pushじゃだめ?
this.setState({
  history: history.concat({
    squares: squares
  }),
})

jumpToは、履歴ボタンを押したときにジャンプする履歴に状態を更新します。
calculateWinnerは、現在の盤状態から勝敗を計算します。

// 気になったコード

//疑問3
// な、なんぞ!? 頭が、、頭が痛い...
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
  ];

//疑問4
// この関数内、大学数学講義室の匂いがする..
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }

// 気づき1
//三項演算子のインデント
 const desc = move ?
    'Go to move #' + move :
    'Go to game start';
Boardクラス内の関数同士関係

renderSquareは、外部Square関数を呼び出し9x9の盤のDOMオブジェクトを返す。
BoardクラスのrenderSquare関数は、外部実行ファイルのSquare関数を呼び出しています。

気づき、疑問点まとめ

気づき1:三項演算子のインデント
疑問点1:current.squares.slice()
疑問点2: history.concat
疑問点3:calculateWinner関数内ののマジカル多次元配列
疑問点4:calculateWinner関数内のマジカル条件

終了

CRはこれにておしまいです!
私は、CRにあまり時間をかけずに引っかかった箇所だけをメモしておき、
のちの写経にて問題解決をする方法をとっています。
実際、このチュートリアルはコード量も少なく比較的わかりやすい関数呼び出しを行なっているため、解析ツールも必要ありませんでした。
10分ほどで終わったと思います。
(しかし、この初投稿記事を書くのにほぼ半日かかってしまった…。
記事書いてる人って、神様なんだろうかガンジーなんだろうか。。)

次回は、「プログラム写経」を行なっていきたいと思います!

参考記事

本稿で参考にさせていただいた記事を紹介させていただきます!
1. kazuo_reveさんの「新人の方によく展開している有益な情報」
https://qiita.com/kazuo_reve/items/d1a3f0ee48e24bba38f1
2. ソースコードを読むための技術
https://i.loveruby.net/ja/misc/readingcode.html

blog/codereading