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 }