핸들러를 따라 요청을 전달할 수 있는 디자인 패턴. 요청을 받으면 각 핸들러는 요청을 처리할지 다음 체인으로 전달할지 결정한다.
사용처
장점
단점
구현
모든 핸들러 클래스가 동일한 인터페이스를 구현하는 것이 중요하다. 세부 핸들러는 메서드가 있는 다음 핸들러만 신경을 써야한다.
Handler : 모든 구체적인 핸들러의 공통 인터페이스. 일반적으로 단일메서드만 포함하지만, 다음 핸들러를 설정하기 위한 메서드가 포함되기도 한다.
BaseHandler : 모든 핸들러 클래스에 공통적인 코드를 선택적인 클래스다. 일반적으로는 다음 핸들러에 대한 참조를 저장하기 위해 사용한다. 클라이언트는 이전 핸들러의 생성자나 setter를 통해 체인을 구출할 수 있다.
ConcreateHandlers : 요청을 처리하기 위한 실제 코드. 각 핸들러는 요청을 처리할지 여부와 함께 체인에 전달할지 결정한다.
Client : 체인을 한 번만 구성할지 동적으로 구성할지 결정할 수 있다. 요청은 모든 핸들러에게 보낼 수 있다.
코드
Handler
interface MyIterator <T>{
boolean hasNext();
T next();
}
BaseHandler
public MyIterator<T> iterator() {
return new MyIterator<T>() {
private int index=0;
@Override
public boolean hasNext() {
return index<list.size();
}
@Override
public T next() {
return list.get(index++);
}
};
}
ConcreateHandler
public class MyCollection<T> {
private List<T> list;
public MyCollection(List<T> list) {
this.list = list;
}
public <U> MyCollection<U> map(Function<T, U> function) {
List<U> newList = new ArrayList<>();
foreach(d -> newList.add(function.apply(d)));
return new MyCollection<>(newList);
}
public MyCollection<T> filter(Predicate<T> predicate) {
List<T> newList = new ArrayList<>();
foreach(d -> {
if (predicate.test(d)) newList.add(d);
});
return new MyCollection<>(newList);
}
public int size() {
return list.size();
}
public void foreach(Consumer<T> consumer) {
for (int i=0; i<list.size(); i++){
T data = list.get(i);
consumer.accept(data);
}
}
Client
int s = new MyCollection<>(Arrays.asList("a","ab", "abc", "abcd","abcde"))
.map(String::length)
.filter(i -> i%2 == 0)
.size();
//메서드 체이닝 사용
chain of responsiility는 decorator와 구조가 매우 비슷하다.