Notice
Recent Posts
Recent Comments
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

DeFacto-Standard IT

Facade Pattern (기본) 본문

Design Pattern/Structural Pattern

Facade Pattern (기본)

defacto standard 2018. 2. 16. 23:52

1. 가정


 - 로거를 구현한다.

 - 문자열을 넘기면 해당 문자열을 출력하는 로거를 작성한다.


2. Naive Code


 - Log4J

public class Log4J {
public void logging(String string) {
System.out.println("Log4J : " + string);
}
}

Log4J라는 이름의 Logger클래스이다. 문자열을 받아서 콘솔에 출력하는 역할을 한다.


 - NaiveClient

public class NaiveClient {
public static void main(String args[]) {
Log4J logger = new Log4J();
logger.logging("Log this String");
}
}

로거를 활용해서 문자를 로깅한다.


실행 결과는 다음과 같다.



3. 문제점


다음과 같은 요구사항의 변경이 생겼다고 가정해보자.

 - Log4J가 아닌 Logback을 사용한다. (Logback은 Log4J의 업그레이드 버전이다)


위 요구사항을 충족시키기 위해서는 Log4J클래스를 Logback 클래스로 선언해야한다.


즉, 이를 사용하는 Client의 소스코드가 변경되어야 한다. 이는 새로운 타입의 로거가 출시되고, 이를 변경하고자 할 때 마다 발생한다.



4. 해결방안


위와 같은 문제가 발생하는 이유는, Client와 Log4J라는 로거 간의 강한 결합도 때문이다.


따라서 로거라는 개념을 추상화하고, Client에서는 추상화된 개념을 사용하여 다루어야 한다.


이러한 추상화된 개념을 Facade라고 한다.


Log4J와 Logback의 추상적인 개념인 Facade에 해당하는 인터페이스를 Logger라고 하겠다.


Logger라는 추상적 개념을 통해 접근을 한다면,


현재 Logger 레퍼런스에 바인딩된 구체적인 로거가 Log4J라면 Log4J에 맞는 작업을 수행할 것이고

현재 Logger 레퍼런스에 바인딩된 구체적인 로거가 Logback이라면 Logback에 맞는 작업을 수행할 것이다.


그러나 이 구체적인 로거가 바뀔 필요가 있다고 하더라도 이를 사용하는 클래스의 소스코드는 변하지 않는다.




5. Solution Code


 - Facade

public interface Facade {
public void operation();
}


 - ConcreteFacade1, 2, 3

public class ConcreteFacade1 implements Facade {
@Override
public void operation() {
System.out.println("ConcreteFacade1# operation()");
}
}
public class ConcreteFacade2 implements Facade {
@Override
public void operation() {
System.out.println("ConcreteFacade2# operation()");
}
}
public class ConcreteFacade3 implements Facade {
@Override
public void operation() {
System.out.println("ConcreteFacade3# operation()");
}
}


 - Client

public class Client {
public static void main(String args[]) {
Facade facade;

facade = new ConcreteFacade1();
facade.operation();

facade = new ConcreteFacade2();
facade.operation();

facade = new ConcreteFacade3();
facade.operation();
}
}

3번 모두 다른 ConcreteFacadeN 객체를 사용하여 바인딩 하지만, 결국 operation의 수행은 Facade타입의 변수인 facade를 통해서만 이루어지는 것에 주목한다.


실행결과는 다음과 같다.



6. Facade Pattern


은행을 예로 들어보자.


고객은 어떤 은행이든 상관없이, 창구 직원에게 '출금'을 요청하면, '출금'이라는 행위를 은행에서 알아서 해준다.


이는 모든 은행의 공통적인 특징이다.


은행마다 조금씩 순서는 달라도, 결국 '출금'을 요청하기만 하면 된다.


'출금'은 예제에서 '로깅'에 해당한다.


'은행'은 예제에서 '로거'에 해당한다.


고객 : Client 클래스

창구 : Facade 인터페이스

출금 : Facade 인터페이스가 구현하는 함수

A은행, B은행 : Facade 인터페이스를 구현하는 구체적인 클래스


정도로 정리할 수 있겠다.


위와 같이 구조화 한다면 얻을 수 있는 장점은

1. 사용할 구체적인 클래스를 바꿔도 Client에서 Facade를 사용하는 코드는 변하지 않는다.

2. Facade를 구현하는 클래스가 늘어나더라도 다른 부분의 소스코드 변경이 일어나지 않는다.


과 같다.


- UML & Sequence Diagram



- Class Diagram




7. 장단점

장점 : 

단점 : 

'Design Pattern > Structural Pattern' 카테고리의 다른 글

Flyweight Pattern  (0) 2018.02.16
Bridge Pattern (기본)  (3) 2018.02.16
Adapter Pattern  (0) 2018.02.16
Proxy Pattern  (0) 2018.02.16
Composite Pattern  (0) 2017.09.26
Comments