if문은 특정 조건에 따라 프로그램을 제어하기 위해 사용한다. 그렇기 때문에 요구사항(조건)이 변경되면 영향도가 가장 많이 발생하는 코드 중 하나이다. 일반적으로 우리는 서비스를 분기할때 다형성을 통해 클래스를 디자인하여 (ex. strategy, policy pattern 등) if문을 없애곤 한다. 하지만 이렇게 나뉘어진 서비스들도 각 함수내에 목적 코드를 실행하기 전 제어를 위한 중복된 if문들이 생기게 된다. 경험상 모두가 알다시피 이러한 중복된 if 조건들은 변경사항이 발생하면 프로젝트내에 모두 찾기하여 일일이 수정해야 하기 때문에 분명 좋은 코드가 아니다.

 

  아래 몇가지 패턴은 내가 자주 쓰는 패턴 중 하나다. if 조건들을 오브젝트화 하여 핵심관심(Core Concerns)과 횡단관심(Crosscutting Concerns)을 분리(Computer Science, SoC)하고 중복코드를 없애 재사용이 가능한 상태로 만드는 디자인이다. (물론, AOP를 적용하면 좀 더 우아하게 처리 할 수도 있다.) 

Interceptor Filter Pattern

  코드에서 대부분의 if문들은 아래와 같은 형태를 띄고 있다. [그림 1]은 함수내에서 원하는 목적 코드를 수행하기 전 인증, 권한, 데이터 유효성 확인 등과 같은 전처리를 수행하는 코드를 예로 든다. 보통 이러한 전처리 코드들은 경험상 중복되는 경우가 많으며 변경 가능성이 높은 코드들이다. 

[그림 1] if 조건에 따른 필터 처리

  [그림 1]에서 글쓰기 권한을 '준회원'에서 '일반회원'으로 변경되면 우리는 해당 if문들을 찾아 일일이 수정해야한다. 예시가 간단하기 때문에 쉽게 수정할 수 있다고 생각하겠지만, if문이 많은 그리고 복잡도가 높은 실제 프로젝트내에서 중복 코드의 변경은 개발자의 실수가 충분히 발생할 수 있다. 그리고 이러한 횡단관심 코드들은 해당 함수의 핵심관심을 읽기 어렵게 만든다. 즉, 한 마디로 코드가 더러워 진다. 

 

  위와 같은 경우에는 Interceptor Fileter Pattern을 사용하여 조건들을 오브젝트화 하여  중복코드를 없애면 코드를 좀 더 깔끔하게 만들 수 있다.

[그림 2] Interceptor filter class diagram

  Interfceptor Filter pattern은 조건들을 오브젝트화 하여 중복 코드를 없애고 여러 조건들을 조합하여 재사용할 수 있도록 한다. 또한, 조건 코드와 목적 코드의 디팬던시가 제거됨으로 변경에 따른 영향도를 최소화 할 수 있다.

[그림 3] Interceptor filter sequence diagram

  시퀀스는 간단하다. Client는 FilterChain에 target과 filter들을 추가하고 FilterChain.doFilter()를 호출한다. FilterChain은 첫 번째 Filter를 호출하고 호출된 해당 Filter는 주어진 조건을 확인하고 판단하고 다시 FilterChain..doFilter를 호출하면 다음 Filter를 호출하게 되고 FilterChain.stopFilter를 호출하면 중단되며 target은 실행되지 않는다. 모든 filter들이 실행되어 통과되면 target은 그 때 실행될 수 있다.  각 요구사항에 따라 디테일한 동작은  변형하여 디자인 하면 된다. 수도코드를 보면 아래와 같다.

 

1. FilterChain

2. Filter

3. Target

4. Client에서 사용할 때

 

Chain of Responcibility Pattern

  [그림 1]처럼 하나의 목적코드를 실행하기 위해 전처리 조건들을 사용할때도 있지만, [그림 4] 다음과 같이 조건에 따른 여러 분기를 하여 그에 알맞은 목적 코드를 실행 할 때도 있다. 이럴때는 Chain of Responsibility Pattern를 사용하여 조건들을 오브젝트화할 수 있다. 

[그림 4] if 조건에 따라 목적코드를 분기하는 경우

  [그림 4]는 특정 조건에 따라 목적 코드를 분기하는 경우다. Chain of Responsibility Pattern의 컨셉은 자신의 조건이 맞으면 수행하고 그렇지 않으면 패스한다. else if로 만들수도 있고 if, if, 조건으로 만드는 등 각 요구사항에 맞게  Varient하여 디자인 하면 된다. [그림 5] 아래는 log level에 따라 각 Logger들이 로깅을 할지 말지를 판단하는 체인을 구현한 예시다. 

[그림 5] Logger class diagram

 

[그림 6] Logger sequence diagram

1. CommonLogger

2. concrete Logger 

3. Client 사용할 때

5. 호출 결과

consoleLogger.logMessage(logLevel: .info, message: "info message!!")

 -> console logger info message!!

 

consoleLogger.logMessage(logLevel: .error, message: "error message!!")

 -> console logger error message!! 

      file logger error message!! 

      error logger error message!!

 

+ Recent posts