アナパタ リターンズ その2:Organization Hierarchy


前回の続き、

Organization Hierarchy

組織の階層構造を表す


再起構造は古くは pigs ear と呼ばれたように自身への関連を追加するだけで非常に単純に表現できる。しかしこの関連だけでは、最上位のオブジェクトには親が存在しないことや、あるオブジェクトの親が、あるオブジェクトの子にはならないなどのルールが表現できないため、関連に { hierarchy } という制約を入れている。
さらに、最上位の親が1つで、複数の親の存在が許されないことを表すために、parent と children のロール名を付与する。


何を解決するか

組織の構造として 部門、部、人 の階層構造があるとし、これをそのままモデル化すると以下のようになる。

この構造は静的に定義されているため、例えば、営業所 を加えることが柔軟に行えない。そこで、部門や部などを Organization(組織) として抽象化し、{hierarchy} の制約をつけて階層構造を表現する。


オブジェクト図で表すと

アロマコーヒーメーカー社は販売と購買の部門があり、販売部門の下にはボストンとロンドンの販売部門がある とすると以下のようになる。


実装例

Organization Hierarchy の実装例を示す。ここでは、組織のためのスタティック変数として Registry を使う。

public class Organization {
    private static Map<String, Organization> instances = new HashMap<>();

    void register() { 
      instances.put(name, this);
    }
    static void clearRegistry() {
      instances.clear();
    }
    static Organization get(String name) {
      return instances.get(name);
    }

    private String name;
    ・・・


そして、Organization のコンストラクタと親と子のアクセッサを用意する。

public class Organization {
    ・・・
    private Organization parent;

    Organization(String name, Organization parent) {
        this.name = name;
        this.setParent(parent);
    }

    Organization getParent() {
        return this.parent;
    }

    Set<Organization> getChildren() {
        Set<Organization> result = new HashSet<>();
        for (Organization org : instances.values()) {
            if (this.equals(org)) {
                result.add(org);
            }
        }
        return result;
    }


祖先や子孫、兄弟の要素を取得する導線を用意することができる。

    // 祖先
    public Set<Organization> getAncestors() { 
        Set<Organization> result = new HashSet<>();
        if (this.parent != null) {
            result.add(this.parent);
            result.addAll(this.parent.getAncestors());
        }
        return result;
    }

    // 子孫
    public Set<Organization> getDescendents() {
        Set<Organization> result = new HashSet<>();
        result.addAll(getChildren());

        for (Organization org : instances.values()) {
            result.addAll(org.getDescendents());
        }
        return result;
    }

    // 兄弟
    public Set<Organization> getSiblings() {
        Set<Organization> result = new HashSet<>();
        result = getParent().getChildren();
        result.remove(this);
        return result;
    }


親要素を追加する場合には階層の構造に関する制約を入れる。

   void setParent(Organization arg) {
        validateParent(arg);
        this.parent = arg;
    }

    void validateParent(Organization org) {
        if (org != null && org.getAncestors().contains(this)) {
            throw new IllegalArgumentException("organization is contained in ancestors");
        }
    }

派生型での表現

Organization の派生型として、具象クラスを用意することもできる。

この場合、Company や Division などの間にある制約(Division の親は Company など)をノートする。


すると実装としては以下のように各派生型において制約をもうけることになる。

class Division extends Organization {
  ・・・
  Division (String name, Company parent) {
    super(name, parent);
  }

  void validateParent(Organization parent) {
    Assert.isTrue(parent instanceof Company);
    super.assertValidParent(parent);
  }
  ・・・