Еще один паттерн, из группы поведенческих, — цепочка ответственности (chain of responsibility).
Цепочка обязанностей выстраивает объекты составных частей приложения связанными между собой по цепочке, для передачи запроса на обработку от более низких, детализированных слоев системы к более высоким глобальным.
Вот UML диаграмма:
Классический пример это контекстная справка в Microsoft Office, вы можете нажать кнопку вопросительного знака и кликнуть на любом элементе интерфейса, а система попытается найти страницу справки, максимально соответствующую запрошенному элементу. Делается это следующим образом, — поскольку интерфейс системы иерархичен, то запрс справки начинает подниматься снизу вверх — от элемента по которому был произведен клик вверх, первый элемент который «возьмется дать ответ», и покажет справку.
В моем случае Цепочку Ответственности иллюстрирует класс игрового юнита, поведение к которому приближается вражеский ююнит, и в зависимости от игровой ситуации мы решаем как он поступит. Я определил несколько поведений: DefaultBehavoir (бызовый класс с общим функционалом), CarefulBehavoir (осторожность), AttackBehavoir (смелость), AngryBehavoir (злость). Мы можем выстроить иерархию поведения так как захотим (при этом верхнее поведение играет более важную роль при принятии решений).
Вот код примера:
// IBehavoir.java
package patterns.chain;
public interface IBehavoir {
public int handle(int distance, int power);
}
// DefaultBehavoir.java
package patterns.chain;
public class DefaultBehavoir implements IBehavoir {
static public int WAIT = 0;
static public int ATTACK = 1;
static public int RUN = 2;
protected DefaultBehavoir behavoir;
protected Unit unit;
public DefaultBehavoir(Unit unit) {
this.unit = unit;
}
public int handle(int distance, int power) {
if(behavoir != null) {
return behavoir.handle(distance, power);
} else {
System.out.println("DefaultBehavoir::handle()");
return DefaultBehavoir.WAIT;
}
}
public DefaultBehavoir addBehavoir(DefaultBehavoir behavoir) {
this.behavoir = behavoir;
return this;
}
static public String getCode(int option) {
switch(option) {
case 1:
return "attack!!!";
case 2:
return "run!";
default:
return "waiting...";
}
}
}
// CarefulBehavoir.java
package patterns.chain;
public class CarefulBehavoir extends DefaultBehavoir implements IBehavoir {
public CarefulBehavoir(Unit unit) {
super(unit);
}
public int handle(int distance, int power) {
System.out.println("CarefulBehavoir::handle()");
if(power < unit.getPower()) {
if(distance < unit.getAttackDistance()) {
return DefaultBehavoir.ATTACK;
} else {
return DefaultBehavoir.WAIT;
}
} else if((distance + 2 < unit.getAttackDistance()) && (power > unit.getPower())) {
return DefaultBehavoir.RUN;
}
return super.handle(distance, power);
}
}
// AttackBehavoir.java
package patterns.chain;
public class AttackBehavoir extends DefaultBehavoir implements IBehavoir {
public AttackBehavoir(Unit unit) {
super(unit);
}
public int handle(int distance, int power) {
System.out.println("AttackBehavoir::handle()");
if((distance < unit.getAttackDistance()) && (power < unit.getPower())) {
return DefaultBehavoir.ATTACK;
}
return super.handle(distance, power);
}
}
// AngryBehavoir.java
package patterns.chain;
public class AngryBehavoir extends DefaultBehavoir implements IBehavoir {
public AngryBehavoir(Unit unit) {
super(unit);
}
public int handle(int distance, int power) {
System.out.println("AngryBehavoir::handle()");
if((distance / 2 < unit.getAttackDistance()) && (power / 5 < unit.getPower())) {
return DefaultBehavoir.ATTACK;
}
return super.handle(distance, power);
}
}
// Unit.java
package patterns.chain;
public class Unit {
protected int power;
protected int attackDistance;
private DefaultBehavoir behavoir;
public Unit(int power, int attackDistance) {
this.power = power;
this.attackDistance = attackDistance;
}
public int getPower() {
return power;
}
public int getAttackDistance() {
return attackDistance;
}
protected void setBehavoir(DefaultBehavoir behavoir) {
this.behavoir = behavoir;
}
protected DefaultBehavoir getBehavoir() {
return behavoir;
}
public int processSituation(int distance, int power) {
return behavoir.handle(distance, power);
}
}
// TestApp.java
package patterns.chain;
public class TestApp {
/**
* @param args
*/
public static void main(String[] args) {
Unit unit = new Unit(5,10);
unit.setBehavoir(
new CarefulBehavoir(unit).addBehavoir(
new AttackBehavoir(unit).addBehavoir(
new DefaultBehavoir(unit)
)
)
);
System.out.println(DefaultBehavoir.getCode(unit.processSituation(1,1)));
System.out.println(DefaultBehavoir.getCode(unit.processSituation(5,10)));
System.out.println(DefaultBehavoir.getCode(unit.processSituation(20,5)));
unit.setBehavoir(
new AngryBehavoir(unit).addBehavoir(
new CarefulBehavoir(unit).addBehavoir(
new DefaultBehavoir(unit)
)
)
);
System.out.println(DefaultBehavoir.getCode(unit.processSituation(1,1)));
System.out.println(DefaultBehavoir.getCode(unit.processSituation(7,20)));
System.out.println(DefaultBehavoir.getCode(unit.processSituation(20,5)));
}
}
Вот результат работы программы:
CarefulBehavoir::handle() attack!!! CarefulBehavoir::handle() run! CarefulBehavoir::handle() AttackBehavoir::handle() DefaultBehavoir::handle() waiting... AngryBehavoir::handle() attack!!! AngryBehavoir::handle() attack!!! AngryBehavoir::handle() CarefulBehavoir::handle() DefaultBehavoir::handle() waiting...
Видно как злость влияет на принятие благоразумных решений.
Пример, к сожалению, далек от совершенства но, надеюсь, общую идею иллюстрирует.