アナパタ リターンズ その4:Accountability

blog1.mammb.com
前回の続き

Accountability

パーティー間の複雑な依存関係グラフを表す。


単純な組織階層を扱う場合には、Organization Hierarchy でどうにかなるが、大きな組織を扱いだすと手に負えなくなってくる。例えばACM社のボストン営業所はニューイングランド地区の統括部門へ売上報告を行う とともに、本社組織の営業部門本部への報告を行うという2つの組織構造の子となる場合など。このような場合は、親と子の関係を Accountability(責任/説明責任/責任関係) という抽象概念で結びつけて組織化すると良い。


オブジェクト図の例

前述の例をオブジェクト図とすると以下のようになる。

各親子の関係は Accountability にて関係付けられ、その関係は Accountability Type にて規定される。


実装例

AccountabilityType を定義する。増減がない場合には enumとして定義することも可。

class AccountabilityType extends NamedObject {
  public AccountabilityType(String name) {
    super(name);
  }
}


Accountability が親と子との関係を表し、どのような関連かを示す AccountabilityType を持つ。

class Accountability {
  private Party parent;
  private Party child;
  private AccountabilityType type;
  
  Accountability (Party parent, Party child, AccountabilityType type) {
    this.parent = parent;
    parent.friendAddChildAccountability(this);
    this.child = child;
    child.friendAddParentAccountability(this);
    this.type = type;
  }

  Party child() { return child; }
  Party parent() { return parent; }
  AccountabilityType type() { return type; }
}


Accountability という関係にてつなぎ合わされるものとして Party を定義し、Party 同士は、任意数のAccountabilityを経て、親と子との関係を持つ。

class Party extends NamedObject {
  private Set<Accountability> parentAccountabilities = new HashSet<>();
  private Set<Accountability> childAccountabilities = new HashSet<>();
  
  public Party(String name) {
    super(name);
  }
  void friendAddChildAccountability(Accountability arg) {
    childAccountabilities.add(arg);
  }
  void friendAddParentAccountability(Accountability arg) {
    parentAccountabilities.add(arg);
  }
}


親と子は以下のコードで抽出できる。

class Party...
  Set parents() {
    Set<Party> result = new HashSet<>();
    for (Accountability acc : parentAccountabilities.values()) {
      result.add(acc.parent());
    }
    return result;
  }

  Set children() {
    Set<Party> result = new HashSet<>();
    for (Accountability acc : childAccountabilities.values()) {
      result.add(acc.child());
    }
    return result;
  }


親と子の関係が"Supervises"というAccountabilityTypeとなっているものがあるかをテストできる。

class Tester...
  AccountabilityType supervision = new AccountabilityType("Supervises");
  Party mark = new Party("mark");
  Party tom = new Party("tom");
  Party stMarys = new Party ("St Mary's");

  public void setUp() {
    new Accountability (stMarys, mark, appointment);
    new Accountability (stMarys, tom, appointment);
  }

  public void testSimple() {
    assert(stMarys.children().contains(mark));
    assert(mark.parents().contains(stMarys));
  }


さらに、以下のようにすると、特定の AccountabilityType の関係となっている親を得ることもできる。

class Party...
  Set parents(AccountabilityType arg) {
    Set<Party> result = new HashSet<>();
    for (Accountability acc : parentAccountabilities.values()) {
      if (acc.type().equals(arg)) result.add(acc.parent());
    }
    return result;
  }


class Tester...
  AccountabilityType appointment = new AccountabilityType("Appointment");

  public void testParents() {
    Accountability.create(tom, mark, supervision);
    assert(mark.parents().contains(stMarys));
    assert(mark.parents(appointment).contains(stMarys));
    assertEquals(2, mark.parents().size());
    assertEquals(1, mark.parents(appointment).size());
    assertEquals(1, mark.parents(supervision).size());
    assert(mark.parents(supervision).contains(tom));
  }


階層構造が崩れ、親と子の関係がループしてしまわないように、インスタンス化時にルールを適用する必要もある。

class Accountability...
  private Accountability (Party parent, Party child, AccountabilityType type) {
    ...
  }
  static Accountability create (Party parent, Party child, AccountabilityType type) {
    if (!canCreate(parent, child, type)) 
        throw new IllegalArgumentException ("Invalid Accountability");
    else return new Accountability (parent, child, type);
  }
  static boolean canCreate (Party parent, Party child, AccountabilityType type) {
    if (parent.equals(child)) return false;
    if (parent.ancestorsInclude(child, type)) return false;
    return true;
  }

class Party...
  boolean ancestorsInclude(Party sample, AccountabilityType type) {
    Iterator it = parents(type).iterator();
    while (it.hasNext()) {
      Party eachParent = (Party) it.next();
      if (eachParent.equals(sample)) return true;
      if (eachParent.ancestorsInclude(sample, type)) return true;
    }
    return false;
  }


こうすることで、親と子の関係を定義した後、関係を逆転して再度定義できないようルール化できる。

class Tester...
  public void testCycle() {
    Accountability.create(mark, tom, supervision);
    try {
      Accountability.create(tom, mark, supervision);
      fail("created accountability with cycle");
    } catch (Exception ignore) {}
    assert(!mark.parents().contains(tom)); //just be sure!
    AccountabilityType modelMentor = new AccountabilityType();
    Accountability.create(tom, mark, modelMentor); // okay to create with different type
    assert(!mark.parents().contains(tom)); //now okay
  }

そして その後

さらに発展して派生パターンと Knowledge Level の議論に進むが、これは次回。

blog1.mammb.com