Effective Java 第2版 を可視化する

井上です。

どのプログラム言語においても、必読書と呼ばれる本が何冊か存在すると思います。
私は長らく使っているJavaでは「Effective Java 第2版」が真っ先に挙げられると思います。

この本、初版が出版されたのが2001年、第2版が出版されたのが2008年なのですが、諸事情により一時期絶版になっていました。
しかし、今年の3月にめでたく再販となりました。
私自身、今でもたまに読み返す程手放せない本になっています。
#ただ、2008年出版ということもあり、バージョンとしては Java 5 対応の内容となっており、Java 8 対応の第3版の発売を期待したいところです。

さて、Effective Java のような技術書を読む場合、皆さんはどのように読み進めるでしょうか?最初から順に読む人も多いのではないでしょうか?
しかし、Effective Java を初めて読む人がその方法で読もうとすると、理解するのがとても難しいです。
なぜなら、この本、前後関係なく他の項目への参照が大変多いのです。

例えば、最初の項目である「項目1 コンストラクタの代わりに static ファクトリーメソッドを検討する」を見てみると、以下の9項目を参照していることが分かります(参照順)。

  • 項目15 可変性を最小限にする
  • 項目3 private のコンストラクタか enum 型でシングルトン特性を強制する
  • 項目4 private のコンストラクタでインスタンス化不可能を強制する
  • 項目30 int 定数の代わりに enum を使用する
  • 項目18 抽象クラスよりインタフェースを選ぶ
  • 項目52 インタフェースでオブジェクトを参照する
  • 項目32 ビットフィールドの代わりに EnumSet を使用する
  • 項目53 リフレクションよりインタフェースを選ぶ
  • 項目16 継承よりコンポジションを選ぶ

これでは、項目1 を読み終わる前に挫折しかねませんね。
私自身も、最初から順番に読むのは断念して、興味のある章や項目を拾い読みして、ちょっとずつ理解を深める方法を取りました。

では、実際どのような順番で読むと理解しやすいでしょうか?
各項目同士がどのような参照関係を持っているかを可視化できれば、何かヒントが得られるのではないかと思い立ち、実際にやってみることにしました。

各項目同士の参照関係を表現する方法として、有向グラフを用いることにします。
各項目をノードとして表現し、ある項目が別の項目を参照している場合、ノード間にエッジを引くことにします。

以下は、項目1から参照している項目を表した有向グラフの例です。

sample

今回、有向グラフを可視化するにあたり、JUNG (Java Universal Network/Graph Framework) というJava言語用のグラフ可視化ライブラリを使いました。

JUNG
http://jung.sourceforge.net/

さて、ここからが本番です。
Effective Java を再読して、各項目がどの項目を参照しているかを全て洗い出し、以下のようなコードで可視化してみました。

package effectivejava;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.io.FileUtils;

import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Effective Java (第2版) の項目同士の関係を
 * JUNG(Java Universal Network/Graph Framework) を使って可視化する
 */
public class EffectiveJavaSecondEditionVisualization {

    private static Graph<Integer, Integer> createGraph()
            throws IOException {

        // 項目同士の関係性を有向グラフで表す
        final Graph<Integer, Integer> graph = new DirectedSparseGraph<>();

        // 以下の形式のファイルを用意する (reference.txt)
        // (1列目の項目から2列目の項目を参照していることを表す)
        //
        // 1 15
        // 1 3
        // ...
        final List<String> lines = FileUtils.readLines(new File("reference.txt"), "UTF-8");
        final Pattern pattern = Pattern.compile("^(\\d+) (\\d+)$");
        int edgeNumber = 0;
        for (final String line : lines) {
            final Matcher matcher = pattern.matcher(line);
            if (matcher.matches()) {
                final Integer source
                        = Integer.valueOf(matcher.group(1));
                final Integer target
                            = Integer.valueOf(matcher.group(2));
                if (graph.containsVertex(source) == false) {
                    graph.addVertex(source);
                }
                if (graph.containsVertex(target) == false) {
                    graph.addVertex(target);
                }
                graph.addEdge(edgeNumber++, source, target);
            }
        }
        return graph;
    }

