読者です 読者をやめる 読者になる 読者になる

クラスの依存関係グラフ

計算機 手慰み Java

Java のクラスの依存関係を調べてみた。

規模が大きいアプリケーションの方が統計が取りやすいので、Apache Hadoop の 3.0.0-alpha バージョンを対象に調べる。テストとアノテーションを除いた、Hadoop 由良のパッケージに含まれるクラスに依存関係のグラフについて調査する。

クラス間の依存関係を調べるのに、JDK8 に含まれる jdeps というコマンドを使った。jdeps に -verbose オプションをつけると 各 jar 内のクラスが依存しているクラスが出力される。-dotoutput オプションで DOT ファイルに結果を保存することができる。下のようなファイルが出力される。

digraph "hadoop-mapreduce-client-common-3.0.0-alpha1.jar" {
    // Path: ./mapreduce/hadoop-mapreduce-client-common-3.0.0-alpha1.jar
   "org.apache.hadoop.mapred.LocalClientProtocolProvider" -> "java.io.IOException";
   "org.apache.hadoop.mapred.LocalClientProtocolProvider" -> "java.lang.Object";
   "org.apache.hadoop.mapred.LocalClientProtocolProvider" -> "java.lang.String";
   "org.apache.hadoop.mapred.LocalClientProtocolProvider" -> "java.net.InetSocketAddress";
   "org.apache.hadoop.mapred.LocalClientProtocolProvider" -> "org.apache.hadoop.classification.InterfaceAudience (not found)";
   "org.apache.hadoop.mapred.LocalClientProtocolProvider" -> "org.apache.hadoop.classification.InterfaceAudience$Private (not found)";
   "org.apache.hadoop.mapred.LocalClientProtocolProvider" -> "org.apache.hadoop.conf.Configuration (hadoop-common-3.0.0-alpha1.jar)";

依存先のクラス名の後ろに( ) 内にパッケージ名が付く。(not found)はパッケージが見つからないという意味です。これがあると→の元と先が同じクラスでも別の名前になってしまうので、sed で取り除く。

今回は、対象の JAR 内のクラスの関係のみを調べたい。そこで、辺が示す先がorg.apache.hadoop以外から始まるパッケージに含まれるクラスとなっている辺は除いた。また、org.apache.hadoop.classification パッケージのクラスも、今回の対象のJAR内のクラスではないので、それらが先にある辺も除いた。

JAR ごとに DOT ファイルが生成されるが、全部をまとめてひとつのグラフとして調べる。

頂点が 7844, 辺が 36076 のサイズの有向グラフとなっている。

graph-tool だと描写が非力だったので、gephi で可視化をする。

f:id:fjkz:20161111223015p:plain

上の画像はグラフを可視化したものである。大きくて色の濃い丸が、in/out を問わない次数の大きい頂点である。辺の色は頂点の色に基づく。少ない頂点のみが次数が大きく、ほとんどの頂点は次数が小さいことが可視化の結果から予想できる。

そこで、次数と、その次数のクラスの数の関係を調べてみる。

f:id:fjkz:20161111203145p:plain

上の図は、次数とその次数を持つクラスの割合を示している。入力次数・出力次数いずれの場合も、次数が10以上100以下のクラスはべき乗則に従っているように見える。どうやらクラスの依存関係のグラフはスケールフリーネットワークの性質を持つ可能性があるようだ。べき係数は 2 程度で、スケールフリーネットワークでは典型的な値となっている。Barabási–Albert モデルのネットワークの成長過程は、ソフトウェアが拡大していく過程と似ていることから、クラスの関係がスケールフリーネットワークとなっていることが説明できるだろう。*1

次数が大きいクラスというのは、ソフトウェアの中で重要なクラスであると考えられる。べき乗則に従うということは、特定のクラスの重要度が飛び抜けて高いということを意味している。

入力次数が大きいクラスは多くのクラスから呼び出される Fan-in なクラスである。何度も再利用されている非常に有用な部品と言える。多くのクラスから利用されていることから、欠陥も少ないと考えられる。一方で、利用者が多いクラスの動きを変える場合は影響範囲が非常に大きくなるので、なかなか変更できあに。そのため、安定度が高いクラスであると言える。

出力次数が大きいクラスは多くのクラスを呼び出している。Fan-out なクラスである。GoFデザインパターンでいうところの Facade となっていて、そのクラスから呼び出される機能が多いということを意味していると思われる。変更の影響を受けやすく、そのため欠陥密度も高いだろう。このクラスを重点的にテストすると効果が高そうだ。

入力次数の大きいところで、べき乗則に従わない特異的に大きい次数を持ったクラスが存在していて、図の右からはみ出してしまっている。具体的には、org.apache.hadoop.conf.Configuration という設定プロパティの値を得るためのクラスが 1368 の入力次数を持っている。このクラスは、非常に多くのクラスから横断的に使用されるクラスである。このようなクラスの存在が、今回調べたソフトウェアに特有のものなのか一般的に存在しうるのかは、他のソフトウェアも調査しないと分からない。もし異常に入力次数が多いクラスが存在するということが一般的であるとしたら、ソフトウェアにスケールフリーネットワークとは異なる特別なネットワーク構造があると言えるかもしれない。

*1:Barabási–Albert モデルのべき係数は 3 で完全な説明はできない。いつかちゃんとまとめたい。