3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

株式会社CAMAdvent Calendar 2019

Day 14

レガシーコードに対する解析ツール(重複コード編)

Last updated at Posted at 2019-12-13

こんにちは! 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を利用した結果

こんなコードを書くことはないかもしれませんが、重複コード例を作成しましたので、以下の二つを参考にしていきます。

sample1.js
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;

sample2.js
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だったりの様々な言語の重複率も表示されます。(以下参照)

とある結果.txt
┌────────────┬────────────────┬─────────────┬──────────────┬──────────────────┬────────┐
│ 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/

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?