오늘보다 발전된 내일의 나를 위해…












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에 안에 있음) 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("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);


위키피디아에 있는 잘못된 코드보다 훨씬 더 낫다. 올바른 책임 연쇄 패턴은 어떤 메시지를 처리할 수 있는 여러 개체가 있을 때 이 개체들은 차례대로 메시지를 처리할 수 있는 기회를 받는다. 만약 그중 한 개체가 메시지를 처리하면 그거에 대한 책임을 진다. 즉 다음 개체는 메시지를 처리할 기회를 받지 못한다. 이래서 책임 연쇄란 이름이 붙었다. 그래서 맨 앞의 에의 올바른 구현은 아래와 같다.




YoungKyonYou

Integration of Knowledge