abstract class Path { abstract boolean isOk(); } class Fail extends Path { Fail() { } boolean isOk() { return false; } } class Success extends Path { Success() { } boolean isOk() { return true; } } class Right extends Path { Path rest; Right(Path rest) { this.rest = rest; } boolean isOk() { return true; } } class Left extends Path { Path rest; Left(Path rest) { this.rest = rest; } boolean isOk() { return true; } } // ---------------------------------------- abstract class Door { abstract Path escapePath(Person p); } class Into extends Door { Room next; Into(Room next) { this.next = next; } Path escapePath(Person p) { return this.next.escapePath(p); } } class Escape extends Door { String name; Escape(String name) { this.name = name; } Path escapePath(Person p) { if (p.isDest(this.name)) return new Success(); else return new Fail(); } } class Short extends Door { Room next; double height; Short(Room next, double height) { this.next = next; this.height = height; } Path escapePath(Person p) { if (p.height <= this.height) return this.next.escapePath(p); else return new Fail(); } } class Room { Door left; Door right; Room(Door left, Door right) { this.left = left; this.right = right; } Path escapePath(Person p) { Path lp = this.left.escapePath(p); if (lp.isOk()) return new Left(lp); else { Path rp = this.right.escapePath(p); if (rp.isOk()) return new Right(rp); else return new Fail(); } } } class Person { String dest; double height; Person(String dest, double height) { this.dest = dest; this.height = height; } boolean isDest(String s) { return this.dest.equals(s); } } // The Factory class helps in writing tests: // new Factory().Example().escapePath(new Person("mars", 1)) class Factory { Factory() { } Room Example() { Door meadow = new Escape("meadow"); Door street = new Escape("street"); Room ms = new Room(meadow, street); Room planets = new Room(new Escape("mars"), new Escape("venus")); return new Room(new Into(ms), new Short(planets, 1)); } }