시그마 삽질==six 시그마

컴퍼지트 패턴(Composite pattern) 본문

프로그래밍/디자인패턴

컴퍼지트 패턴(Composite pattern)

Ethan Matthew Hunt 2020. 4. 24. 23:46

 

'Java 객체 지향 디자인 패턴' 책을 구입하시길  추천드립니다.

책 구입을 원하시는분은 요기를 클릭하시면 됩니다.

하단의 내용은 제가 예전에 읽었던 내용을 요약 정리한 것입니다.

 

In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

Source: https://en.wikipedia.org/wiki/Composite_pattern

https://en.wikipedia.org/wiki/Composite_pattern

 

컴퍼지트 패턴은 부분-전체의 관계를 갖는 객체들을 정의할때 사용하는 패턴이다

 

Component: 구체적인 부분, 즉 Leaf 클래스와 전체에 해당하는 Composite 클래스에 공통 인터페이스를 정의한다

Leaf: 구체적인 부분 클래스로 Composite 객체의 부품으로 설정한다

Composite: 전체 클래스로 복수개의 Component를 갖도록 정의한다

 

컴퓨터의 전력량을 계산하는 로직이다

public class Computer {
	private Body body ;
	private Keyboard keyboard ;
	private Monitor monitor ;
	
	public void addBody(Body body) { this.body = body ; }
	public void addKeyboard(Keyboard keyboard) { this.keyboard = keyboard ; }
	public void addMonitor(Monitor monitor) { this.monitor = monitor ; }
	public int getPrice() {
		int bodyPrice = body.getPrice() ;
		int keyboardPrice = keyboard.getPrice() ;
		int monitorPrice = monitor.getPrice() ;
		
		return bodyPrice + keyboardPrice + monitorPrice ;
	}
	public int getPower() {
		int bodyPower = body.getPower() ;
		int keyboardPower = keyboard.getPower() ;
		int monitorPower = monitor.getPower() ;
		
		return bodyPower + keyboardPower + monitorPower ;
	}
}


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


public class Client {
	public static void main(String[] args) {
		// 컴퓨터의 부품으로 Body, Keyboard, Monitor 객체를 생성함
		Body body = new Body(100, 70) ;
		Keyboard keyboard = new Keyboard(5, 2) ;
		Monitor monitor = new Monitor(20, 30) ;
		
		// Computer 객체를 생성하고 부품 객체들을 설정함
		Computer computer = new Computer() ;
		computer.addBody(body) ;
		computer.addKeyboard(keyboard) ;
		computer.addMonitor(monitor) ;

		//컴퓨터의 가격과 전력 소비량을 구함
		int computerPrice = computer.getPrice() ;
		int computerPower = computer.getPower() ;
		
		System.out.println("Computer Power: " + computerPower + " W") ;
		System.out.println("Computer Price: " + computerPrice + " 만원") ;	
	}
}

 

만약 요기서 컴퓨터 클래스의 부품으로 Speaker 클래스를 추가한다면? 또는 Mouse 클래스를 추가한다면?

새로운 객체 참조를 컴퓨터 클래스에 추가하고, setter 메서드인 add 메서드 추가,getPrice 메서드 추가해야함

 

문제의 핵심은 Computer 클래스에 속한 부품의 구체적인 객체를 가리키게 되면 OCP를 위반하게 됨

즉 Computer 클래스가 Monitor,Body, Keyboard,Speaker 객체들을 직접 가리키면 이러한 부품의 변화에 따라 Computer 클래스의 코드도 변할 수 밖에 없음

그러므로 구체적인 부품들을 일반화한 클래스를 정의하고 이를 Computer 클래스가 가리키게 하는것이 올바른 설계임

 

 

public abstract class ComputerDevice {
	public abstract int getPrice() ;
	public abstract int getPower() ;

}


public class Computer extends ComputerDevice {
	private List<ComputerDevice> components = new ArrayList<ComputerDevice>() ;

	public void addComponent(ComputerDevice component) {
		components.add(component) ;
	}
	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 ;
	}
}


public class Body extends ComputerDevice {
	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 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;
	}
}


public class Monitor extends ComputerDevice {
	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;
	}
}



public class Client {
	public static void main(String[] args) {
		Computer computer = new Computer() ;
		Body body = new Body(100, 70) ;
		Keyboard keyboard = new Keyboard(5, 2) ;
		Monitor monitor = new Monitor(20, 30) ;
		
		computer.addComponent(body) ;
		computer.addComponent(keyboard) ;
		computer.addComponent(monitor) ;
		
		int computerPrice = computer.getPrice() ;
		int computerPower = computer.getPower() ;
		
		System.out.println("Computer Power: " + computerPower + " W") ;
		System.out.println("Computer Price: " + computerPrice + " 만원") ;	
	}
}

 

 

 

 

 

컴포지트 패턴의 단골 예시는 디렉토리와 파일이다

 

public abstract class AbstractFile {
	private String name ;
	private int depth = 0 ;
	
	public AbstractFile(String name) { this.name = name ; }
	public String getName() { return name; }
	public void setDepth(int depth) { this.depth = depth ; }
	public int getDepth() { return depth ; }

	public abstract int getSize() ;
	public abstract void print() ;
}


public class Directory extends AbstractFile {
	private List<AbstractFile> files = new ArrayList<AbstractFile>();

	public Directory( String name ) { super(name) ; } 
	public void addEntry(AbstractFile entry) {
		entry.setDepth(getDepth()+1) ;
		files.add(entry) ;	
	}
	public void removeEntry(AbstractFile entry) {
		files.remove(entry) ;
	}
	public int getSize() {
		int totalSize = 0 ;
		for (AbstractFile entry: files) totalSize += entry.getSize();
		return totalSize;
	}
	public void print() {
		for ( int i = 0 ; i < getDepth() ; i ++ )
			System.out.print("\t") ;
		System.out.println("[Directory] " + getName() + ", Size: " + getSize()) ;	
		for ( AbstractFile entry: files ) entry.print() ;
	}
}


public class File extends AbstractFile {
	private int size ;
	
	public File( String name, int size ) {
		super(name) ;
		this.size = size ;
	} 
	public int getSize() { return size ; }
	public void print() {
		for ( int i = 0 ; i < getDepth() ; i ++ )
			System.out.print("\t") ;
		System.out.println("[File] " + getName() + ", Size: " + size) ;	
	}
}


public class Client {
	public static void main(String[] args) {
		Directory dir1 = new Directory("root") ;
		Directory dir2 = new Directory("Dir1") ;
		
		File f1 = new File("f1", 100) ;
		File f2 = new File("f2", 200) ;
		File f3 = new File("f3", 300) ;
		File f4 = new File("f4", 400) ;
		
		dir1.addEntry(f1) ;
		dir1.addEntry(dir2) ;
		dir2.addEntry(f2) ;
		dir2.addEntry(f3) ;
		dir1.addEntry(f4) ;

		dir1.print() ;
	}
}

 

'프로그래밍 > 디자인패턴' 카테고리의 다른 글

Facade pattern  (0) 2020.04.30
옵저버 패턴(Observer Pattern)  (0) 2020.04.25
데코레이터 패턴(Decorator pattern)  (0) 2020.04.24
스테이트 패턴(State pattern)  (0) 2020.04.24
전략 패턴(Strategy Pattern)  (0) 2020.04.24
Comments