リファクタリングはじめの1歩 — 手法や注意点を初心者向けに解説 —

リファクタリングとは?

 プロのソフトウェア開発者はほぼ全員知っているが、学生やアマチュアの開発者はあまり知らない用語として、真っ先に思いつくものの一つが「リファクタリング」ではないでしょうか。

 アマチュアの開発者は、多くの場合自分のためだけにコードを書きます。コードは論文発表のためだったり、自分の作りたい実験的なシステムのためだったり、目的は様々ですが、基本的には期限付きのもので必要な時にある程度動作すればそれでよく、目的を果たした後はそのまま放置されることがほとんどでしょう。一方、プロの開発者は、チームで作業を行うことがほとんどです。プロジェクトの存続期間も長く、製品を完成させた後も、機能追加・機能改善など引き続き作業が発生します。そのため、

  • 他人が読んで理解しやすい
  • 変更がしやすい
  • バグが生まれにくい

など、いわゆる「品質の高い」コードであるかどうかは非常に重要なポイントになります。

 リファクタリングは、ソースコードの品質を向上させる手法の一つです。リファクタリングの教科書ともいえる名著「リファクタリング(第2版): 既存のコードを安全に改善する」での定義を引用すれば、

リファクタリングとは、ソフトウェアの外部の振る舞いを保ったままで、内部の構造を改善していく作業

を指します。具体的には、様々な理由で設計が劣化し、可読性が低下してしまったソースコードを、機能毎に切り分けてクラス化・関数化したり、逆に同じ処理をまとめてクラス化・関数化したり、変数や関数名をわかりやすいものに変えたり、様々な手法を用いて人間にとって理解しやすいコードに置き換えます。この際、ソフトウェアを使うユーザー側から見える挙動は変わっていません(厳密には、処理速度が速くなったり遅くなったりすることがあるかもしれませんが、それが許容できるか、あるいは思わぬプラスの効果になるかはシステムによります)。

リファクタリングにより、以下のようなメリットが得られます。

  1. 全体的な開発スピードの向上
    リファクタリングにより設計が改善されると、各モジュールの影響範囲が明確になります。これにより、例えば新規機能開発の際に、どこをどのように変更すればいいか、容易に理解することができ、開発スピードが上がります。
  1. 潜在的なバグの顕在化
    リファクタリングの作業にはバク修正は含まれていませんが、リファクタリング作業自体を通して、そのソースコードの深く理解することができます。最終的に設計やコードをわかりやすくした結果として、いままで気が付かなかったバグが発見されることがあります。
  1. チームとしてのパフォーマンス向上
    前述のとおり、多くのソフトウェア開発プロジェクトはチーム戦です。自分の書いたコードを他のメンバーがデバックすることもあるでしょう。リファクタリングを行い定常的に可読性を高めておくことは、むしろ自分以外の各メンバーにとってメリットが大きいといえます。また、新しく加わるメンバーにとっても、コードの理解が容易であれば、戦力になるまでのキャッチアップの時間を最小限にできます。

逆にデメリットとしては、リファクタリングをしている間はユーザーが受ける価値は全く変わらない、ということが挙げられます。もっと悪いケースとして、リファクタリングのやり方によっては、せっかく正常に動作しているソフトウェアに手を加えることで、動作がおかしくなってしまうこともありえます。リファクタリングは、ある意味で将来への投資であり、今、ソースコードの品質を上げるコストを支払うことで、上に挙げた将来の開発上の様々なメリットを享受する行為と言えます。つまり、リファクタリングを行うか否かは、

  • 現在の製品がユーザーに与えている価値
  • 製品の将来ロードマップ
  • リファクタリングにかかるコスト
  • リファクタリングにより、動作している製品を改修するリスク
  • 逆にリファクタリングしない場合の、将来のバグやトラブルのリスク


などを総合的に分析して決断すべきでしょう。そのような決断をする際の判断材料のひとつととして、Sider Labs を使用いただければ幸いです。

リファクタリング中のエンジニア