    private static void visualize(Graph<Integer, Integer> graph) {

        // the Fruchterman-Reingold force-directed algorithm
        final Layout<Integer, Integer> layout
                = new FRLayout<>(graph);

        layout.setSize(new Dimension(800, 600));
        final BasicVisualizationServer<Integer, Integer> visualizationServer =
                new BasicVisualizationServer<>(layout);
        visualizationServer.setPreferredSize(new Dimension(800, 600));
        final Transformer<Integer, Paint> vertexPaint
                = new Transformer<Integer, Paint>() {
            public Paint transform(final Integer i) {
                if (i < 1 || 78 < i) {
                    throw new IllegalArgumentException();
                }
                // 章ごとに色分けする
                if (i <= 7) {         // 第2章
                    return Color.GREEN;
                } else if (i <= 12) { // 第3章
                    return Color.RED;
                } else if (i <= 22) { // 第4章
                    return Color.YELLOW;
                } else if (i <= 29) { // 第5章
                    return Color.ORANGE;
                } else if (i <= 37) { // 第6章
                    return Color.MAGENTA;
                } else if (i <= 44) { // 第7章
                    return Color.PINK;
                } else if (i <= 56) { // 第8章
                    return Color.BLUE;
                } else if (i <= 65) { // 第9章
                    return Color.CYAN;
                } else if (i <= 73) { // 第10章
                    return Color.LIGHT_GRAY;
                } else {              // 第11章
                    return Color.WHITE;
                }
            }
        };
        visualizationServer.getRenderContext()
                .setVertexFillPaintTransformer(vertexPaint);
        visualizationServer.getRenderContext()
                .setVertexLabelTransformer(new ToStringLabeller<Integer>());
        visualizationServer.getRenderer()
                .getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);

        final JFrame frame
                = new JFrame("Effective Java 第2版 - 項目同士の関係");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(visualizationServer);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String... args) throws IOException {

        final Graph<Integer, Integer> graph = createGraph();

        visualize(graph);
    }
}

以下は、上記のコードを実行して項目同士の関係を可視化した例です。(分かりやすいように章ごとに色を変えています)

EffectiveJava-Visualization

実際可視化された項目同士の参照関係を見て、何となく読み取れそうなことは以下の通りでしょうか。

  • 第2章に含まれる項目、特に項目1〜項目6が他の章の項目への参照が多いので、後半に読む方が良さそう(第3章、第4章についても同様)
  • 第5章、第6章、第9章、第11章は、章に含まれる項目同士の関連性が高く、かつ他の章への参照も少ないので、早い段階に読むのが良さそう
  • 第7章、第8章は一般的な内容ではあるが、項目同士の参照関係的には第5章、第6章、第9章の後に読んだ方が良さそう
  • 第10章は章に含まれる項目同士の関連性が高いが、他の章(第4章とか)への参照が多いので、後半(第4章と同時期)に読むのが良さそう

今回可視化をしてみる前までは、経験則的に次のような順で読み進めるのが良さそうだと考えていました。

  • 第8章 プログラミング一般 (項目45 〜 項目56)
  • 第7章 メソッド (項目38 〜 項目44)
  • 第5章 ジェネリックス (項目23 〜 29)
  • 第6章 enum とアノテーション (項目30 〜 項目37)
  • 第9章 例外 (項目57 〜 項目65)
  • 第4章 クラスとインタフェース (項目13 〜項目22)
  • 第3章 すべてのオブジェクトに共通のメソッド (項目8 〜 項目12)
  • 第2章 オブジェクトの生成と消滅 (項目1 〜 項目7)
  • 第10章 並行性 (項目66 〜 項目73)
  • 第11章 シリアライズ (項目74 〜 項目78)

しかし、今回可視化してみた結果から、以下のような順番の方が読みやすいのかも知れませんね。

  • 第5章 ジェネリックス (項目23 〜 29)
  • 第6章 enum とアノテーション (項目30 〜 項目37)
  • 第9章 例外 (項目57 〜 項目65)
  • 第11章 シリアライズ (項目74 〜 項目78)
  • 第8章 プログラミング一般 (項目45 〜 項目56)
  • 第7章 メソッド (項目38 〜 項目44)
  • 第4章 クラスとインタフェース (項目13 〜項目22)
  • 第10章 並行性 (項目66 〜 項目73)
  • 第3章 すべてのオブジェクトに共通のメソッド (項目8 〜 項目12)
  • 第2章 オブジェクトの生成と消滅 (項目1 〜 項目7)

さて、今回は 有向グラフ を用いて Effective Java の項目同士の参照関係を可視化してみました。

Effective Java をこれから読まれる方や、以前挫折して積ん読状態になっている方は、今回の情報を元にチャレンジされてみてはいかがでしょうか?

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中