Java | オブジェクト指向:UML の読み方

Java Java
スポンサーリンク

UML とは何か(まず全体像)

UML は「図でプログラムを表すための共通ルール」のようなものです。
Java のコードを、箱や矢印で表現して「このシステムにはどんなモノがいて、どうつながっているか」を見える化するために使います。

ポイントは「書き方を完璧に覚えること」ではなく
「図を見たときに、だいたい何が言いたいか読めるようになること」です。
ここでは、現場でよく出てくるクラス図とシーケンス図を中心に、読み方に絞って説明します。


クラス図の読み方(箱と線が何を意味するか)

クラスの箱を読む

クラス図では、ひとつのクラスは「3 段の箱」で描かれます。

一番上の段にクラス名が書かれます。
真ん中の段にフィールド(属性)が書かれます。
一番下の段にメソッド(操作)が書かれます。

例えば、次の Java クラスがあるとします。

public class User {
    private String id;
    private String name;

    public String id() { return id; }
    public String name() { return name; }
}
Java

クラス図上では頭の中でこう読みます。

クラス名の段に「User」
属性の段に「id : String」「name : String」
操作の段に「id() : String」「name() : String」

記号もよく出てきます。
属性やメソッドの前に「+」「-」「#」が付いていたら、可視性を表しています。

「+」は public(どこからでも呼べる)
「-」は private(そのクラスの中だけで使う)
「#」は protected(サブクラスなどから使う)

と対応させて読むと、Java とのつながりが見えてきます。

クラス同士を結ぶ線(関連)を読む

箱と箱の間の線は「関連」です。
意味はシンプルで、「このクラスは、あのクラスを知っている(参照している)」です。

例えば、次のコード。

public class Address {
    private String city;
}

public class User {
    private String name;
    private Address address;  // User は Address をフィールドに持っている
}
Java

クラス図では User と Address の箱が線で結ばれます。
この線を見たときは「User は Address をフィールドに持っているんだな」と読めればOKです。

さらに線の端に数字や記号が書いてあれば「多重度」です。
User 側に「1」、Address 側に「1」と書かれていれば「ユーザ 1 人に対して住所 1 つ」。
User 側に「1」、Address 側に「0..1」と書いてあれば「住所はあってもなくてもいい」。
Post と Comment の線で、Comment 側に「*」と書かれていれば「記事 1 つにコメントが複数」という意味です。

「数字=片側から見たときに相手が何件あるか」と覚えておくと読みやすくなります。

三角形の矢印(継承・実装)を読む

クラス間を結ぶ、先が白い三角形の矢印は「継承(一般化)」です。
矢印の先が親クラス、根本側が子クラスです。

public abstract class Animal {
    public abstract void speak();
}

public class Dog extends Animal {
    @Override
    public void speak() { System.out.println("wan"); }
}
Java

この関係はクラス図では「Dog から Animal へ白い三角矢印」で描かれます。
図を見たときは「Dog は Animal の一種(is-a)」と読みます。

インターフェースの implements も、よく似た形で描かれます。
実装クラスからインターフェースへ、三角形付きの点線矢印が伸びていたら
「このクラスはこのインターフェースを実装している」と読めば大丈夫です。


典型的なクラス図を Java 目線で読む練習

例 1: ユーザと住所

クラス図のイメージを文章で表現するとこうです。

「User」というクラスの箱があり、属性に「id : String」「name : String」「address : Address」が書いてある。
「Address」というクラスの箱があり、属性に「city : String」「street : String」が書いてある。
User と Address の箱の間に線があり、User 側に「1」、Address 側に「0..1」と書いてある。

これを Java に翻訳する感覚で読むと、

User は id と name を持っていて、Address 型のフィールド address を持っている。
Address は city と street を持っている。
User から見た Address は「0 または 1 件」なので、住所がないユーザもありえる。

と理解できます。

コードにすると、こんなイメージです。

public class Address {
    private String city;
    private String street;
}

public class User {
    private String id;
    private String name;
    private Address address;   // 0..1 多重度:null 許容などで表現
}
Java

