티스토리 뷰

knowledge/pattern

[Pattern] Facade Pattern

글을 쓰는 개발자 2021. 12. 12. 09:28
반응형

의도

한 서브시스템 내의 인터페이스 집합에 대한 획일화된 하나의 인터페이스를 제공하는 패턴으로, 서브시스템을 사용하기 쉽도록 상위 수준의 인터페이스를 정의

 

동기

시스템을 서브시스템으로 구조화하면 복잡성을 줄이는 데에 큰 도움이 된다. 공통적인 설계 목표는 서브시스템들 사이의 의사소통 및 종속성을 최소화하려는 것!

예를 들어 소비자가 어느 물품에 대하여 구매버튼을 눌렀다고 하자. 그러면 물류센터에서는 해당 물품을 출고할 것이고, 택배사에서는 해당 물품에 대하여 배송관련 작업을 시행할 것이다. 소비자 입장에서는 구매 버튼을 누르면 특정 시간이 지났을 때 물품이 오면 되는 것이다. 이러한 커머스 시스템은 구매 관련 클래스를 정의하고 구매 관련되어 있는 인터페이스를 정의합니다. 구매자는 어떤 택배사와 물류센터를 통해서 배달이 오는지는 알 필요가 없습니다.

이런 구매 관련 클래스를 퍼사드 객체로 정의하면 쉽게 사용할 수 있습니다. 예시를 통해 말하자면 구매 과녈 클래스는 사용자에게는 커머스 시스템을 사용하는 데 필요한 가장 필수적인 인터페이스만을 제공하고, 내부적으로는 커머스를 구현하는 클래스들을 함께 동작하도록 묶어주는 역할도 수행하면 됩니다.

 

 

Buyer.java

public class Buyer {
    private final String address;
    private final String detailAddress;
    private final String postal;
    private final String phoneNumber;
    private Issue issue;

    public Buyer(String address, String detailAddress, String postal, String phoneNumber) {
        this.address = address;
        this.detailAddress = detailAddress;
        this.postal = postal;
        this.phoneNumber = phoneNumber;
    }

    public void buy(DeliveryCompanyType deliveryCompanyType) {
        Work work = new WareHouseImpl(WorkType.출고);
        issue = new Issue(new ArrayList<>(List.of(work)),deliveryCompanyType);
    }

    public String getAddress() {
        return address;
    }

    public String getDetailAddress() {
        return detailAddress;
    }

    public String getPostal() {
        return postal;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public Issue getIssue() {
        return issue;
    }

}

 

2. Delivery.java

public interface Delivery extends Work {
    void setDeliveryCompanyType(DeliveryCompanyType type);
}

 

3. DeliveryCompanyType.java

public enum DeliveryCompanyType {
    CJ,
    한진,
    우체국
}

 

4. DeliveryImpl.java

public class DeliveryImpl implements Delivery{
    private final WorkType workType;
    private DeliveryCompanyType deliveryCompanyType;

    public DeliveryImpl(WorkType workType) {
        this.workType = workType;
    }

    public DeliveryCompanyType getDeliveryCompanyType() {
        return deliveryCompanyType;
    }

    @Override
    public WorkType getWorkType() {
        return workType;
    }

    @Override
    public void setDeliveryCompanyType(DeliveryCompanyType type) {
        this.deliveryCompanyType = type;
    }
}

 

5. Issue.java

public class Issue {
    private final List<Work> flow;

    private final DeliveryCompanyType deliveryCompanyType;

    public Issue(List<Work> work, DeliveryCompanyType deliveryCompanyType) {
        this.flow = work;
        this.deliveryCompanyType = deliveryCompanyType;
    }

    public WorkType currentWork() {
        return flow.get(flow.size()-1).getWorkType();
    }

    public void next(Work work) {
        Work recentWork = flow.get(flow.size()-1);
        if (work.getWorkType().ordinal() - recentWork.getWorkType().ordinal() == 1) {
            if (work instanceof DeliveryImpl) {
                ((DeliveryImpl) work).setDeliveryCompanyType(deliveryCompanyType);
            }
            flow.add(work);
        }else if(work.getWorkType() == WorkType.드랍){
          throw new RuntimeException("이미 드랍된 이슈입니다.");
        } else{
            throw new RuntimeException("다음 단계가 아닙니다.");
        }
    }

    public void drop() {
        flow.add(new DeliveryImpl(WorkType.드랍));
    }

    public DeliveryCompanyType getDeliveryCompanyType() {
        return deliveryCompanyType;
    }
}

 

6. WareHouse.java

public interface WareHouse extends Work {
}

 

7. WareHouseImpl.java

public class WareHouseImpl implements WareHouse{
    public static final String LOCATION = "용인Hub";
    private final WorkType workType;

    public WareHouseImpl(WorkType workType) {
        this.workType = workType;
    }

    @Override
    public WorkType getWorkType() {
        return workType;
    }
}

 

8. WorkType.java

public interface Work {
    WorkType getWorkType();
}

 

9. WorkType.java

public enum WorkType {
    출고,
    송장,
    배송,
    완료,
    반품,
    회수,
    드랍
}

 

10. 테스트 케이스 확인 결과

    @Test
    void FACADE_PATTERN_관련_테스트() {
        Buyer buyer = new Buyer("서울특별시 강남구 xxx",
                "x층 xxx호",
                "12345",
                "01012345678"
        );
        buyer.buy(DeliveryCompanyType.CJ);
        assertThat(buyer.getIssue().currentWork()).isEqualTo(WorkType.출고);
        Issue currentIssue = buyer.getIssue();
        currentIssue.next(new DeliveryImpl(WorkType.송장));
        assertThat(buyer.getIssue().currentWork()).isEqualTo(WorkType.송장);
        buyer.getIssue().next(new DeliveryImpl(WorkType.배송));
        assertThat(buyer.getIssue().currentWork()).isEqualTo(WorkType.배송);
        assertThatThrownBy(() ->buyer.getIssue().next(new DeliveryImpl(WorkType.회수)))
                .isInstanceOf(RuntimeException.class).hasMessage("다음 단계가 아닙니다.");
        buyer.getIssue().drop();
        assertThat(buyer.getIssue().currentWork()).isEqualTo(WorkType.드랍);
    }

결과

  • 서브 시스템의 구성요소를 보호할 수 있다. 이로써 사용자가 다루어야 할객체의 수가 줄어들며, 서브 시스템을 쉽게 사용할 수 있습니다.
  • 서브시스템과 사용자 코드 간의 결합도를 더욱 약하게 만든다. 서브시스템 내 정의된 요소들은 강하게 결합할 수 있다. 서브시스템과 사용자 간의 결합이 약하면, 서스시스템 내의 요소를 다양화하는 작업을 원할하게 할 수 있다.

 

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   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
글 보관함