오늘보다 발전된 내일의 나를 위해…
Design Pattern-Wrapper Pattern
해당 내용은 POCU 아카데미 COMP_2500에서 배운 내용을 공부하기 위해 작성된 글입니다
Proxy Pattern
책임 연쇄라는 말은 다소 추상적으로 들린다. 위키에서 책임 연쇄 패턴에 대해 찾아보면 아래와 같은 코드를 볼 수 있다.
하지만 이는 복잡하면서도 잘못된 책임 연쇄 패턴이다. 위키에 나온 예를 이해하기 쉽도록 구성해보자.
Logger Class
public abstract class Logger{
private EnmSet<LogLevel> logLevels;
private Logger next;
public Logger(LogLevel[] levels){
this.logLevels=EnumSet.coptOf(Arrays.asList(levels));
}
public Logger setNext(Logger next){
this.next=next;
return this.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);
}
위 코드를 살펴보면 멤버 변수로 next를 가지고 있는데 이는 자기 자신을 참조하기 위함이다. message 메서드에서의 if문은 만약 내가 severity 즉, 로그 레벨을 처리할 수 있다면(Enum
ConsoleLogger Class
public class ConsoleLogger extends Logger{
public ConsoleLogger(LogLevel[] levels){
super(levels);
}
@Override
protected void log(String msg){
System.err.println("Writing to console: " +msg);
}
}
EmailLogger Class
public class EmailLogger extends Logger{
public EmailLogger(LogLevel[] levels){
super(levels);
}
@Override
protected void log(String msg){
System.err.println("Sending via email: "+msg);
}
}
FileLogger Class
public class FileLogger extends Logger{
public FileLogger(LogLevel[] levels){
super(levels);
}
@Override
protected void log(String msg){
System.err.println("Writing to log file: "+msg);
}
}
Enum LogLevel
public enum LogLevel{
INFO,
DEBUG,
WARNING,
ERROR,
FUNCTIONAL_MESSAGE,
FUNCTIONAL_ERROR;
public static LogLevel[] all(){
return values();
}
}
이제 실제로 로거를 써보자
Main Class
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에서 처리 (consoleLogger는 모든 로그 레벨을 처리)
logger.message("Entering function ProcessOrder().", LogLevel.DEBUG);
logger.message("Order record retrieved.", LogLevel.INFO);
//consoleLogger와 emailLogger에서 처리한다
//(emailLogger는 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);
//consoleLogger와 fileLogger에서 처리 (fileLogger는 Warning과 Error 로그 레벨을 처리)
logger.message("Customer Address details missing in Branch DataBase.",LogLevel.WARNING);
logger.message("Customer Address details missing in Organization DataBase.",LogLevel.ERROR);
실제 로거는 하나만 만들고 그 로거에 메시지를 쏴주면 알아서 다 호출해주는 것이다. 이 logger가 연쇄적으로 다음 애들을 호출하는 방식이다.
Logger Class
public abstract class Logger{
(...)
public final void message(String msg, LogLevel severity){
(...)
if(this.next!=null){
this.next.message(msg, severity);
}
}
(...)
}
위 코드에서 if 문을 통해 다음을 호출한다. 하지만 여기서 질문을 해야 한다.
왜 이런 쓸데없는 짓을 할까??
훨씬 더 직관적인 방법을 살펴보자.
LogManager Class
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 mesage(String msg, LogLeve severity){
for(Logger logger : this.loggers){
logger.message(msg, severity);
}
}
}
Logger Class
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 abstract void log(String msg);
}
ConsoleLogger Class
public class ConsoleLogger extends Logger{
public ConsoleLogger(LogLevel[] levels){
super(levels);
}
@Override
protected void log(String msg){
System.err.println("Writing to console: " +msg);
}
}
EmailLogger Class
public class EmailLogger extends Logger{
public EmailLogger(LogLevel[] levels){
super(levels);
}
@Override
protected void log(String msg){
System.err.println("Writing to console: " +msg);
}
}
FileLogger Class
public class FileLogger extends Logger{
public FileLogger(LogLevel[] levels){
super(levels);
}
@Override
protected void log(String msg){
System.err.println("Writing to console: " +msg);
}
}
Main Class
LogManager logManager=LogManager.getInstance();
logManager.addHandler(new ConsoleLogger(LogLevel.all()));
logManager.addHandler(new EmailLogger(new LogLevel[]{LogLevel.FUNCTIONAL_MESSAGE,LogLevel.FUNCTIONAL_ERROR}));
logManager.addHandler(new FileLogger(new LogLevel[]{LogLevel.WARNING, LogLevel.ERROR}));
logManager.message("Entering function processOrder().", LogLevel.DEBUG);
logManager.message("Order record retrieved.", LogLevel.INFO);
logManager.message("Unable to Process Order ORD1 Dated D1 For Customer C1.", LogLevel.FUNCTIONAL_ERROR);
logManager.message("Customer Address details missing in Branch DataBase.",LogLevel.WARNING);
logManager.messagE("Customer Address details missing in Organization DataBase.",LogLevel.ERROR);
위키피디아에 있는 잘못된 코드보다 훨씬 더 낫다. 올바른 책임 연쇄 패턴은 어떤 메시지를 처리할 수 있는 여러 개체가 있을 때 이 개체들은 차례대로 메시지를 처리할 수 있는 기회를 받는다. 만약 그중 한 개체가 메시지를 처리하면 그거에 대한 책임을 진다. 즉 다음 개체는 메시지를 처리할 기회를 받지 못한다. 이래서 책임 연쇄란 이름이 붙었다. 그래서 맨 앞의 에의 올바른 구현은 아래와 같다.