Compsite pattern enables a single object and a list of objects to operate the same way. That way the client does not need to know if it is using a single object or a list, while using it. So the usage becomes simple in that case.
![](https://bigboxcode.com/wp-content/uploads/2022/12/Composite-Pattern-Composition-Diagram-v3-1024x433.png)
This article describes Composite pattern implementation in TypeScript. Check the implementation details.
Implementation
To implement the Composite pattern, make sure the item/leaf classes using the same interface, then we can implement the composite class using the steps below-
- Create a composite class.
- Implement the same interface that the items are implementing. That way, the same interface in ensured.
- Store a list of items in a private property.
- When performing operations, perform that on the item list in a loop.
- Implement methods for adding and removing items from the list.
Here is a simple example of Composite pattern-
// Composite pattern example in TypeScript
// Item interface
interface Item {
operation1(): void;
operation2(): void;
}
// First item class
class Item1 implements Item {
operation1(): void {
console.log('Item 1: operation 1');
}
operation2(): void {
console.log('Item 1: operation 2');
}
}
// Second item class
class Item2 implements Item {
operation1(): void {
console.log('Item 2: operation 1');
}
operation2(): void {
console.log('Item 2: operation 2');
}
}
// Composite class
class ItemGroup implements Item {
private itemList: Item[] = [];
operation1(): void {
for (let item of this.itemList) {
item.operation1();
}
}
operation2(): void {
for (let item of this.itemList) {
item.operation2();
}
}
addItem(item: Item): void {
this.itemList.push(item);
}
removeItem(item: Item): void {
this.itemList.splice(this.itemList.indexOf(item), 1);
}
}
// Demo
const item1 = new Item1();
const item2 = new Item2();
const anotherItem1 = new Item1();
const itemGroup = new ItemGroup();
itemGroup.addItem(item1);
itemGroup.addItem(item2);
itemGroup.addItem(anotherItem1);
itemGroup.operation1();
itemGroup.operation2();
Output generated by the code above is –
Item 1: operation 1
Item 2: operation 1
Item 1: operation 1
Item 1: operation 2
Item 2: operation 2
Item 1: operation 2
Examples
Here are a few Composite pattern examples-
Example #1: Transport List
For this example, we want to use a list of transport vehicles and single vehicle the same way.
Transport Interface [Item Interface]
- Create a file “transport.ts”.
- Create interface “Transport”.
- Declare methods “start”, “stop”, “operate” in the interface.
// transport.ts
interface Transport {
start(): void;
operate(): void;
stop(): void;
}
export default Transport;
Bike Class [Item Class]
- Create a file “bike.ts”.
- Create class “Bike”.
- Implement “Transport” interface for the class. Define method – “start”, “stop”, “operate” as part of the interface implementation.
// bike.ts
import Transport from "./transport";
class Bike implements Transport {
start(): void {
console.log("Starting Bike...");
}
operate(): void {
console.log("Riding Bike");
}
stop(): void {
console.log("Stopping Bike...");
}
}
export default Bike;
Plane Class [Item Class]
- Create a file “plane.ts”.
- Create class “Plane”.
- Implement “Transport” interface for “Plane” class.
// plane.ts
import Transport from "./transport";
class Plane implements Transport {
start(): void {
console.log("Starting Plane...");
}
operate(): void {
console.log("Flying Plane");
}
stop(): void {
console.log("Stopping Plane...");
}
}
export default Plane;
Car Class [Item Class]
- Create a file “car.ts”.
- Create class “Car”.
- Implement “Transport” interface for the “Car” class.
// car.ts
import Transport from "./transport";
class Car implements Transport {
start(): void {
console.log("Starting Car...");
}
operate(): void {
console.log("Driving Car");
}
stop(): void {
console.log("Stopping Car...");
}
}
export default Car;
Composite Class
- Create a file “transport-group.ts”.
- Create class “TransportGroup”.
- Define a private property to store a list of transports.
- Implement “Transport” interface for the “TransportGroup” class. In the method implementation call methods from the individual transport in a loop.
- Define method for adding and removing transports to the list.
// transport-group.ts
import Transport from "./transport";
class TransportGroup implements Transport {
private transportList: Transport[] = [];
start(): void {
for (let transport of this.transportList) {
transport.start();
}
}
operate(): void {
for (let transport of this.transportList) {
transport.operate();
}
}
stop(): void {
for (let transport of this.transportList) {
transport.stop();
}
}
addTransport(transport: Transport): void {
this.transportList.push(transport);
}
removeTransport(transport: Transport): void {
this.transportList.splice(this.transportList.indexOf(transport), 1);
}
}
export default TransportGroup;
Demo
To use the composite transport group, use the add method and pass transport object, to add to the list. Then call the methods on the group object.
// demo.ts
import Bike from "./bike";
import Car from "./car";
import Plane from "./plane";
import TransportGroup from "./transport-group";
const bike = new Bike();
const plane = new Plane();
const car = new Car();
const secondCar = new Car();
const transports = new TransportGroup();
transports.addTransport(bike);
transports.addTransport(plane);
transports.addTransport(car);
transports.addTransport(secondCar);
console.log("-----------------Output with 4 transports------------------n");
transports.start();
transports.operate();
transports.stop();
console.log("n-----------------Output when plane is removed------------------n");
transports.removeTransport(plane);
transports.start();
transports.operate();
transports.stop();
Output
The output of the demo above will be like below.
-----------------Output with 4 transports------------------
Starting Bike...
Starting Plane...
Starting Car...
Starting Car...
Riding Bike
Flying Plane
Driving Car
Driving Car
Stopping Bike...
Stopping Plane...
Stopping Car...
Stopping Car...
-----------------Output when plane is removed------------------
Starting Bike...
Starting Car...
Starting Car...
Riding Bike
Driving Car
Driving Car
Stopping Bike...
Stopping Car...
Stopping Car...
Example #2: Player Group
Here we are considering players for different types of sports. We will create a composite class so that we can perform operations on a bunch of players all together.
Player Interface [Item Interface]
- Create a file “player.ts”.
- Create interface “Player”.
- Declare method for the interface. Here we have method – “printDetails”, which is used to print full details of the player.
// player.ts
interface Player {
printDetails(): void;
}
export default Player;
Basketball Player Class [Item Class]
- Create a file “basketball-player.ts”.
- Create interface “BasketballPlayer”.
- Define private property – “name”, “age”, and “point”. In the constructor, accept the property values and set that.
- Implement “Player” interface for the class.
// basketball-player.ts
import Player from "./player";
class BasketballPlayer implements Player {
private name: string;
private age: number;
private point: number;
constructor(name: string, age: number, point: number) {
this.name = name;
this.age = age;
this.point = point;
}
printDetails(): void {
console.log("Game: Basketball");
console.log("Name: " + this.name);
console.log("Age: " + this.age);
console.log("Points: " + this.point);
}
}
export default BasketballPlayer;
Football Player Class [Item Class]
- Create a file “football-player.ts”.
- Create interface “FootballPlayer”.
- Define private property – “name”, “age”, and “goal”. In the constructor, accept the property values and set that.
- Implement “Player” interface for the class.
// football-player.ts
import Player from "./player";
class FootballPlayer implements Player {
private name: string;
private age: number;
private goal: number;
constructor(name: string, age: number, goal: number) {
this.name = name;
this.age = age;
this.goal = goal;
}
printDetails() {
console.log("Game: Football");
console.log("Name: " + this.name);
console.log("Age: " + this.age);
console.log("Goals: " + this.goal);
}
}
export default FootballPlayer;
Cricket Player Class [Item Class]
- Create a file “cricket-player.ts”.
- Create interface “CricketPlayer”.
- Define private property – “name”, “age”, and “run”. In the constructor, accept and set property values.
- Implement “Player” interface for the class. Define “printDetails” method as part of interface implementation.
// cricket-player.ts
import Player from "./player";
class CricketPlayer implements Player {
private name: string;
private age: number;
private run: number;
constructor(name: string, age: number, run: number) {
this.name = name;
this.age = age;
this.run = run;
}
printDetails(): void {
console.log("Game: Cricket");
console.log("Name: " + this.name);
console.log("Age: " + this.age);
console.log("Runs: " + this.run);
}
}
export default CricketPlayer;
Player Group
- Create file “player-group.ts”.
- Create class “PlayerGroup”.
- Define private property “playerList” which is an array of type “Player”.
- Implement “Player” interface for “PlayerGroup”. In the method implementation call the methods from individual player object in a loop.
- Implement “addElement” and “removeElement” for managing the list of players.
// player-group.ts
import Player from "./player";
class PlayerGroup implements Player {
private playerList: Player[] = [];
printDetails(): void {
for (let player of this.playerList) {
player.printDetails();
}
}
addElement(transport: Player): void {
this.playerList.push(transport);
}
removeElement(transport: Player): void {
this.playerList.splice(this.playerList.indexOf(transport), 1);
}
}
export default PlayerGroup;
Demo
Create a bunch of player objects, and add those to the group. Now we can perform operations on the list of players.
We can also create a group of other players groups. Check the demo/example below.
// demo.ts
import BasketballPlayer from "./basketball-player";
import CricketPlayer from "./cricket-player";
import FootballPlayer from "./football-player";
import PlayerGroup from "./player-group";
// Under 15 players
const under15Players = new PlayerGroup();
under15Players.addElement(new FootballPlayer("FPlayer 15_1", 13, 23));
under15Players.addElement(new FootballPlayer("FPlayer 15_2", 14, 30));
under15Players.addElement(new BasketballPlayer("BPlayer 15_1", 12, 80));
under15Players.addElement(new BasketballPlayer("BPlayer 15_2", 14, 100));
under15Players.addElement(new CricketPlayer("CPlayer 15_1", 14, 467));
// Under 19 Players
const under19Players = new PlayerGroup();
under19Players.addElement(new FootballPlayer("FPlayer 19_1", 18, 43));
under19Players.addElement(new BasketballPlayer("BPlayer 19_1", 17, 77));
under19Players.addElement(new CricketPlayer("CPlayer 19_1", 18, 654));
under19Players.addElement(new CricketPlayer("CPlayer 19_2", 16, 789));
// National team players
const nationalTeamPlayers = new PlayerGroup();
nationalTeamPlayers.addElement(new FootballPlayer("FPlayer N_1", 18, 43));
nationalTeamPlayers.addElement(new BasketballPlayer("BPlayer N_1", 17, 77));
nationalTeamPlayers.addElement(new CricketPlayer("CPlayer N_1", 18, 654));
// Create a group with all teams
const allTeams = new PlayerGroup();
allTeams.addElement(under15Players);
allTeams.addElement(under19Players);
allTeams.addElement(nationalTeamPlayers);
// Print details of all players
// from each game and group
allTeams.printDetails();
Output
We will get the following output-
Game: Football
Name: FPlayer 15_1
Age: 13
Goals: 23
Game: Football
Name: FPlayer 15_2
Age: 14
Goals: 30
Game: Basketball
Name: BPlayer 15_1
Age: 12
Points: 80
Game: Basketball
Name: BPlayer 15_2
Age: 14
Points: 100
Game: Cricket
Name: CPlayer 15_1
Age: 14
Runs: 467
Game: Football
Name: FPlayer 19_1
Age: 18
Goals: 43
Game: Basketball
Name: BPlayer 19_1
Age: 17
Points: 77
Game: Cricket
Name: CPlayer 19_1
Age: 18
Runs: 654
Game: Cricket
Name: CPlayer 19_2
Age: 16
Runs: 789
Game: Football
Name: FPlayer N_1
Age: 18
Goals: 43
Game: Basketball
Name: BPlayer N_1
Age: 17
Points: 77
Game: Cricket
Name: CPlayer N_1
Age: 18
Runs: 654
Source Code
Use the following link to get the source code:
Other Code Implementations
Use the following links to check Composite pattern implementation in other programming languages.