Composite Pattern?
- 단일 객체든, 객체 집합이든, 같은 방법으로 취급하여 다루는 디자인 패턴
- 트리 구조와 상당히 유사한 성격을 가지고 있다.
- 클라이언트 전체와 부분을 구별하지 않고, 동일한 인터페이스로 사용한다!
구성 요소
- Component : 구체적인 부분 -> 공통 Interface로 선언
- Leaf : 구체적인 부분 클래스 -> Interface를 상속받은 객체
- Composite : 여러 개의 Component를 가지는 객체 -> Leaf의 집합
* 정말 트리 구조와 유사하지 않나요?
* 아닌거 같다면 죄송..
실제 코드
간단하게 여러분들에게 설명하기 위해서 Code로 작성해보았어요!
우선, 가정을 하나 둘게요.
Cafe라는 Domain을 지정해서 설명해볼까 해요
회사를 그만두고 Cafe를 창업하게 됐어요. 그래서 카페에 있는 물품들을 관리하고자 시스템을 개발하고 싶더라구요.
그럼 어떻게 Cafe를 차리기 위한 물품들을 한번에 관리할 수 있을까요?
그래서 추상화된 클래스를 만들었어요!
package designpattern.composite;
abstract class CafeComposite {
private final String name;
private final int price;
CafeComposite(final String name, final int price) {
if (price < 0) {
throw new IllegalArgumentException("가격은 0원이상이어야 합니다.");
}
this.name = name;
this.price = price;
}
String getName() {
return name;
}
int getPrice() {
return price;
}
public abstract String typeOf();
}
1. 추상 클래스 선언
간단하게, 가격과 이름을 저장하는 객체를 만들었습니다.
상품마다 반드시 필요한 정보는 가격과 이름이기 때문에 공통 부분을 추상클래스로 묶었어요
정보조회에 대한 기능과 무슨 물품인지 알기 위해서 그에 맞는 메서드를 만들었죠
다음은 의자와 책상과 커피콩을 살려고 해요!
package designpattern.composite;
class Chair extends CafeComposite {
Chair(final String name, final int price) {
super(name, price);
}
@Override
public String typeOf() {
return "Chair";
}
}
package designpattern.composite;
class Desk extends CafeComposite{
Desk(final String name, final int price) {
super(name, price);
}
@Override
public String typeOf() {
return "Desk";
}
}
package designpattern.composite;
class CoffeeBean extends CafeComposite{
CoffeeBean(final String name, final int price) {
super(name, price);
}
@Override
public String typeOf() {
return "CoffeeBean";
}
}
2. Chair Desk CoffeBean 클래스 선언
스스로가 어떤 종류의 객체인지 설명하는 typeOf 메서드를 완성한 모습입니다!
구성요소들이 준비됬으니, Cafe를 만들러 가볼까요?
package designpattern.composite;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Cafe {
private List<CafeComposite> composites;
public Cafe() {
composites = new ArrayList<>();
}
public void addComposite(final CafeComposite composite) {
composites.add(composite);
}
public int getCafeTotalPrice() {
return composites.stream()
.mapToInt(CafeComposite::getPrice)
.sum();
}
public String getCafeCompositeName() {
return composites.stream()
.map(CafeComposite::getName)
.collect(Collectors.joining("\n"));
}
public void clearAllComposite() {
composites.clear();
}
}
3. Cafe 클래스 구현
구성요소인 Component를 List형태로 가지고 있는 Cafe 클래스입니다.
관리자는 내가 얼마의 비용이 들고 어떤 구성요소가 있는지 쉽게 조회하기 위한 기능들을 만들어봤어요!
전체가격과 모든 구성요소의 이름을 알 수 있겠네요.
그럼 Test를 하러 가볼까요?
package designpattern.composite;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class CafeTest {
private Cafe cafe;
@BeforeEach
void setUp() {
cafe = new Cafe();
}
@Test
void 카페에_있는_물건들의_총합이_올바른지_테스트() {
/* Given */
Chair chair = new Chair("No1.chair", 3000);
Desk desk = new Desk("No1.desk", 2000);
CoffeeBean coffeeBean = new CoffeeBean("Columbia", 300);
/* When */
cafe.addComposite(chair);
cafe.addComposite(desk);
cafe.addComposite(coffeeBean);
/* Then */
assertThat(cafe.getCafeTotalPrice()).isEqualTo(5300);
}
@Test
void 카페에_있는_물건들의_이름이_올바른지_테스트() {
/* Given */
Chair chair = new Chair("No1.chair", 3000);
Desk desk = new Desk("No1.desk", 2000);
CoffeeBean coffeeBean = new CoffeeBean("Columbia", 300);
/* When */
cafe.addComposite(chair);
cafe.addComposite(desk);
cafe.addComposite(coffeeBean);
/* Then */
assertThat(cafe.getCafeCompositeName()).isEqualTo("No1.chair\nNo1.desk\nColumbia");
}
@AfterEach
void tearDown() {
cafe.clearAllComposite();
}
}
4. CafeTest 구현
내가 준비한 재료들과, 관리 시스템이 잘 돌아가는지 테스트할 수 있는 코드입니다!
돌려보니까 기분좋게 통과되네요 ^8^
직접 구현한 객체들의 다이어그램입니다!
When?
그렇다면 컴포지트 패턴은 언제 쓸까요?
- 다양한 Object를 관리하는데 개별적인 특성을 보고 판단하는 것이 아닌 전체로 보고 싶을 때
- 다양한 Object를 한번에 관리하고 싶을 때
그럼 언제 쓰지 말까요?
- 개별적인 Component에 직접 접근하고 싶을 때
당연한 말이겠지만, 통째로 관리하는 입장이기 때문에 자식의 특성이 많아질수록 쓰면 안되겠죠 ㅎㅎ
모든 리스트를 순회하면서 instanceof 를 할 수 없기에...
참고
Composite Pattern
'Developer > Design Pattern' 카테고리의 다른 글
Proxy Pattern - 프록시 패턴 (0) | 2020.01.25 |
---|---|
Bridge Pattern - 브릿지 패턴 (0) | 2020.01.05 |
Command Pattern - 커맨드 패턴 (2) | 2019.10.19 |
Adapter Pattern - 어댑터 패턴 (0) | 2019.06.23 |
Factory Method Pattern - 팩토리 메서드 (0) | 2019.06.21 |