ООП для чайников. Паттерны проектирования. Цепочка ответственности (Chain of Responsibility)

Еще один паттерн, из группы поведенческих, — цепочка ответственности (chain of responsibility).

Цепочка обязанностей выстраивает объекты составных частей приложения связанными между собой по цепочке, для передачи запроса на обработку от более низких, детализированных слоев системы к более высоким глобальным.

Вот UML диаграмма:

chain

Классический пример это контекстная справка в 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...

Видно как злость влияет на принятие благоразумных решений.
Пример, к сожалению, далек от совершенства но, надеюсь, общую идею иллюстрирует.

Comments

comments

Leave a reply:

Site Footer

Sliding Sidebar

About Me

About Me

Для кого этот блог?

Для тех кого интересуют современные интернет технологи, IT бизнес, стартапы, менеджмент, контроль качества, личная эффективность, мотивация. Здесь я буду писать о том, что в первую очередь будет интересно мне, о проблемах и решениях. О том что пригодилось мне, и возможно будет интересно Вам.

Что заставило меня создать его?

Желание совершенствоваться. Достигать успеха. Находить людей со схожими проблемами и задачами, вместе искать выходы и решения.

Немного о себе.

Мой первый серьезный опыт в IT это работа над desktop приложениями в компании «Эксперт-Софт». У истоков её стояли несколько амбициозных и талантливых молодых людей, с огнем в глазах и желанием работать «как майкрософт». То чем мы там занимались вполне могли бы сегодня назвать «стартапом». Рук было откровенно мало, поэтому приходилось заниматься всем: кодированием на Delphi, написанием скриптов на VBA, дизайном, вёрсткой и поддержкой вебсайта, работой над рекламной полиграфией, проектированием интерфейсов и БД. Работы было много, но запал был велик, команда очень разношерстная, гармонично дополняя друг-друга в решении нетривиальных задач. Благодаря тому что пришлось попробовать многое, постепенно вырисовалось понимание того чем хочется заниматься, и как. Софтверным программированием я был сыт по горло. Массы проблем десктопного софта в вебе просто не было, по определению. Зато был четкий фокус на дизайне, юзабилити, скорости. Поэтому когда пришла пора уходить из «Эксперт-Софт», я без всякого сожаления стал искать работу как разработчик для web. Поскольку городишко у нас не очень большой, выбор был практически предопределен. Так я стал работать в «Оникс-Системз», где и продолжаю работать поныне. За время работы в компании я как разработчик принимал участие в работе над несколькими десятками проектов. Несколько десятков проектов было сделано мною как фрилансером. Самым большим проектом в котором я сыграл роль менеджера, считаю свою семью. Также довольно большой проект мы сейчас поднимаем с командой разработчиков (на данный момент команда состоит из четырех php разработчиков, одного flex кодера и тестировщика). Отсюда, большой интерес к современным практикам и методологиям, разным подходам в управлении командой, повышению эффективности и качества работы. По мере сил, вдохновения и свободного времени, я буду писать об этом.

Если у Вас возникли какие-то вопросы ко мне лично, буду рад если Вы свяжетесь со мной:

e-mail:
skype: denis.sheremetov
Старый сайт, с музычкой и флешом

Прочая онлайновая деятельность: