こんにちは! matsuheiです。
この記事は、CAM Advent Calendar 14日目の記事です。
昨日は、 kita21 さんの 今まで触れてきたコードを少しだけ改善する(https://qiita.com/kita21/items/f95834d4c0f7b8093305) でした。
先日、レガシーコードで分析する際に使うと良さそうなものを調べたり、教えてもらったりした際に色々な手法やものが存在することを知りました。
今回は、その時に知ったものの一つ、レガシーコードに見つけた重複コード検出手法について記述していきます。
背景
- 以前、比較的レガシーコードに触れる機会があった
- コード書く際に似たコードを生成してしまうことがあったので、その際に気づけるようにしたかった
- レガシーコード改善の手法を調べることで、開発の際に気をつけるべきことを学びたかった
重複コードを検出するためのツール
今回利用したのは jscpd
です。
https://github.com/kucherenko/jscpd
jsでなければ、phpだと phpcpd
、golangだと dupl
などがあります。
なぜjscpd
を使おうと思ったのか
この理由に関しては、
- npm packageにより、インストールが簡単
- 存在するコードに対して、より理解しやすく簡潔なものにするためにはどうすればいいのか考えた結果です。
利用方法
次に利用方法についてざっくりと記述していきます。
jscpd
のインストール
npmを利用してインストールしました。
npm install jscpd
jscpd
の使い方
基本的な使用方法は以下の形だと思います。
jscpd -p {path}
対応言語は、以下のようにさまざまなものに対応しています。
- javascript
- PHP
- Python
- Ruby
- Java
- html
- css etc...
また、色々なオプションがありますので、それを以下に表示しておきます。
最新のものに関しては、 jscpd --help
にて確認してください。
Options:
-V, --version output the version number
-l, --min-lines [number] min size of duplication in code lines (Default is 5)
-k, --min-tokens [number] min size of duplication in code tokens (Default is 50)
-x, --max-lines [number] max size of source in lines (Default is 500)
-z, --max-size [string] max size of source in bytes, examples: 1kb, 1mb, 120kb (Default is 30kb)
-t, --threshold [number] threshold for duplication, in case duplications >= threshold jscpd will exit with error
-c, --config [string] path to config file (Default is .cpd.json in <path>)
-i, --ignore [string] glob pattern for files what should be excluded from duplication detection
-r, --reporters [string] reporters or list of reporters separated with coma to use (Default is time,console)
-o, --output [string] reporters to use (Default is ./report/)
-m, --mode [string] mode of quality of search, can be "strict", "mild" and "weak" (Default is "mild")
-f, --format [string] format or formats separated by coma (Example php,javascript,python)
-b, --blame blame authors of duplications (get information about authors from git)
-s, --silent do not write detection progress and result to a console
-a, --absolute use absolute path in reports
-n, --noSymlinks dont use symlinks for detection in files
--ignoreCase ignore case of symbols in code (experimental)
-g, --gitignore ignore all files from .gitignore file
--formats-exts [string] list of formats with file extensions (javascript:es,es6;dart:dt)
-d, --debug show debug information(options list and selected files)
--list show list of total supported formats
--xsl-href [string] (Deprecated) Path to xsl file
-p, --path [string] (Deprecated) Path to repo, use `jscpd <path>`
-h, --help output usage information
sample codeにjscpd
を利用した結果
こんなコードを書くことはないかもしれませんが、重複コード例を作成しましたので、以下の二つを参考にしていきます。
const getDateDetail = (year, month, date, hour, minute, second) => {
const dateObj = new Date(`${year}/${month}/${date} ${hour}:${minute}:${second}`)
const dateDetail = {
year,
month,
date,
hour,
minute,
second,
formattedDate: dateObj.toString(),
integer: dateObj.getTime()
};
dateDetail.keyNum = Object.keys(dateDetail).length + 1;
return dateDetail;
}
console.log(getDateDetail(2019, 11, 11, 11, 11, 11,));
module.exports = getDateDetail;
year = 2019;
month = 11;
date = 11;
hour = 11;
minute = 11;
second = 11;
const dateObj = new Date(`${year}/${month}/${date} ${hour}:${minute}:${second}`)
const dateDetail = {
year,
month,
date,
hour,
minute,
second,
formattedDate: dateObj.toString(),
integer: dateObj.getTime()
};
dateDetail.keyNum = Object.keys(dateDetail).length + 1;
console.log(getDateDetail(2019, 11, 11, 11, 11, 11,));
上を見るとわかるように、いかにも明らかに重複しているコードを準備しました。
これらに対して、jscpd
を実行することで以下のような結果を得ることができます。
結果
> node@1.0.0 jscpd PWD/workspace/javascript
> jscpd -p ./src
Clone found (javascript):
- src/test.js [7:1 - 20:8]
src/sample.js [2:3 - 14:9]
┌────────────┬────────────────┬─────────────┬──────────────┬──────────────────┬────────┐
│ Format │ Files analyzed │ Total lines │ Clones found │ Duplicated lines │ % │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────────┼────────┤
│ javascript │ 2 │ 38 │ 1 │ 13 │ 34.21% │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────────┼────────┤
│ Total: │ 2 │ 38 │ 1 │ 13 │ 34.21% │
└────────────┴────────────────┴─────────────┴──────────────┴──────────────────┴────────┘
Execution Time: 53.669ms
どこが重複しているかが列挙されていき、最後に各言語の重複率が表示されます。
今回の場合だとjavascript
しか記述していないため、javascript
のものしか結果が出てきませんが、実際はyaml, markdownだったりの様々な言語の重複率も表示されます。(以下参照)
┌────────────┬────────────────┬─────────────┬──────────────┬──────────────────┬────────┐
│ Format │ Files analyzed │ Total lines │ Clones found │ Duplicated lines │ % │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────────┼────────┤
│ yaml │ 1 │ 17 │ 0 │ 0 │ 0% │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────────┼────────┤
│ markdown │ 12 │ 2205 │ 0 │ 0 │ 0% │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────────┼────────┤
│ javascript │ 536 │ 11057 │ 145 │ 3512 │ 31.76% │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────────┼────────┤
│ json │ 1 │ 146 │ 0 │ 0 │ 0% │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────────┼────────┤
│ Total: │ 550 │ 13425 │ 145 │ 3512 │ 26.16% │
└────────────┴────────────────┴─────────────┴──────────────┴──────────────────┴────────┘
この情報により、どのくらい重複しているか確認することができ、無駄にコードを作成していることを数値化することができます。
この情報を元に、重複している部分をメソッドにまとめたり、同じ処理を減らしたりなどのきっかけとなり、より簡潔なコードを作成することが可能になると思います!
まとめ
javascriptにて、重複コードを検出するために利用できるツールを紹介しました。
これを利用して同じ処理を見つけて簡潔なコードを生成できるようにと紹介しました。
自分の書いたコードで同じような処理は必要とあれば、まとめられるようにしていきたいですね!
明日は @tosaka07 の記事となります。
お楽しみに!!
参考文献
https://github.com/kucherenko/jscpd
https://elijahmanor.com/js-copypaste-detect/