CS_Step13 - 컴포지트 패턴(Composite Pattern)

장선웅·2022년 7월 26일
0

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;
    }
 }

이처럼 설계를 한다면 확장성이 떨어지게 되고(개방-폐쇄의 원칙에 위배), 새로운 부품을 추가할 때마다 계속해서 코드를 수정해야한다.


4. 컴포지트 패턴 구현 방법

  1. 구체적인 부품들을 일반화한 ComputerDevice 클래스 정의( = Component )
abstract class ComputerDevice {
	public abstract int getPrice();
    public abstract int getPower();
}
  1. 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;
    }
 }
  1. 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;
  }
}
  1. 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

5. 컴포지트 패턴의 장/단점

장점

  1. 객체들이 모두 같은 타입으로 취급된다 => 새로운 클래스 추가가 용이하다
  2. 단일 객체, 복합 객체의 구분 없이 코드 작성이 가능하다.

단점

  1. 설계를 일반화한다 => 객체들간의 구분, 제약이 어렵다
profile
개발을 꿈꾸는 초짜

0개의 댓글