WHITEPLUS TechBlog

株式会社ホワイトプラスのエンジニアによる開発ブログです。

ChatGPTと書籍で学んだリファクタリングを比較してみた

はじめに

こんにちは、ホワイトプラスのコアシステム開発G エンジニアのyamauchiです。 この記事ではChatGPTがどれくらいリファクタリングに活用できるのか確認するため、書籍「リファクタリング 既存のコードを安全に改善する」から学んだリファクタリングとChatGPTに依頼したリファクタリングにどのような差が出るのか比較し検証してみました。

書籍で学んだリファクタリング

タイプが違うリファクタリング前後のTypeScriptのコードを用意し、書籍のテクニックに沿ってリファクタリングを行いました。

①関数の抽出

処理を関数に括りだし命名することで意図と実装を分離させる。
何を行っているか一目で分からない場合は可読性を上げるために、 2回以上使用されている場合は再利用性と変更容易性を上げるため関数の抽出を行う。

リファクタリング前

public cleanClothes(): void {
  for (let i = 0; i < this.clothes.length; i++) {
    const clothing = this.clothes[i];
    if (clothing.isDirty()) {
      clothing.clean();
    }
  }
}

リファクタリング後

public cleanClothes(): void {
    for (let i = 0; i < this.clothes.length; i++) {
      this.cleanClothing(i);
    }
}

private cleanClothing(index: number): void {
    const clothing = this.clothes[index];
    if (clothing.isDirty()) {
      clothing.clean();
    }
}

②関数のインライン化

名前をつけるまでもなく、その関数が何を処理するか分かる場合は間接的な処理の呼び出しをなくし、直接処理を記述することで冗長性を排除する。

リファクタリング前

function applyDiscount(orderTimes: number, price: number): number {
  if (isFirstTimeOrder(orderTimes)) {
    price -= 100;
  }
  return price;
}

function isFirstTimeOrder(orderTimes: number): boolean {
  return orderTimes === 1;
}

リファクタリング後

function applyDiscount(orderTimes: number, price: number): number {
  if (orderTimes === 1) {
    return price -= 100;
  }
  return price;
}

ChatGPTと書籍のリファクタリングの比較

検証ではChatGPTに対して以下の命令を実行しました。
「以下のTypeScriptのコードをリファクタリングしてください」

また書籍とChatGPTのリファクタリング結果をそれぞれ並べました。
(ChatGPTはリファクタリングを命令すると変更点とその理由をセットで教えてくれるためコード下部にそちらも追記しています)

①関数の抽出

書籍

public cleanClothes(): void {
    for (let i = 0; i < this.clothes.length; i++) {
      this.cleanClothing(i);
    }
}

private cleanClothing(index: number): void {
    const clothing = this.clothes[index];
    if (clothing.isDirty()) {
      clothing.clean();
    }
}

ChatGPT

public cleanClothes(): void {
  this.clothes.forEach((clothing) => {
    this.cleanClothing(clothing);
  });
}

private cleanClothing(clothing: Clothing): void {
  if (clothing.isDirty()) {
    clothing.clean();
  }
}
上記のコードでは、forループの代わりにArray.forEachメソッドを使用しています。また、cleanClothingメソッドの引数を直接衣類のインスタンスとしました。
これにより、より簡潔で読みやすいコードになりました。cleanClothesメソッド内のループ処理がよりシンプルになり、cleanClothingメソッドの呼び出しも直感的です。

②関数のインライン化

書籍

function applyDiscount(orderTimes: number, price: number): number {
  if (orderTimes === 1) {
    return price -= 100;
  }
  return price;
}

ChatGPT

function applyDiscount(orderTimes: number, price: number): number {
  const discount = isFirstTimeOrder(orderTimes) ? 100 : 0;
  return price - discount;
}

function isFirstTimeOrder(orderTimes: number): boolean {
  return orderTimes === 1;
}
上記のコードでは、applyDiscount関数内の条件文を三項演算子を使って単純化しました。
また、discount変数を導入して割引額を明示的に表現しています。これにより、コードの読みやすさが向上しました。

比較の結果

①関数の抽出

ループ処理をfor文で書くかforEachで書くかの差異が発生しましたが、
「関数の抽出」という点ではリファクタリング結果が概ね書籍と一致しました。

②関数のインライン化

書籍とChatGPTで大きく差異が発生しました。
可読性という観点でif文が三項演算子に修正されたものの、期待していた「関数のインライン化」は行われずisFirstTimeOrder()はそのまま独立した関数として残る結果となりました。

考察

想定していた以上にChatGPTは正確にリファクタリングしてくれました。

書籍とChatGPTで幾つか差異はありましたが、リファクタリングの手法や結果は正確なものでした。

またリファクタリングの結果だけでなく、どのような修正を行ったかに加え保守性・可読性など修正の目的までセットで教えてくれるところは嬉しい点でした。

ただ命令に工夫の余地はありそうです。

今回は「観点とかよく分からないからいい感じにリファクタリングして!」と丸投げしたため、ChatGPT側でよしなに汲み取りリファクタリングしてくれましたが、 こちらから明確に観点を絞り、条件付けしてあげることでより再現性が高く、想定した動作を期待できそうです。

②のリファクタリングでは関数のインライン化を達成できませんでしたが明示的に条件を提示して命令した結果、関数のインライン化を行ってくれました。

関数のインライン化を明示的に指示したリファクタリング結果

また条件付けをしていない命令文で複数回命令を実行すると結果が変わってしまうことを確認しました。

同じ命令文で複数回実行した結果

そのため観点が明確な場合は条件付けで命令を行ったほうが良さそうです。

上記の結果を踏まえて、個人的にはあくまで補助的な役割としてChatGPTのリファクタリングを活用するのが良いのかなと感じました。 今後も自身のコード修正やコードレビューなどの参考材料としてChatGPTを使用していきたいと思います。 ※サンプルコードはあくまでリファクタリングの題材のためのコードであるため、ミュータブルになっていない・料金が負の数になる可能性があるなど気になる点はあるかと思いますが何卒ご容赦くださいm( )m

おわりに

ホワイトプラスでは、ビジョンバリューに共感していただけるエンジニアを募集しています!

ネットクリーニングの「リネット」など「生活領域×テクノロジー」で事業を展開している会社です。どんな会社か気になった方はオウンドメディア「ホワプラSTYLE」をぜひご覧ください。

オンラインでカジュアル面談もできますので、今回の記事の内容に興味を持っていただけたら、ぜひお気軽にお問い合わせください。

open.talentio.com

open.talentio.com