상위문서: GoF 디자인 패턴
객체를 component와 composite로 구성하여 트리 구조로 구성하여 표현하는 전체-부분 패턴으로, 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 한다.
Directory-File이 대표적인 예이다.
위 마름표 화살표 표시는 합성관계를 나타낸다.
합성 관계 :
생성자에서 필드에 대한 객체를 생성하는 경우
전체 객체의 라이프타임과 부분 객체의 라이프 타임은 의존적이다.
즉, 전체 객체(마름모가 표시된 클래스)가 없어지면 부분 객체도 없어진다.
// 구성장치 class
public class 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 { 동일한 구조 }
public class Monitor { 동일한 구조 }
// Computer class
public class Computer {
private Keyboard Keyboard;
private Body body;
private Monitor monitor;
public addKeyboard(Keyboard keyboard) { this.keyboard = keyboard; }
public addBody(Body body) { this.body = body; }
public addMonitor(Monitor monitor) { this.monitor = monitor; }
public int getPrice() {
int keyboardPrice = keyboard.getPrice();
int bodyPrice = body.getPrice();
int monitorPrice = monitor.getPrice();
return keyboardPrice + bodyPrice + monitorPrice;
}
public int getPower() {
int keyboardPower = keyboard.getPower();
int bodyPower = body.getPower();
int monitorPower = monitor.getPower();
return keyboardPower + bodyPower + monitorPower;
}
}
// main
public static void main(String[] args) {
// 컴퓨터의 부품으로 Keyboard, Body, Monitor 객체를 생성
Keyboard keyboard = new Keyboard(5, 2);
Body body = new Body(100, 70);
Monitor monitor = new Monitor(20, 30);
// Computer 객체를 생성하고 부품 객체들을 설정
Computer computer = new Computer();
computer.addKeyboard(keyboard);
computer.addBody(body);
computer.addMonitor(monitor);
// 컴퓨터의 가격과 전력 소비량을 구함
int computerPrice = computer.getPrice();
int computerPower = computer.getPower();
System.out.println("Computer Price: " + computerPrice + "만원");
System.out.println("Computer Power: " + computerPower + "W");
}
// 결과
// Computer Price: 102만원
// Computer Power: 120W
만약 다른 부품이 추가되는 경우에는?
Speaker라는 장치를 추가해보자
// Speaker class
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 class
public class Computer {
. . .
private Speaker speaker; // 추가
. . .
public addSpeaker(Speaker speaker) { this.speaker = speaker; } // 추가
public int getPrice() {
. . .
int speakerPrice = speaker.getPrice(); // 추가
return keyboardPrice + bodyPrice + monitorPrice + speakerPrice;
}
public int getPower() {
. . .
int speakerPower = speaker.getPower(); // 추가
return keyboardPower + bodyPower + monitorPower + speakerPower;
}
}
위와 같은 방법으로 추가하게 되면 확장성에 좋지 않다. 즉, OCP를 만족하지 않는다.
문제점의 핵심은 Computer 클래스에 속한 부품의 구체적인 객체를 가리키면 OCP를 위반하게 된다는 것이다.
OCP : Open-Closed Principle
'소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다' 는 프로그래밍 원칙. 객체 지향 프로그래밍의 핵심 원칙이라고 할 수 있다. 이 원칙을 무시한다면 OOP의 가장 큰 장점인 유연성, 재사용성, 유지보수성을 결코 얻을 수 없다.
구체적인 부품들을 일반화한 클래스를 정의하고 이를 Computer 클래스가 가리키도록 설계한다.
// ComputerDevice : Component
public abstract class ComputerDevice {
public abstract int getPrice();
public abstract int getPower();
}
// 구성 장치들 : Components
public class Speaker extends ComputerDevice {
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; }
}
public class Keyboard { 동일한 구조 }
public class Body { 동일한 구조 }
public class Monitor { 동일한 구조 }
// Computer class : 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;
}
}
// main
public static void main(String[] args) {
// 컴퓨터의 부품으로 Keyboard, Body, Monitor 객체를 생성
Keyboard keyboard = new Keyboard(5, 2);
Body body = new Body(100, 70);
Monitor monitor = new Monitor(20, 30);
Speaker speaker = new Speaker(10, 10);
// Computer 객체를 생성하고 addComponent()를 통해 부품 객체들을 설정
Computer computer = new Computer();
computer.addComponent(keyboard);
computer.addComponent(body);
computer.addComponent(monitor);
computer.addComponent(speaker);
// 컴퓨터의 가격과 전력 소비량을 구함
int computerPrice = computer.getPrice();
int computerPower = computer.getPower();
System.out.println("Computer Price: " + computerPrice + "만원");
System.out.println("Computer Power: " + computerPower + "W");
}
// 결과
// Computer Price: 112만원
// Computer Power: 130W