1. 컴포지트 패턴이란?
- 컴포지트 패턴은 여러 개의 객체들로 구성된 복잡 객체와 단일 객체를 클라이언트에서 구별없이 다르게 해주는 패턴을 의미한다.
간단히 말하자면, 전체-부분의 관계를 갖는 객체들 사이의 관계를 정의할때 주로 사용되며, 또한 클라이언트는 전체와 부분을 구분하지 않고 동일한 인터페이스를 사용할 수 있다.
2. 컴포지트 패턴의 등장인물
1) Component(컴포넌트)
- 구체적인 부분
- Leaf 클래스와 전체에 해당하는 Composite 클래스에 공통 인터페이스를 정의한다.
2) Leaf(리프)
- 구체적인 부분 클래스
- COmposite 객체의 부품으로 설정한다.
3) Composite(컴포지트)
- 전체 클래스
- 복수 개의 Component를 갖도록 정의
- 그러므로 복수 개의 Leaf, 또는 복수 개의 Composite 객체를 부분으로 가질 수 있다.
3. 컴포지트 패턴은 왜 사용할까?
예를 들어, 컴퓨터(Computer)에 여러개의 장치를 추가하는 예제를 만들어 보도록 하자.
//컴퓨터에 추가할 장치들(Monitor, Mouse, Keyboard) ////////////////////////Monitor//////////////////////// class Monitor { //Monitor의 가격 private int price; //Monitor의 전력 private int power; //생성자 public Monitor(int price, int power) { this.price = price; this.power = power; } //Getter 메서드 public int getPrice(){ return price; } public int getPower(){ return power; } } ////////////////////////Mouse//////////////////////// class Mouse { //Mouse의 가격 private int price; //Mouse의 전력 private int power; //생성자 public Mouse(int price, int power) { this.price = price; this.power = power; } //Getter 메서드 public int getPrice(){ return price; } public int getPower(){ return power; } } ////////////////////////Keyboard//////////////////////// class Keyboard { //Keyboard의 가격 private int price; //Keyboard의 전력 private int power; //생성자 public Keyboard(int price, int power) { this.price = price; this.power = power; } //Getter 메서드 public int getPrice(){ return price; } public int getPower(){ return power; } } //Computer클래스 class Computer { //각 장치들의 변수 생성 private Monitor monitor; private Mouse mouse; private Keyboard keyboard; //Computer에 각 장치를 추가하는 메서드 public void addMonitor(Monitor monitor) { this.monitor = monitor; } public void addMouse(Mouse mouse) { this.mouse = mouse; } public void addKeyboard(Keyboard keyboard) { this.keyboard = keyboard; } //각 장치들로 만들어진 컴퓨터의 가격과 전력량을 얻는 메서드 public int getPrice() { int monitorPrice = monitor.getPrice(); int mousePrice = mouse.getPrice(); int keyboardPrice = keyboard.getPrice(); return monitorPrice + mousePrice + keyboardPrice; } public int getPower() { int monitorPower = monitor.getPower(); int mousePower = mouse.getPower(); int keyboardPower = keyboard.getPower(); return monitorPower + mousePower + keyboardPower; } }
다음은 이를 실행할 Client클래스를 구현해보도록 하자(main 클래스)
public class Client{ public static void main(String[] args) { //각 장치들의 객체 생성 Monitor monitor = new Monitor(10000000, 30); Mouse mouse = new Mouse(10000, 4); Keyboard keyboard = new Keyboard(20000, 5); //Computer객체를 생성한 뒤 각 장치 객체 설정 Computer computer = new Computer(); computer.addMonitor(monitor); computer.addMouse(mouse); computer.addKeyboard(keyboard); //만들어진 컴퓨터의 각격과 전력량을 구함 int computerPrice = computer.getPrice(); int computerPower = computer.getPower(); System.out.println("Computer Price : " + computerPrice); System.out.println("Computer Power : " + computerPower); } } //결과값 Computer Price : 10030000 Computer Power : 39
하지만 여기서 더 많은 장치들이 추가된다고 가정하자
//Speaker클래스 public class Speaker { private int price; private int power; public Speaker(int power, int price) { this.power = power; this.price = price; } public int getPrice() { return price; } public int getPower() { return power; } } //Speaker가 추가된 Computer 클래스 class Computer { //각 장치들의 변수 생성 private Monitor monitor; private Mouse mouse; private Keyboard keyboard; //Speaker 객체 추가 ========================> 추가 코드 private Speaker speaker; //Computer에 각 장치를 추가하는 메서드 public void addMonitor(Monitor monitor) { this.monitor = monitor; } public void addMouse(Mouse mouse) { this.mouse = mouse; } public void addKeyboard(Keyboard keyboard) { this.keyboard = keyboard; } //Speaker 추가 메서드 추가 =======================> 추가 코드 public void addSpeaker(Speaker speaker) { this.speaker = speaker; } //각 장치들로 만들어진 컴퓨터의 가격과 전력량을 얻는 메서드 public int getPrice() { int monitorPrice = monitor.getPrice(); int mousePrice = mouse.getPrice(); int keyboardPrice = keyboard.getPrice(); //Speaker 가격 메서드 =================================> 추가 코드 int speakerPrice = speaker.getPrice(); return monitorPrice + mousePrice + keyboardPrice + speakerPrice; } public int getPower() { int monitorPower = monitor.getPower(); int mousePower = mouse.getPower(); int keyboardPower = keyboard.getPower(); //Speaker 가격 메서드 =================================> 추가 코드 int speakerPower = speaker.getPower(); return monitorPower + mousePower + keyboardPower + speakerPrice; } }
이처럼 설계를 한다면 확장성이 떨어지게 되고(개방-폐쇄의 원칙에 위배), 새로운 부품을 추가할 때마다 계속해서 코드를 수정해야한다.
- 구체적인 부품들을 일반화한 ComputerDevice 클래스 정의( = Component )
abstract class ComputerDevice { public abstract int getPrice(); public abstract int getPower(); }
- ComputerDevice 클래스를 상속받은 각각의 장치 클래스 구현( = Leaf)
////////////////////////Monitor//////////////////////// class Monitor extends ComputerDevice{ //Monitor의 가격 private int price; //Monitor의 전력 private int power; //생성자 public Monitor(int price, int power) { this.price = price; this.power = power; } //ComputerDevice의 추상 메서드 오버라이딩 public int getPrice(){ return price; } public int getPower(){ return power; } } ////////////////////////Mouse//////////////////////// class Mouse extends ComputerDevice{ //Mouse의 가격 private int price; //Mouse의 전력 private int power; //생성자 public Mouse(int price, int power) { this.price = price; this.power = power; } //ComputerDevice의 추상 메서드 오버라이딩 public int getPrice(){ return price; } public int getPower(){ return power; } } ////////////////////////Keyboard//////////////////////// class Keyboard extends ComputerDevice{ //Keyboard의 가격 private int price; //Keyboard의 전력 private int power; //생성자 public Keyboard(int price, int power) { this.price = price; this.power = power; } //ComputerDevice의 추상 메서드 오버라이딩 public int getPrice(){ return price; } public int getPower(){ return power; } }
- ComputerDevice를 상속받은 Computer클래스 구현( = Composite)
public class Computer extends ComputerDevice {
// 복수 개의 ComputerDevice 객체를 배열로 생성
private List<ComputerDevice> components = new ArrayList<ComputerDevice>();
// ComputerDevice 객체를 Computer 클래스에 추가하는 메서드
public addComponent(ComputerDevice component) {
components.add(component);
}
// ComputerDevice 객체를 Computer 클래스에서 제거하는 메서드
public removeComponent(ComputerDevice component) {
components.remove(component);
}
// 전체 가격을 포함하는 각 부품의 가격을 합산
public int getPrice() {
int price = 0;
for(ComputerDevice component : components) {
price += component.getPrice();
}
return price;
}
// 전체 소비 전력량을 포함하는 각 부품의 소비 전력량을 합산
public int getPower() {
int power = 0;
for(ComputerDevice component : components) {
price += component.getPower();
}
return power;
}
}
- CLient클래스 구현( = main)
public class Client{ public static void main(String[] args) { //각 장치들의 객체 생성 Monitor monitor = new Monitor(10000000, 30); Mouse mouse = new Mouse(10000, 4); Keyboard keyboard = new Keyboard(20000, 5); // Computer 객체 생성 Computer computer = new Computer(); // Computer클래스( = Composite)안에 있는 장치 추가 메서드 실행 compuer.addComponent(monitor); compuer.addComponent(mouse); compuer.addComponent(keyboard); //Computer의 가격과 전력량 구하는 메서드 int computerPrice = computer.getPirce(); int computerPower = computer.getPower(); System.out.println("Computer Price : " + computerPrice); System.out.println("Computer Power : " + computerPower); } } //결과값 Computer Price : 10030000 Computer Power : 39
장점
- 객체들이 모두 같은 타입으로 취급된다 => 새로운 클래스 추가가 용이하다
- 단일 객체, 복합 객체의 구분 없이 코드 작성이 가능하다.
단점
- 설계를 일반화한다 => 객체들간의 구분, 제약이 어렵다