Wzorzec projektowy „Łańcuch odpowiedzialności”
Wzorzec projektowy łańcuch odpowiedzialności, jest wzorcem behawioralnym, który umożliwia przekazywanie zapytań między obiektami, aż do momentu, gdy któreś z nich zdecyduje, że jest w stanie obsłużyć dane zapytanie. Wzorzec ten pozwala na elastyczne i dynamiczne przypisanie obsługi żądania w trakcie działania programu.

Wzorzec łańcuch odpowiedzialności opiera się na tworzeniu łańcucha obiektów, z których każdy może obsłużyć daną prośbę lub przekazać ją dalej do następnego obiektu w łańcuchu.
Działa to na zasadzie hierarchii. W tym wzorcu klient przekazuje żądanie do pierwszego obiektu
w łańcuchu. Jeśli obiekt ten nie może obsłużyć żądania, przekazuje je do wyższego w hierarchii obiektu i tak dalej, aż żądanie zostanie obsłużone lub dojdziemy do końca łańcucha.
Podstawowe elementy wzorca Łańcuch odpowiedzialności
Handler – klasa bazowa, która określa podstawowe metody i właściwości, które muszą zostać zaimplementowane przez każdy kolejny element łańcucha
BaseHandler (opcjonalnie)- bazowy obiekt obsługujący, który zawiera kod przygotowawczy który będzie wspólny dla każdego elementu łańcucha odpowiedzialności.
ConcreteHandler – jest to konkretna implementacja klasy Handler, która ma za zadanie obsłużyć żądanie lub przekazać je dalej do następnego elementu w łańcuchu.
Client – jest to klasa lub obiekt, który przekazuje żądanie do pierwszego elementu w łańcuchu.
Przykład implementacji we wzorcu Łańcuch odpowiedzialności
public abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler handler) {
nextHandler = handler;
}
public abstract void handleRequest(Request request);
}
public class Request {
private String type;
private String data;
public Request(String type, String data) {
this.type = type;
this.data = data;
}
public String getType() {
return type;
}
public String getData() {
return data;
}
}
Pierwszy Handler
public class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getType().equals("type1")) {
System.out.println("Request handled by ConcreteHandler1");
} else {
if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
}
Drugi Handler
public class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getType().equals("type2")) {
System.out.println("Request handled by ConcreteHandler2");
} else {
if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
}
Klasa Client
public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNextHandler(handler2);
Request request1 = new Request("type1", "data1");
Request request2 = new Request("type2", "data2");
Request request3 = new Request("type3", "data3");
handler1.handleRequest(request1);
handler1.handleRequest(request2);
handler1.handleRequest(request3);
}
}
W tym przykładzie mamy trzy klasy: Handler, ConcreteHandler1 i ConcreteHandler2. Klasa Handler jest klasą abstrakcyjną, która definiuje metodę handleRequest i pole nextHandler.
Klasy ConcreteHandler1 i ConcreteHandler2 dziedziczą po klasie Handler i implementują metodę handleRequest. Klasa Client tworzy instancje obu klas ConcreteHandler1 i ConcreteHandler2, łączy je ze sobą i przekazuje im trzy różne żądania.
Po uruchomieniu programu otrzymujemy następujące wyniki:
Request handled by ConcreteHandler1
Request handled by ConcreteHandler2
Wynika to z faktu, że pierwsze żądanie jest typu „type1„, który jest obsługiwany przez ConcreteHandler1, a drugie żądanie jest typu „type2„, który jest obsługiwany przez ConcreteHandler2. Trzecie żądanie zostało zignorowane, ponieważ żaden z handlerów nie obsługiwał tego typu.