안녕하세요. J4J입니다.
이번 포스팅은 퍼사드(Facade) 패턴에 대해 적어보는 시간을 가져보려고 합니다.
Facade 패턴이란?
facade 패턴은 서비스에 존재하는 복잡한 구조의 서브 클래스들을 간략화하여 사용할 수 있도록 인터페이스를 제공해 주는 디자인 패턴입니다.
개념적인 부분으로 접근했을 때는 어떤 말인지 이해하기 어려울 수 있으나 facade 패턴에 대해 간단하게 얘기해보면 중간 다리 역할을 하는 객체를 생성하는 패턴이다라고 생각하시면 됩니다.
facade 패턴의 구조에 대해서 그림으로 표현해보면 다음과 같습니다.
facade 패턴을 사용하지 않는다면 기능을 사용해야 하는 client 입장에서는 다양한 서브 클래스들에 모두 접근해야 될 수 있습니다.
하지만 이런 client의 접근은 서비스의 구조가 확장될수록 서브 클래스들과의 의존 관계가 높아지고 구조를 복잡하게 만드는 원인이 될 수 있습니다.
그래서 해당 상황에서 facade 패턴을 적용한다면 다음과 같은 구조로 변경됩니다.
사실 facade 패턴은 많은 분들이 패턴에 대한 인지를 하고 있지 않음에도 불구하고 자연스럽게 패턴을 적용시키고 계실 겁니다.
예를 들어 여러 가지 기능을 개발하면서 특정 상황에 한 개의 메서드 내부에 정의되어 있는 기능들이 너무 많이 담겨 있다는 생각을 가져보셨을 겁니다.
일반적으로 이런 상황에서는 기능을 최소화하기 위해 메서드를 새롭게 정의하는 과정을 거치게 되고 이 과정을 거치며 새롭게 정의된 기능을 담아두는 클래스를 생성하셨을 수 있습니다.
그리고 최초에 많은 기능들이 담겨 있던 메서드는 새롭게 생성된 클래스를 바라보게 됩니다.
결과적으로 위의 자연스러운 행동 패턴들은 facade 패턴을 적용한 케이스가 됩니다.
또한 spring 개발을 하시는 분들도 인지하지 못한 상황에서 facade 패턴을 적용하고 계실 겁니다.
보통 spring 개발을 할 때 비즈니스 로직을 구성하기 위해 controller-service-repository의 구조를 잡게 됩니다.
물론 각각의 layer들이 수행하는 역할들이 존재하기는 합니다.
하지만 facade 패턴의 관점에서 본다면 service는 controller의 facade 클래스가 될 수 있습니다.
왜냐하면 controller가 다른 여러 클래스들을 바라보지 않고 간략화하여 사용될 수 있도록 service 내부에 기능이 정의되어 있기 때문입니다.
이처럼 facade 패턴은 구조가 복잡해질수록 더 읽기 쉬운 코드를 생산하기 위해 많은 분들이 자연스럽게 적용하고 계실 겁니다.
Facade 패턴 특징
facade 패턴의 특징에 대해 정리해 보면 다음과 같습니다.
[ 장점 ]
- 서브 클래스와 상관없이 facade 클래스만 바라보면 되기 때문에 복잡성을 감소
- 서브 클래스와 상관 없이 facade 클래스만 바라보면 되기 때문에 결합도 감소
- 서브 클래스와 상관 없이 facade 클래스만 이해하면 되기 때문에 client의 편의성을 높임
[ 단점 ]
- 구조를 이해해야 하는 클래스가 늘어나기에 전체 구조를 바라보는 입장에선 추가 복잡성이 발생
- 서브 클래스를 변경하면 facade 클래스도 함께 변경이 필요하기 때문에 추가 작업이 발생
- 한 개의 facade 클래스에 너무 많은 의존성이 담길 수 있음
facade 패턴의 장점과 단점에 대해 확인하신 것처럼 facade 패턴을 적용하는 것은 복잡도가 높은 구조에서 많은 도움이 될 수 있습니다.
하지만 반대로 복잡도가 높지 않은 곳에서 무분별하게 facade 패턴을 적용하게 된다면 관리 포인트만 늘어나지 실질적으로 도움 되는 상황이 발생되지는 않습니다.
결국 서비스를 개발하는 개발자 관점에 facade 패턴 사용을 위한 명확한 기준을 잡고 적용시킨다면 많은 도움이 될 것으로 생각합니다.
Facade 패턴 예시
facade 패턴에 대한 간단한 예시를 얘기해 보겠습니다.
상품을 주문하는 경우를 예시로 하여 코드를 구현한다고 하면 다음과 같이 코드를 작성해 볼 수 있습니다.
// seller
package com.jforj.facade.before;
public class Seller {
public void checkOrder() {
System.out.println("check order by seller");
}
public void requestToFactory() {
System.out.println("request to factory by seller");
}
public void receiveProduct() {
System.out.println("receive product from factory");
}
public void sendProductToDelivery() {
System.out.println("send product to delivery by seller");
}
}
// factory
package com.jforj.facade.before;
public class Factory {
public void checkOrder() {
System.out.println("check order in factory");
}
public void makeProduct() {
System.out.println("make product in factory");
}
public void sendProductToSeller() {
System.out.println("send product to seller in factory");
}
}
// delivery
package com.jforj.facade.before;
public class Delivery {
public void receiveProduct() {
System.out.println("receive product in delivery");
}
public void delivery() {
System.out.println("delivery to customer");
}
}
// main
package com.jforj.facade.before;
public class Main {
public static void main(String[] args) {
Factory factory = new Factory();
Seller seller = new Seller();
Delivery delivery = new Delivery();
seller.checkOrder(); // 주문 확인
seller.requestToFactory(); // 공장에 상품 생산 요청
factory.checkOrder(); // 주문 확인
factory.makeProduct(); // 상품 생산
factory.sendProductToSeller(); // 상품 발송
seller.receiveProduct(); // 상품 수령
seller.sendProductToDelivery(); // 상품 발송
delivery.receiveProduct(); // 상품 수령
delivery.delivery(); // 배송
}
}
코드를 확인해 보면 판매자가 주문을 확인하면 공장에서 상품을 생산하여 배송되는 과정을 알 수 있습니다.
그리고 이런 과정이 고객 입장에서 상품을 주문할 때 흔히 발생될 수 있는 케이스입니다.
하지만 여기서 의문을 제기해 볼 만한 것은 고객은 상품을 주문했을 때 어떤 과정을 통해 상품이 전달되는지 궁금할까? 입니다.
고객 입장에서는 상품을 주문한 것에 대해서만 알고 싶고 세부 과정에 대해서는 궁금하지 않을 겁니다.
이런 내용을 바탕으로 facade 패턴을 적용하여 재 구성한다면 다음과 같이 코드를 변경해 볼 수 있습니다.
// seller
package com.jforj.facade.after;
public class Seller {
public void checkOrder() {
System.out.println("check order by seller");
}
public void requestToFactory() {
System.out.println("request to factory by seller");
}
public void receiveProduct() {
System.out.println("receive product from factory");
}
public void sendProductToDelivery() {
System.out.println("send product to delivery by seller");
}
}
// factory
package com.jforj.facade.after;
public class Factory {
public void checkOrder() {
System.out.println("check order in factory");
}
public void makeProduct() {
System.out.println("make product in factory");
}
public void sendProductToSeller() {
System.out.println("send product to seller in factory");
}
}
// delivery
package com.jforj.facade.after;
public class Delivery {
public void receiveProduct() {
System.out.println("receive product in delivery");
}
public void delivery() {
System.out.println("delivery to customer");
}
}
// facade
package com.jforj.facade.after;
public class OrderFacade {
public void order() {
Factory factory = new Factory();
Seller seller = new Seller();
Delivery delivery = new Delivery();
seller.checkOrder(); // 주문 확인
seller.requestToFactory(); // 공장에 상품 생산 요청
factory.checkOrder(); // 주문 확인
factory.makeProduct(); // 상품 생산
factory.sendProductToSeller(); // 상품 발송
seller.receiveProduct(); // 상품 수령
seller.sendProductToDelivery(); // 상품 발송
delivery.receiveProduct(); // 상품 수령
delivery.delivery(); // 배송
}
}
// main
package com.jforj.facade.after;
public class Main {
public static void main(String[] args) {
OrderFacade orderFacade = new OrderFacade();
orderFacade.order(); // 주문
}
}
변경된 코드를 살펴보면 client 서브 클래스들 사이에 facade 클래스를 새롭게 두었습니다.
그래서 client는 주문을 하는 것에 대해서만 알지 주문을 했을 때 발생되는 세부 과정에 대해서는 모르게 변경되었습니다.
결국 facade 패턴에서 의도한 대로 상위 인터페이스를 새롭게 정의함으로 써 복잡도, 결합도, client의 편의성에서 모두 이점을 얻는 것을 확인할 수 있습니다.
이상으로 퍼사드(Facade) 패턴에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'설계 > 디자인패턴' 카테고리의 다른 글
[디자인패턴] 추상 팩토리(Abstract Factory) 패턴 이해하기 (0) | 2024.04.28 |
---|---|
[디자인패턴] 템플릿 메서드(Template Method) 패턴 이해하기 (0) | 2024.04.22 |
[디자인패턴] 팩토리 메서드(Factory Method) 패턴 이해하기 (0) | 2024.04.22 |
[디자인패턴] 싱글톤(Singleton) 패턴 이해하기 (0) | 2024.03.30 |
[디자인패턴] 빌더(Builder) 패턴 이해하기 (2) | 2024.03.23 |
댓글