Composite Pattern
Treat individual objects and Composite of objects uniformly.
개별 객체와 복합 객체를 클라이언트에서 동일하게 사용
child가 parent에 대한 참조자를 가지면
- tree traversal이 용이해지며
- child 자신이 제거되기를 원하는 경우 편리함
children의 저장 순서가 요구되는 경우
Composite Object (복합 객체)
사용자 정의 클래스를 도메인으로 하는 속성을 가진 객체
Composite Pattern 에서는 복합 객체를 이렇게 정의한다.
Compose objects into tree structures
to represent Part-Whole (or HAS -A) hierarchies.
Let clients treat individual objects & composite objects uniformly.
복합 객체
문제점)
* composite object 와 leaf object를 동일하게 취급하고 싶다.
aPicture.draw(); - composite object 의 draw
aLine.draw(); - leaf object 의 draw
Composite Pattern 구현 두 가지 방법
Composite Pattern 1
leaf 와 composite object의 공통 연산을 가지는 interface 정의
Component 의 실제 타입은 Leaf or Composite
child는 Leaf of Composite
Component leaf 와 composite object 의 공통 인터페이스 선언
Component
interface Graphic{
public void draw(); //공통 연산
}
Leaf
class Circle implements Graphic
{
//공통 연산
public void draw() {
//자신을 그리는 코드
System.out.println("원");
}
}
Composite Object : 구성(집합체) – 위임
class CompositeGraphics implements Graphic {
//Composite object HAS parts (or children).
//List : 제네릭 타입, 제네릭 인터페이스/ Graphic: 타입실인수
private List<Graphic> children = new ArrayList<Graphic>();
//공통 연산: 실제 작업은 각 child에게 위임
public void draw() {
for (Graphic child : children) child.draw();
}
//child 조작 연산
public void add(Graphic child) { children.add(child); }
public void rem(Graphic child) { children.rem(child); }
}
Client 1-1 메인 실행
// Four leafs
Circle c1 = new Circle();
Line c2 = new Line();
Rec c3= new Rectangle();
Tri c4 = new Triangle();
// Three composite graphics
CompositeGraphic g, g1, g2;
g = new CompositeGraphic();
g1 = new CompositeGraphic();
g2 = new CompositeGraphic();
// Composes the graphics
g1.add(c1);
g1.add(c2);
g1.add(c3);
//g2-c4
g2.add(c4);
g.add(g1);
g.add(g2);
해당방법의 문제점)
DIP 설계원칙 위배 : Depend on abstractions, not on concretions.
선언타입은 상위 타입(interface, 추상class)으로 선언해야 된다.
Client 1-2 메인 실행
composite와 leaf의 타입을 모두 Component 타입으로 선언할 경우
child 조작 연산 (add, remove) 실행 時 “type casting”이 요구됨. (NOTE) 투명성 규칙에 위배됨
// Four leafs
Graphics c1, c2, c3, c4;//참조변수의 선언타입
c1 = new Circle(); //참조변수의 실제타입
c2 = new Line();
c3 = new Rectangle();
c4 = new Triangle();
// Three composite graphics
Graphics g, g1, g2;
g = new CompositeGraphic();
g1 = new CompositeGraphic();
g2 = new CompositeGraphic();
// Composes the graphics
(CompositeGraphics)g1.add(c1);
(CompositeGraphics)g1.add(c2);
(CompositeGraphics)g1.add(c3);
(CompositeGraphics)g2.add(c4);
(CompositeGraphics)g.add(g1);
(CompositeGraphics)g.add(g2);
//타입변환(downcasting)을 해줘야 호출이된다.-> 구현 클래스를 알게 된다.
method() 가 object 객체의 interface에 포함되는지(호출가능한지)
Graphics c1, c2, c3, c4;//참조변수의 선언타입
Graphics g, g1, g2;
DIP 에 따라 상위 타입(interface)로 선언했지만
(CompositeGraphics)g1.add(c1);
(CompositeGraphics)g1.add(c2);
(CompositeGraphics)g1.add(c3);
(CompositeGraphics)g2.add(c4);
(CompositeGraphics)g.add(g1);
(CompositeGraphics)g.add(g2);
강제 타입 변환이 발생
타입변환(downcasting)을 해줘야 호출이된다.-> 구현 클래스를 알게 된다.
method() 가 object 객체의 interface에 포함되는지(호출가능한지) 어떻게 알 수 있을까
선언타입(interface)으로 객체 생성 -> add 호출 불가 -> CompositeGraphics에 있기 때문에 타입 변환 필요
그렇게 되면 LSP 투명성 규칙에 위배
다형성 프로그래밍 時 클라이언트는 구현 클래스를 알 필요 없이 인터페이스를 사용할 수 있어야 한다.
LSP : 타입체킹 X (instanceof, downcasting)
Composite Pattern 2
공통 연산 + child 조작 연산을 가지는 interface 정의
Component
The Component defines an interface for all objects in the Composition : 공통연산 and Child 조작 연산
// Component
// 호출가능한 메소드 집합
// the composite and the leaf nodes (공통 연산 and child 조작 연산)
public interface Graphic
{
// 공통 연산
public void draw();
// the leaf nodes (child 조작 연산)
public void add(Graphic child);
public void remove(Graphic child);
}
Composite
The Composite also implements the Leaf-related operations
The Composite's role is to define behavior of the components having children and to store child components
import java.util.ArrayList;
import java.util.List;
// Composite Object : 구성(Aggregation) , 위임
public class CompositeGraphics implements Graphic
{
// Composite object Has Parts or Children
private List<Graphic> children = new ArrayList<>();
// 공통연산
@Override
public void draw() {
for(Graphic child : children){
child.draw();
}
}
@Override
public void add(Graphic child) {
children.add(child);
}
@Override
public void remove(Graphic child) {
children.remove(child);
}
}
Leaf
A Leaf defines the behavior for the elements in the Composition.
It does this by implementing the operations the Composite supports
// Leaf
// individual objects as nodes
public class Circle implements Graphic
{
@Override
public void draw() {
System.out.println("원");
}
// they don’t have to do anything,
// they can just inherit the
// default implementation.
// Sometimes the best you can do is throw a runtime exception.
@Override
public void add(Graphic child) {throw new UnsupportedOperationException(); }
@Override
public void remove(Graphic child) {throw new UnsupportedOperationException(); }
}
public class Line implements Graphic {
@Override
public void draw() {
System.out.println("라인");
}
@Override
public void add(Graphic child) {throw new UnsupportedOperationException();}
@Override
public void remove(Graphic child) { throw new UnsupportedOperationException(); }
}
public class Rectangle implements Graphic {
@Override
public void draw() {
System.out.println("직사각형");
}
@Override
public void add(Graphic child) { throw new UnsupportedOperationException(); }
@Override
public void remove(Graphic child) { throw new UnsupportedOperationException(); }
}
public class Triangle implements Graphic {
@Override
public void draw() {
System.out.println("트라이앵글");
}
@Override
public void add(Graphic child) { throw new UnsupportedOperationException(); }
@Override
public void remove(Graphic child) { throw new UnsupportedOperationException(); }
}
Client 2
// Four leafs
Graphics c1, c2, c3, c4;//선언타입
c1 = new Circle(); // 실제타입
c2 = new Line();
c3 = new Rectangle();
c4 = new Triangle();
// Three composite graphics
Graphics g, g1, g2;
g = new CompositeGraphic();
g1 = new CompositeGraphic();
g2 = new CompositeGraphic();
// Composes the graphics
g1.add(c1);
g1.add(c2);
g1.add(c3);
g2.add(c4);
g.add(g1);
g.add(g2);
실행결과
지원하지 않는 leaf 의 연산에 대해 디버그시 예외 처리하게 된다.
LSP 해결 :
subtype 추가 메소드를 상위타입에도 추가
leaf에는 ReV: null false, throw new UnsupportedOperationException();
댓글