컴퓨터를 모델링해보자. 컴퓨터의 구성 장치인 Keybord, Body, Monitor 클래스를 정의한다. Computer 클래스는 이 구성 장치들을 포함하는 것으로 구현할 수 있다. 클래스 다이어그램에서는 Computer 클래스의 구성 장치 간의 관계를 함성(Composition) 관계로 표현할 수 있다.
Keyboard, Body, Monitor 클래스 코드
public clas Keyboard{
private int price;
private int power;
public Keyboard(int power, int price){
this.power = power;
this.price = price;
}
public int getPrice(){
return price;
}
public int getPower(){
return power;
}
}
public class Body{
private int price;
private int power;
public Body(int power, int price){
this.power = power;
this.price = price;
}
public int getPrice(){
return price;
}
public int getPower(){
return power;
}
}
public class Monitor{
private int price;
private int power;
public Monitor(int power, int price){
this.power = power;
this.price = price;
}
public int getPrice(){
return price;
}
public int getPower(){
return power;
}
}
그리고 Computer 클래스는 이들 클래스의 객체를 부분으로 갖는다.
Computer 클래스
public class Computer{
private Body body;
private Keyboard keyboard;
private Monitor monitor;
public void addBody(Body body){
this.body=body;
}
...
public int getPrice(){
int bodyPrice = body.getPrice();
int keyboardPrice = keyboard.getPrice();
int monitorPrice = monitor.getMonitor();
return bodyPrice+keyboardPrice+monitorPrice;
}
...
}
만약 Speaker 부품을 추가해야 한다고 하면 똑같이 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;
}
}
Computer 클래스에도 Speaker 객체를 사용할 수 있도록 수정한다.
public class Computer{
private Body body;
private Keyboard keyboard;
private Monitor monitor;
private Speaker speaker;
...
}
Speaker 클래스를 지원하려고 Computer 클래스를 수정했지만 이 방식의 설계는 확장성이 좋지 않다. 새로운 부품을 위한 클래스를 Computer 클래스에 추가할 때마다 Computer 클래스의 코드를 수정해야 하기 때문이다. 이는 OCP를 위반한다.
앞선 설계는 Computer 클래스에 속한 부품의 구체적인 객체를 가리키게 되어 OCP를 위반하게 된다. 이를 해결하기 위한 방법은 구체적인 부품들을 일반화한 클래스를 정의하고 이를 Computer 클래스가 가리키게 하는 것이 올바른 설계이다.
Computer가 가질 수 있는 부품들을 일반화해서 ComputerDevice 클래스를 정의했다. 이 클래스는 부품들의 공통 기능만 가지며, ComputerDevice 객체를 실제로 생성할 수는 없다. 그러므로 ComputerDevice는 추상 클래스가 된다.
ComputerDevice와 Keyboard 클래스
public abstract class ComputerDevice{
public abstract int getPrice();
public abstract int getPower();
}
public class Keyboard extends ComputerDevice{
private int price;
private int power;
public Keyboard(int power, int price){
this.power=power;
this.price=price;
}
public int getPrice(){
return price;
}
public int getPower(){
return power;
}
}
Computer 클래스는 ComputerDevice의 하위 클래스이면서 복수 개의 ComputerDevice를 갖도록 설계했다.
Computer 클래스
public class Computer extends ComputerDevice{
//여러 개의 ComputerDevice 객체를 가리킴
private List<ComputerDevice> components = new ArrayList<ComputerDevice>();
//ComputerDevice 객체를 Computer 클래스에 추가함
public void addComponent(ComputerDevice component){
components.add(component);
}
//ComputerDevice 객체를 Computer 클래스에서 제거함
public void 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){
power+=component.getPower();
}
return power;
}
}
addComponent()를 통해 ComputerDevice의 구체적인 부품인 Keyboard, Body, Monitor 객체 등을 Computer 클래스의 부품으로 설정했다.
public class Client{
public static void main(String[] args){
...
Computer comp = new Computer();
comp.addComponent(body);
comp.addComponent(keyboard);
comp.addComponent(monitor);
...
}
}
이제 Computer는 OCP를 준수한다. 즉, Computer 클래스에 새로운 부품 객체를 추가해서 Computer 클래스를 확장하려고 할 때 Computer 클래스의 코드는 변경할 필요가 없다. 일반화된 부품을 의미하는 ComputerDevice 클래스만 이용하기 때문이다. Speaker를 추가하고 싶으면 ComputerDevice의 하위 클래스로 구현하면 된다.
컴퍼지트 패턴은 부분-전체의 관계를 갖는 객체들을 정의할 때 유용하다. Monitor, Body 등의 객체가 Computer 클래스 전체 객체의 일부분으로 정의되었다. 이런 경우 부분 객체의 추가나 삭제 등이 있어도 전체 객체의 클래스 코드를 변경하지 않으면 컴퍼지트 패턴이 유용하다. 그리고 클라이언트는 전체와 부분을 구분하지 않고 동일한 인터페이스를 사용할 수 있다.
구체적인 부분이다. 즉, Leaf 클래스와 전체에 해당하는 Composite 클래스에 공통 인터페이스를 정의한다.
구체적인 부분 클래스로 Composite 객체의 부품으로 설정한다.
전체 클래스로 복수 개의 Component를 갖도록 정의한다. 그러므로 복수 개의 Leaf, 또는 복수 개의 Composite 객체를 부분으로 가질 수 있다.