リファクタリングの方法

この章ではリファクタリングの具体的なやり方について説明します。まず最初に準備することは、リファクタリング対象となるコードについての十分なテストコードを用意することです。「動いているものには触るな」という格言があるように、正常に動いているものに手を入れるのは怖いものです。加えた変更の影響範囲が把握できずに、意図せず別の箇所でバグを生んでしまうことは十分ありえます。そのため、テストコードを用意して動作を確認をしながら安全にリファクタリングを行うことが重要になります。

 テストの作成には時間がかかりますが、コードとテストと、自分の実現したいことを2度書くこと自体が、バグを減らすことにつながります。また、GitHub, GitLab などのバージョン管理ツールを導入していない場合は必ず導入しましょう。もしなんらかの問題で、動作がおかしくなってしまっても、バージョン管理ツールで正常な状態にいつでも戻すことができます。

 テストコードの準備ができたら、いよいよリファクタリングを行います。コードの可読性を上げる手法はいろいろあります。先に紹介した、「リファクタリング(第2版): 既存のコードを安全に改善する」では、リファクタリングの手法(How) が78種類ものパターンに分類されて、それぞれの典型的な手順がまとめられています。また、リファクタリングを始めるトリガー(When) となる「コードの不吉な臭い(英: Code Smell)」についても、24種類のパターンを示しています。ここでは、我々のSider Labs サービスとも関係が深いものとして、「コードクローン」を見つけた際の「関数の抽出」の手法を具体的に見ていきましょう。

 いわゆるコピペによって生じる重複したコードは、一般的にコードクローンと呼ばれます。コードクローンは、典型的なコードの不吉な臭いの一つです。コードクローンに修正を加える際には、プロジェクト内部の重複箇所を漏れなく探して、同様の修正をしなければなりません。この作業は、プロジェクトが進み、コードベースが大きくなるにつれて、困難になっていきます。この問題を解決するリファクタリング手法の一つが「関数の抽出」です。

function printOwing(invoice){
	printBanner();
	let outstanding = calculateOutstanding();
	
	//明細の印字
	console.log(`name: ${invoice.customer}`);
	console.log(`amount: ${outstanding}’);
}

List 1: リファクタリング前のサンプルコード
リファクタリング(第2版): 既存のコードを安全に改善する」より引用

function printOwing(invoice){
	printBanner();
	let outstanding = calculateOutstanding();
	printDetails(outstanding);

	function printDetails(outstanding){
		console.log(`name: ${invoice.customer}`);
		console.log(`amount: ${outstanding}’);
	}
}

List 2: リファクタリング後のサンプルコード
リファクタリング(第2版): 既存のコードを安全に改善する」より引用

 List 1 は、なんらかのレシートを出力するためのサンプルコードです。List 1 のコメント文  //明細の印字 以下の処理を関数として抽出したものがList 2 になります。ここでは、「何」をしているのか調べなければ分からないコードの断片(//明細の印字 以下)を見つけたので、「何」をしているかを示す名前の関数(printDetails) として抽出し、可読性を上げています。List 2 では行数が増えていますが、処理の内容はよりわかりやすくなっています。この後、printDetails 関数をprintOwing 関数の外に置いて他の関数からも利用可能にする場合は、1. 変数(この場合outstanding) のスコープを調査し、2. スコープ外となった変数をパラメータとして渡すようにし、3. 元の関数に残った抽出前のコードを抽出された関数(printDetails) の呼び出しに置き換えます。

 上記の例では、理解のためにコード行数がごく小さいため、正直に言えばリファクタリングの意味はほとんどありません。ただし、もしこのコードが大きなシステムの一部であったり、今回取り上げた明細の印字部ロジックが他の機能でも使い回されているような場合は、「関数の抽出」をする価値が出てきます。

 繰り返しますが、実際のリファクタリングの際には、小さな単位で修正を加えていき、修正のたびにコンパイルとテストを行って下さい。また、確認・テストの際にSider Labs も活用いただければ幸いです。

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.