Help us understand the problem. What is going on with this article?

Refactoring第2版のサンプルコードをTypeScriptで実習する part.1 The First Step in Refactoring

More than 1 year has passed since last update.

前回のStarting Pointから初めて、リファクタリングを開始します。

The First Step in Refactoring

「リファクタリングを始める前に、テストが書いてある状態にしておくこと」と書いてるんですが、テストが載ってません。。。

テストランナーとか入れてもいいんですが、ひとまず最低限壊れていないことだけを保証したいので、assertを使って簡単なスクリプトを書いておきます。

statement.spec.ts
import * as assert from "assert";
import { statement } from "./statement";

const invoice = {
  customer: "BigCo",
  performances: [
    {
      playID: "hamlet",
      audience: 55
    },
    {
      playID: "as-like",
      audience: 35
    },
    {
      playID: "othello",
      audience: 40
    }
  ]
};

const plays = {
  hamlet: { name: "Hamlet", type: "tragedy" },
  "as-like": { name: "As You Like It", type: "comedy" },
  othello: { name: "Othello", type: "tragedy" }
};

const actual = statement(invoice, plays);

const expected = `Statement for BigCo
  Hamlet: $650.00 (55 seats)
  As You Like It: $580.00 (35 seats)
  Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits
`;

assert.strictEqual(actual, expected);

これを yarn ts-node statement.spec.ts で実行します。テストがパスすれば、特に何も出力せず終了します。テストが失敗すると、エラーメッセージが表示されます。

npm scriptsで、"test": "ts-node *.spec.ts" のような定義を書き加えておくと便利かもしれません。

テストについては後々拡張していくことになりそうですが、開始地点としてはこんなものでよいでしょう。

関数の抽出

はじめに、以下のswitch文に注目します。

statement.ts
...
    thisAmount = 0;

    switch (play.type) {
      case "tragedy":
        thisAmount = 40000;
        if (perf.audience > 30) {
          thisAmount += 1000 * (perf.audience - 30);
        }
        break;
      case "comedy":
        thisAmount = 30000;
        if (perf.audience > 20) {
          thisAmount += 10000 + 500 * (perf.audience - 20);
        }
        thisAmount += 300 * perf.audience;
        break;
      default:
        throw new Error(`unknown type: ${play.type}`);
    }
...

このコードは、それぞれのPerformanceについて、Playの種別に応じた料金を計算しています。ここはひとまとまりの関数として切り出せそうです。

function amountFor(perf: Performance, play: Play): number {
  let thisAmount = 0;

  switch (play.type) {
    case "tragedy":
      thisAmount = 40000;
      if (perf.audience > 30) {
        thisAmount += 1000 * (perf.audience - 30);
      }
      break;
    case "comedy":
      thisAmount = 30000;
      if (perf.audience > 20) {
        thisAmount += 10000 + 500 * (perf.audience - 20);
      }
      thisAmount += 300 * perf.audience;
      break;
    default:
      throw new Error(`unknown type: ${play.type}`);
  }
  return thisAmount;
}

...
    let thisAmount = amountFor(perf, play);
...

このように、機能の一部を関数として切り出すことを「関数の抽出(Extract Function)」と呼びます。WebStormなどのIDEでも、Refactoring > Extract Methodなどで簡単に行うことができます。

変数名の変更

次に、amountFor関数の中で、変数の名前をいくつか変えてみます。

  • perf => aPerformance
  • thisAmount => result
function amountFor(aPerformance: Performance, play: Play): number {
  let result = 0;

  switch (play.type) {
    case "tragedy":
      result = 40000;
      if (aPerformance.audience > 30) {
        result += 1000 * (aPerformance.audience - 30);
      }
      break;
    case "comedy":
      result = 30000;
      if (aPerformance.audience > 20) {
        result += 10000 + 500 * (aPerformance.audience - 20);
      }
      result += 300 * aPerformance.audience;
      break;
    default:
      throw new Error(`unknown type: ${play.type}`);
  }
  return result;
}
  • 関数の戻り値を保持する変数名は result
  • 変数名が型を表すようにし、さらに冠詞をつける

これらのルールをそのまま適用する必要はないですが、一定のルールを設けることでコードの意図をわかりやすくすることができます。

次はamountFor関数のもう1つの引数playにメスを入れることになりますが、長くなるので続きは次回。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away