図を見て「フィールドの型」「多重度」「必須か任意か」を読み取る練習をすると、一気に理解が進みます。

例 2: 記事、コメント、フォーマッタ

文章でクラス図のイメージを表すと、

クラス「Post」があり、属性に「title : String」があり、
「comments : List<Comment>」「formatter : Formatter」を持っている。
クラス「Comment」があり、「message : String」を持っている。
インターフェース「Formatter」があり、「format(raw : String) : String」というメソッドがある。
クラス「UpperFormatter」から「Formatter」に三角形付き矢印が伸びている。
Post と Comment が線で結ばれ、Post 側に「1」、Comment 側に「*」が書かれている。

これを Java 目線で読むと、

Post は複数の Comment を持っている(1 対 多)。
Post は Formatter 型のフィールドを持っている。
UpperFormatter は Formatter を実装している。

と理解できます。

コードにすると次のような感じです。

public interface Formatter {
    String format(String raw);
}

public class UpperFormatter implements Formatter {
    @Override
    public String format(String raw) {
        return raw == null ? "" : raw.toUpperCase();
    }
}

public class Comment {
    private String message;
}

public class Post {
    private String title;
    private List<Comment> comments;
    private Formatter formatter;
}
Java

クラス図の「箱と線と矢印」を見るたびに「Java に直したらどうなる?」と頭で変換するクセを付けると、図が一気に読みやすくなります。


シーケンス図の読み方(メソッド呼び出しの流れを見る)

縦の線と横の矢印を読む

シーケンス図は「時間の流れに沿って、オブジェクト同士のメソッド呼び出しがどう行われるか」を表します。
上から下へ時間が流れ、左から右にオブジェクトが並びます。

縦の点線(ライフライン)は「あるオブジェクトの時間軸」を表します。
横の矢印は「メソッド呼び出し」です。

例えば、こんな処理を考えます。

class Controller {
    private final UserService service;
    Controller(UserService service) { this.service = service; }

    void handle() {
        service.createUser("Taro");
    }
}

class UserService {
    private final UserRepository repo;
    UserService(UserRepository repo) { this.repo = repo; }

    void createUser(String name) {
        repo.save(new User(name));
    }
}

interface UserRepository {
    void save(User user);
}
Java

シーケンス図では、左から「Controller」「UserService」「UserRepository」が縦に並んで、
Controller から UserService へ「createUser()」の矢印、
UserService から UserRepository へ「save()」の矢印が順番に描かれます。

図を読むときは、上から順に目で追いながら

最初に Controller の handle() が呼ばれる
Controller が service.createUser(“Taro”) を呼ぶ
UserService が repo.save(new User(“Taro”)) を呼ぶ

と、「どのオブジェクトが、どの順番で、誰に何を頼んでいるか」を追っていけば十分です。

戻り値の矢印を読む

細かく描かれる図では、メソッドの戻り値も点線の矢印で描かれます。
「呼んだ側へ戻る矢印」があったら、「ここで戻り値が返ってきているんだな」と読むくらいで大丈夫です。

初心者のうちは、戻り値の内容よりも「呼び出しの順番」と「誰が誰に依頼しているか」を読むことに集中してください。
それだけでも、コードを読む前にロジックの大枠をつかめるようになります。


強調したい重要ポイント(UML を“使える”形で読む)

一番大事なのは「UML を暗記すること」ではなく、「図を見て Java の設計意図を読み取れること」です。

クラス図では、
箱の上段はクラス名、中段はフィールド、下段はメソッド。
「+」「-」「#」は public / private / protected。
箱と箱の線は「フィールドで参照している」関係。
線の端の数字は「多重度」(1 件か、0..1 か、* か)。
白三角矢印は継承・実装(is-a)。

シーケンス図では、
上から下が時間の流れ。
縦線がオブジェクトのライフライン。
横の矢印がメソッド呼び出し。
矢印のラベルがメソッド名。

このくらいが読めれば、現場の UML にだいぶついていけるはずです。

タイトルとURLをコピーしました