앱의 핵심 모델이 트리로 표현 가능할때만 사용할것
// 여러 요소들 전부에 적용될 수 있는 공통 요소들을 정의하는 추상 클래스
abstract class Component {
protected parent!: Component | null;
public setParent(parent: Component | null) {
this.parent = parent;
}
public getParent(): Component | null {
return this.parent;
}
public add(component: Component): void { }
public remove(component: Component): void { }
public isComposite(): boolean {
return false;
}
public abstract operation(): string;
}
// 트리에 사용될 기본 요소로 하위요소 없이 실제 작업을 수행함
class Leaf extends Component {
public operation(): string {
return 'Leaf';
}
}
/* composite에서 실제 객체 정의
* Leaf 또는 다른 Component를 자식으로 가짐
* 부모 클래스는 자식 클래스가 무엇인지를 알지 못하고, 컴포넌트 인터페이스를 통해서만 하위 요소들과 작동
*/
class Composite extends Component {
protected children: Component[] = [];
// 자식 컴포넌트 가져오기
public add(component: Component): void {
this.children.push(component);
component.setParent(this);
}
// 자식 컴포넌트 삭제
public remove(component: Component): void {
const componentIndex = this.children.indexOf(component);
this.children.splice(componentIndex, 1);
component.setParent(null);
}
public isComposite(): boolean {
return true;
}
// 연산을 실행하면 세부 사항은 하위 요소가 처리하도록 하고 처리 결과를 모아 반환함
public operation(): string {
const results = [];
for (const child of this.children) {
results.push(child.operation());
}
return `Branch(${results.join('+')})`;
}
}
// 추상 클래스를 따르는 객체들에서 클라이언트 코드가 작동
function clientCode(component: Component) {
console.log(`RESULT: ${component.operation()}`);
// ...
}
const simple = new Leaf();
console.log('Client: I\'ve got a simple component:');
clientCode(simple); // RESULT: Leaf
console.log('');
const tree = new Composite();
const branch1 = new Composite();
branch1.add(new Leaf());
branch1.add(new Leaf());
const branch2 = new Composite();
branch2.add(new Leaf());
tree.add(branch1);
tree.add(branch2);
console.log('Client: Now I\'ve got a composite tree:');
clientCode(tree); //RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf))
console.log('');
// child 관리가 추상 클래스에서 이미 정의되어있기 때문에 구상 클래스 구현과 관계없이 인터페이스를 구현한 컴포넌트끼리의 결합이 가능해짐
function clientCode2(component1: Component, component2: Component) {
if (component1.isComposite()) {
component1.add(component2);
}
console.log(`RESULT: ${component1.operation()}`);
// ...
}
console.log('Client: I don\'t need to check the components classes even when managing the tree:');
clientCode2(tree, simple); //RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf)+Leaf)