15 minlesson

Composite Operations and Traversal

Composite Operations and Traversal

Common Operations on Composites

Most composite operations fall into these categories:

Aggregation Operations

typescript
1interface ShippingUnit {
2 // Aggregation - sum of all children
3 getWeight(): number;
4 getItemCount(): number;
5 getTotalValue(): number;
6}
7
8class Container implements ShippingUnit {
9 getTotalValue(): number {
10 return this.children.reduce(
11 (sum, child) => sum + child.getTotalValue(),
12 0
13 );
14 }
15}
16
17class Item implements ShippingUnit {
18 getTotalValue(): number {
19 return this.price;
20 }
21}

Search Operations

typescript
1interface ShippingUnit {
2 find(predicate: (unit: ShippingUnit) => boolean): ShippingUnit | undefined;
3 findAll(predicate: (unit: ShippingUnit) => boolean): ShippingUnit[];
4}
5
6class Container implements ShippingUnit {
7 find(predicate: (unit: ShippingUnit) => boolean): ShippingUnit | undefined {
8 if (predicate(this)) return this;
9
10 for (const child of this.children) {
11 const found = child.find(predicate);
12 if (found) return found;
13 }
14 return undefined;
15 }
16
17 findAll(predicate: (unit: ShippingUnit) => boolean): ShippingUnit[] {
18 const results: ShippingUnit[] = [];
19 if (predicate(this)) results.push(this);
20
21 for (const child of this.children) {
22 results.push(...child.findAll(predicate));
23 }
24 return results;
25 }
26}
27
28// Usage
29const fragileItems = order.findAll(unit =>
30 unit instanceof Item && unit.isFragile
31);

Validation Operations

typescript
1interface ShippingUnit {
2 validate(): ValidationResult;
3}
4
5class Container implements ShippingUnit {
6 validate(): ValidationResult {
7 const errors: string[] = [];
8
9 // Container-level validation
10 if (this.getWeight() > this.maxWeight) {
11 errors.push(`Container exceeds max weight of ${this.maxWeight}kg`);
12 }
13
14 // Validate all children
15 for (const child of this.children) {
16 const childResult = child.validate();
17 errors.push(...childResult.errors);
18 }
19
20 return {
21 isValid: errors.length === 0,
22 errors
23 };
24 }
25}

Traversal Strategies

Depth-First (Default)

typescript
1class Container {
2 // Pre-order: visit node before children
3 traversePreOrder(callback: (unit: ShippingUnit) => void): void {
4 callback(this);
5 for (const child of this.children) {
6 if (child instanceof Container) {
7 child.traversePreOrder(callback);
8 } else {
9 callback(child);
10 }
11 }
12 }
13
14 // Post-order: visit node after children
15 traversePostOrder(callback: (unit: ShippingUnit) => void): void {
16 for (const child of this.children) {
17 if (child instanceof Container) {
18 child.traversePostOrder(callback);
19 } else {
20 callback(child);
21 }
22 }
23 callback(this);
24 }
25}

Breadth-First

typescript
1function traverseBreadthFirst(
2 root: ShippingUnit,
3 callback: (unit: ShippingUnit, depth: number) => void
4): void {
5 const queue: Array<{ unit: ShippingUnit; depth: number }> = [
6 { unit: root, depth: 0 }
7 ];
8
9 while (queue.length > 0) {
10 const { unit, depth } = queue.shift()!;
11 callback(unit, depth);
12
13 if (unit instanceof Container) {
14 for (const child of unit.getChildren()) {
15 queue.push({ unit: child, depth: depth + 1 });
16 }
17 }
18 }
19}
20
21// Usage: print level by level
22traverseBreadthFirst(order, (unit, depth) => {
23 console.log(`Level ${depth}: ${unit.id || unit.sku}`);
24});

Path Operations

typescript
1interface ShippingUnit {
2 getPath(): string[];
3 findPath(target: ShippingUnit): string[] | null;
4}
5
6class Container implements ShippingUnit {
7 findPath(
8 target: ShippingUnit,
9 currentPath: string[] = []
10 ): string[] | null {
11 const path = [...currentPath, this.id];
12
13 if (this === target) return path;
14
15 for (const child of this.children) {
16 if (child === target) {
17 return [...path, child instanceof Item ? child.sku : child.id];
18 }
19
20 if (child instanceof Container) {
21 const found = child.findPath(target, path);
22 if (found) return found;
23 }
24 }
25
26 return null;
27 }
28}
29
30// Usage
31const item = order.find(u => u instanceof Item && u.sku === 'LAPTOP-001');
32const path = order.findPath(item);
33// ['ORDER-123', 'BOX-medium', 'LAPTOP-001']

Cloning Composites

typescript
1interface ShippingUnit {
2 clone(): ShippingUnit;
3}
4
5class Container implements ShippingUnit {
6 clone(): Container {
7 const cloned = new Container(
8 this.id + '-copy',
9 this.containerWeight,
10 { ...this.containerDimensions }
11 );
12
13 for (const child of this.children) {
14 cloned.add(child.clone());
15 }
16
17 return cloned;
18 }
19}
20
21class Item implements ShippingUnit {
22 clone(): Item {
23 return new Item(
24 this.sku,
25 this.weight,
26 { ...this.dimensions }
27 );
28 }
29}

Composite with Visitor

typescript
1interface ShippingVisitor {
2 visitItem(item: Item): void;
3 visitBox(box: Box): void;
4 visitPallet(pallet: Pallet): void;
5}
6
7class WeightCalculator implements ShippingVisitor {
8 private totalWeight = 0;
9
10 visitItem(item: Item): void {
11 this.totalWeight += item.getWeight();
12 }
13
14 visitBox(box: Box): void {
15 this.totalWeight += box.getBoxWeight();
16 }
17
18 visitPallet(pallet: Pallet): void {
19 this.totalWeight += pallet.getPalletWeight();
20 }
21
22 getTotal(): number {
23 return this.totalWeight;
24 }
25}
26
27// Composite accepts visitor
28class Container {
29 accept(visitor: ShippingVisitor): void {
30 visitor.visitBox(this);
31 for (const child of this.children) {
32 child.accept(visitor);
33 }
34 }
35}

Summary

Operation TypeUse Case
AggregationTotals, counts, sums
SearchFind items matching criteria
ValidationCheck constraints at all levels
TraversalProcess all nodes in order
PathLocate items in hierarchy
CloneDeep copy entire structure

Composite's power comes from applying operations recursively across the entire tree structure.

Composite Operations and Traversal - Anko Academy