컴포짓 (Composite) 패턴
그룹 전체와 개별 객체를 동일하게 처리할 수 있는 패턴 (Part-Whole Hierarchy)
클라이언트 입장에서는 전체나 부분이나 모두 동일한 컴포넌트로 인식할 수 있는 계층 구조를 만든다.
Component
├── Leaf
└── Composite
├── Leaf
└── Composite
├── Leaf
└── Leaf
컴포짓 (Composite) 패턴 Before
@AllArgsConstructor
@Getter
public class Item {
private String name;
private int price;
}
public class Bag {
private List<Item> items = new ArrayList<>();
public void add(Item item){
items.add(item);
}
public List<Item> getItems(){
return this.items;
}
}
public class Client {
public static void main(String[] args) {
Item doranBlade = new Item("도란검", 450);
Item healPotion = new Item("체력 물약", 50);
Bag bag = new Bag();
bag.add(doranBlade);
bag.add(healPotion);
Client client = new Client();
client.printPrice(doranBlade);
client.printPrice(bag);
}
private void printPrice(Item item) {
System.out.println(item.getPrice());
}
private void printPrice(Bag bag){
System.out.println(bag.getItems().stream().mapToInt(Item::getPrice).sum());
}
}
현재 코드 Composite 패턴 관점에서 문제점
중복된 메서드
printPrice(Item)와 printPrice(Bag) 메서드가 서로 다른 타입을 처리하고 있지만, 결국 가격 정보를 출력한다는 점에서 같은 역할을 하고 있다. 이로 인해 코드 중복이 발생한다.
유연성 부족
새로운 복합 객체가 추가되면 printPrice를 계속 오버로딩해야 하며, 클라이언트 코드가 복잡해진다.
즉, 개별 객체와 복합 객체를 동일한 방식으로 처리할 수 없으므로 확장성이 떨어진다.
Composite 패턴 적용 한다면?
Item과 Bag을 공통 인터페이스로 추상화하여, 클라이언트가 개별 객체와 복합 객체를 동일하게 처리할 수 있도록 수정
컴포짓 (Composite) 패턴 After
public interface Component {
int getPrice();
}
@AllArgsConstructor
@Getter
public class Item implements Component{
private String name;
private int price;
@Override
public int getPrice() {
return this.price;
}
}
public class Bag implements Component{
private List<Component> components = new ArrayList<>();
public void add(Component component){
components.add(component);
}
public List<Component> getComponents(){
return this.components;
}
@Override
public int getPrice() {
return components.stream().mapToInt(Component::getPrice).sum();
}
}
Composite 패턴을 적용했다.
Component 인터페이스를 만들고 Item 클래스와 Bag 클래스가 Component 인터페이스를 구현 하도록 하였다.
여기서 중요한 점은 Bag 클래스의 참조이다.
Item → Component
클라이언트 코드
public class Client {
public static void main(String[] args) {
Item doranBlade = new Item("도란검", 450);
Item healPotion = new Item("체력 물약", 50);
Bag bag = new Bag();
bag.add(doranBlade);
bag.add(healPotion);
Client client = new Client();
client.printPrice(bag);
client.printPrice(healPotion);
}
public void printPrice(Component component){
System.out.println(component.getPrice());
}
}
이제 클라이언트는 printPrice(Component com) 1개의 메소드만 가지면 된다.
또한 구체적인 정보 또한 몰라도 된다.
여기서 말하는 구체적인 정보는 가격을 구하는 방법이다.
before client 코드와 비교해 보자.
클라이언트는 Item과 Bag에 대해 각각의 처리 로직을 알고 있어야 했다.
각 클래스에 따라 다른 방식으로 가격을 계산하는 메서드를 직접 구현해야 했다.
이처럼 Composite 패턴을 통해 코드는 더 깔끔해지고, 확장성과 유지보수성이 높아졌다.
컴포짓 (Composite) 패턴 정리
그룹 전체와 개별 객체를 동일하게 처리할 수 있는 패턴
장점
복잡한 트리 구조를 편리하게 사용할 수 있다.
다형성과 재귀를 활용할 수 있다.
클라이언트 코드를 변경하지 않고 새로운 엘리먼트 타입을 추가할 수 있다. (OCP)
단점
트리를 만들어야 하기 때문에 (공통된 인터페이스를 정의해야 하기 때문에) 지나치게 일반화 해야 하는 경우도 생길 수 있다.
컴포짓 (Composite) 패턴 적용 사례 (Swing)
public class SwingExample {
public static void main(String[] args) {
JFrame frame = new JFrame();
JTextField textField = new JTextField();
textField.setBounds(200, 200, 200, 40);
frame.add(textField);
JButton button = new JButton("click");
button.setBounds(200, 100, 60, 40);
button.addActionListener(e -> textField.setText("Hello Swing"));
frame.add(button);
frame.setSize(600, 400);
frame.setLayout(null);
frame.setVisible(true);
}
}
JFrame / JButton / JTestField
모두 Component 인터페이스를 구현한다.
Swing에서 add() 메서드를 사용하여 JFrame에 JButton이나 JTextField를 추가하는 과정은 Composite 패턴의 그룹 전체와 개별 객체를 동일하게 처리하는 특성을 보여준다.
실행하면 이런 화면이 나온다.
참고자료
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4
'Computer Sience > Desgin Pattern' 카테고리의 다른 글
[Design Pattern] 퍼사드(Facade) 패턴 (1) | 2024.10.28 |
---|---|
[Design Pattern] 데코레이터 (Decorator) 패턴 (0) | 2024.10.28 |
[Design Pattern] 브릿지 (Bridge) 패턴 (0) | 2024.10.23 |
[Design Pattern] 어댑터 (Adapter) 패턴 (2) | 2024.10.22 |
[Design Pattern] 프로토타입 (Prototype) 패턴 (2) | 2024.10.20 |