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

Refactoring第2版のサンプルコードをTypeScriptで実習する part.3 Split Loop and more

More than 1 year has passed since last update.

前回の続き。

Removing Total Volume Credits

次は、一時変数volumeCreditsの削除を目標にします。この値はループの中で作られているので、まずはSplit Loop(ループの分割)によって、volumeCreditsの計算だけを別のループに移します。

  for (let perf of invoice.performances) {
    // print line for this order
    result += `  ${playFor(perf).name}: ${usd(amountFor(perf))} (${
      perf.audience
    } seats)\n`;
    totalAmount += amountFor(perf);
  }
  for (let perf of invoice.performances) {
    volumeCredits += volumeCreditsFor(perf);
  }

ついでに、volumeCreditsの初期化処理をSlide statements(文の移動)によって、volumeCreditsの計算箇所の近くに移動しておきます。

  let volumeCredits = 0;
  for (let perf of invoice.performances) {
    volumeCredits += volumeCreditsFor(perf);
  }

これによって、関連する処理が一カ所にまとまったので、関数を抽出します。

  const totalVolumeCredits = (): number => {
    let volumeCredits = 0;
    for (let perf of invoice.performances) {
      volumeCredits += volumeCreditsFor(perf);
    }
    return volumeCredits;
  };

一時変数volumeCreditsは削除して、totalVolumeCredits関数の呼び出しに置き換えます。

  result += `You earned ${totalVolumeCredits()} credits\n`;

次に、一時変数totalAmountも削除してしまいましょう。

  const totalAmount = (): number => {
    let result = 0;
    for (let perf of invoice.performances) {
      result += amountFor(perf);
    }
    return result;
  };

totalAmountの計算処理を関数に抽出し、一時変数totalAmountをtotalAmount関数の呼び出しに置き換えます。

ここまでで現状の確認をしておきます。関数内関数が多いのは気になりますが、処理の流れはずっとシンプルになりました。なお、オリジナルのサンプルコードでは関数を使用するより後で定義していますが、↓のコードでは全て使用前に定義し、また、関数内関数であることがわかりやすいよう、function式ではなくアロー関数式で定義しています。

statement.ts
interface Performance {
  playID: string;
  audience: number;
}

interface Invoice {
  customer: string;
  performances: Array<Performance>;
}

interface Play {
  name: string;
  type: string;
}

function usd(aNumber: number): string {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    minimumFractionDigits: 2
  }).format(aNumber / 100);
}

export function statement(
  invoice: Invoice,
  plays: { [playID: string]: Play }
): string {
  const playFor = (aPerformance: Performance): Play =>
    plays[aPerformance.playID];

  const amountFor = (aPerformance: Performance): number => {
    let result = 0;

    switch (playFor(aPerformance).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: ${playFor(aPerformance).type}`);
    }
    return result;
  };

  const volumeCreditsFor = (aPerformance: Performance): number => {
    let result = 0;
    result += Math.max(aPerformance.audience - 30, 0);
    if (playFor(aPerformance).type === "comedy") {
      result += Math.floor(aPerformance.audience / 5);
    }
    return result;
  };

  const totalVolumeCredits = (): number => {
    let result = 0;
    for (let perf of invoice.performances) {
      result += volumeCreditsFor(perf);
    }
    return result;
  };

  const totalAmount = (): number => {
    let result = 0;
    for (let perf of invoice.performances) {
      result += amountFor(perf);
    }
    return result;
  };

  let result = `Statement for ${invoice.customer}\n`;

  for (let perf of invoice.performances) {
    // print line for this order
    result += `  ${playFor(perf).name}: ${usd(amountFor(perf))} (${
      perf.audience
    } seats)\n`;
  }

  result += `Amount owed is ${usd(totalAmount())}\n`;
  result += `You earned ${totalVolumeCredits()} credits\n`;
  return result;
}

次は、計算処理と文字列のフォーマット処理を分割していきます。

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