
책임 연쇄 패턴은 무엇일까?
@FunctionalInterface
public interface Logger {
public enum LogLevel {
INFO, DEBUG, WARNING, ERROR, FUNCTIONAL_MESSAGE, FUNCTIONAL_ERROR;
public static LogLevel[] all() {
return values();
}
}
abstract void message(String msg, LogLevel severity);
default Logger appendNext(Logger nextLogger) {
return (msg, severity) -> {
message(msg, severity);
nextLogger.message(msg, severity);
};
}
static Logger writeLogger(LogLevel[] levels, Consumer<String> stringConsumer) {
EnumSet<LogLevel> set = EnumSet.copyOf(Arrays.asList(levels));
return (msg, severity) -> {
if (set.contains(severity)) {
stringConsumer.accept(msg);
}
};
}
static Logger consoleLogger(LogLevel... levels) {
return writeLogger(levels, msg -> System.err.println("Writing to console: " + msg));
}
static Logger emailLogger(LogLevel... levels) {
return writeLogger(levels, msg -> System.err.println("Sending via email: " + msg));
}
static Logger fileLogger(LogLevel... levels) {
return writeLogger(levels, msg -> System.err.println("Writing to Log File: " + msg));
}
}
class Runner {
public static void main(String[] args) {
// Build an immutable chain of responsibility
Logger logger = consoleLogger(LogLevel.all())
.appendNext(emailLogger(LogLevel.FUNCTIONAL_MESSAGE, LogLevel.FUNCTIONAL_ERROR))
.appendNext(fileLogger(LogLevel.WARNING, LogLevel.ERROR));
// Handled by consoleLogger since the console has a LogLevel of all
logger.message("Entering function ProcessOrder().", LogLevel.DEBUG);
logger.message("Order record retrieved.", LogLevel.INFO);
// Handled by consoleLogger and emailLogger since emailLogger implements Functional_Error & Functional_Message
logger.message("Unable to Process Order ORD1 Dated D1 For Customer C1.", LogLevel.FUNCTIONAL_ERROR);
logger.message("Order Dispatched.", LogLevel.FUNCTIONAL_MESSAGE);
// Handled by consoleLogger and fileLogger since fileLogger implements Warning & Error
logger.message("Customer Address details missing in Branch DataBase.", LogLevel.WARNING);
logger.message("Customer Address details missing in Organization DataBase.", LogLevel.ERROR);
}
}
public abstract class Logger {
private EnumSet<LogLevel> logLevels;
private Logger next;
public Logger(LogLevel[] levels) {
this.logLevels = EnumSet.copyOf(Arrays.asList(levels));
}
public Logger setNext(Logger next) {
this.next = next;
return this.next;
}
logLevel이라는 enum set으로 값들을 집합으로 들고 있고next로 나다음의 로거를 가지고 있다.levels를 받는데, 이는 해당 로그에서 처리할 레벨을 정해준다.setNext를 보니 fluent interface와 비슷하게 생겼다.next를 반환한다. public final void message(String msg, LogLevel severity) {
if (logLevels.contains(severity)) {
log(msg);
}
if (this.next != null) {
this.next.message(msg, severity);
}
}
protected abstract void log(String msg);
}
message 함수는 final이니 상속 불가다.log 함수는 상속받는 클래스에서 구현public class ConsoleLogger extends Logger {
public ConsoleLogger(LogLevel[] levels) {
super(levels);
}
@Override
protected void log(String msg) {
System.err.println("Writing to console: " + msg);
}
}
public class EmailLogger extends Logger {
public EmailLogger(LogLevel[] levels) {
super(levels);
}
@Override
protected void log(String msg) {
System.err.println("Sending via email: " + msg);
}
}
public class FileLogger extends Logger {
public FileLogger(LogLevel[] levels) {
super(levels);
}
@Override
protected void log(String msg) {
System.err.println("Writing to file: " + msg);
}
}
public enum LogLevel {
INFO,
DEBUG,
WARNING,
ERROR,
FUNCTIONAL_MESSAGE,
FUNCTIONAL_ERROR;
public static LogLevel[] all() {
return values();
}
}
Logger logger = new ConsoleLogger(LogLevel.all());
logger
.setNext(new EmailLogger(new LogLevel[]{LogLevel.FUNCTIONAL_MESSAGE, LogLevel.FUNCTIONAL_ERROR}))
.setNext(new FileLogger(new LogLevel[]{LogLevel.WARNING, LogLevel.ERROR}));
// ConsoleLogger에서 처리 -> 모든 로그레벨 처리 가능
logger.message("Entering function ProcessOrder().", LogLevel.DEBUG);
logger.message("Order record retrieved.", LogLevel.INFO);
// ConsoleLogger, EmailLogger에서 처리 가능
logger.message("Unable to Process Order ORD1 Dated D1 For Customer C1.", LogLevel.FUNCTIONAL_ERROR);
logger.message("Order Dispatched.", LogLevel.FUNCTIONAL_MESSAGE);
next로 메시지를 호출하게 해서 연쇄적으로 다음 친구를 호출할 수 있게 된다.
public final class LogManager {
private static LogManager instance;
private ArrayList<Logger> loggers = new ArrayList<Logger>();
public static LogManager getInstance() {
if (instance == null) {
instance = new LogManager();
}
return instance;
}
public void addHandler(Logger logger) {
this.loggers.add(logger);
}
public void message(String msg, LogLevel severity) {
for (Logger logger : this.loggers) {
logger.message(msg, severity);
}
}
}
public abstract class Logger {
private EnumSet<LogLevel> logLevels;
public Logger(LogLevel[] levels) {
this.logLevels = EnumSet.copyOf(Arrays.asList(levels));
}
public final void message(String msg, LogLevel severity) {
if (logLevels.contains(severity)) {
log(msg);
}
}
protected abstracy void log(String msg);
}
LogManager logManager = LogManager.getInstance();
logManager.addHandler(new ConsoleLogger(LogLevel.all()));
logManager.addHandler(new EmailLogger(new LogLevel[]{LogLevel.FUNCTIONAL_MESSAGE, LogLevel.FUINCTIONAL_ERROR}));
logManager.message("Entering function ProcessOrder().", LogLevel.DEBUG);
public final void message(String msg, LogLevel severity) {
if (logLevels.contains(severity)) {
log(msg);
}
if (this.next != null) {
this.next.message(msg, severity);
}
}
public final void message(String msg, LogLevel severity) {
if (logLevels.contains(severity)) {
log(msg);
} else if (this.next != null) {
this.next.message(msg, severity);
}
}