Design Pattern Examples
Overview of object-oriented design patterns
Composite Pattern

Diagram of the composite pattern

A composite design is used for a hierarchical list of items where some items are containers for additional items. The root of the list is always a container. The hierarchical list is a composite of all the node types in the list. The program deals with all the nodes as though they were the same, as represented by the base class.

A common example of this is a file/directory structure. A directory contains entries that can be a file or another directory. The root of the structure is a directory.

How to Use

Links to the Composite classes
C++ C# Python C
FileDirEntry base class FileDirEntry base class FileDirEntry base class FileDirEntry structure
FileEntry derived class FileEntry derived class FileEntry derived class FileEntry structure
DirEntry derived class DirEntry derived class DirEntry derived class DirEntry structure

This example uses a file/directory approach, although the files and directories are hardcoded examples. Each entry, be it a file or a directory, is represented by the FileDirEntry base class. Each entry has a name, a length, and a date when it was last modified. For directories, the length is the sum of all child files and directories contained within.

The type of the entry is determined by the FileDirType property/getter. If the entry is a directory, then it might have child entries in the Children property/getter.

Note: In a flat list, getting to any entry is straightforward: Just provide the index to the entry. For hierarchical lists, however, a "path" must be provided to get to any entry. This "path" describes all the entries that must be passed through to get to the desired entry, starting with the root entry.

For example, a container called "root" contains a child entry called "itemcontainer1" that is also a container. The "itemcontainer1" entry contains an entry called "item2" that is not a container. The "path" to the "item2" entry must pass through "root" and "itemcontainer1". This "path" can be written in any number of ways, with a common approach separating the names of each entry by '/' like this: "/root/itemcontainer1/item2". This should look familiar as it is very much like a file path in an operating system with a hierarchical file structure.

C++

void Composite_Exercise()
{
std::cout << std::endl;
std::cout << "Composite Exercise" << std::endl;
try
{
std::string filepath = "root";
FileDirEntry* rootEntry = Composite_FileAccess::GetEntry(filepath);
std::cout << " Showing object '" << filepath << "'" << std::endl;
filepath = "root/subdir1/FileD.txt";
rootEntry = Composite_FileAccess::GetEntry(filepath);
std::cout << " Showing object '" << filepath << "'" << std::endl;
}
catch (std::filesystem::filesystem_error& e)
{
std::cout << "Error! filesystem_error: " << e.what() << std::endl;
}
std::cout << " Done." << std::endl;
}
static void Composite_Exercise_ShowEntry(FileDirEntry *entry)
Recursively display the contents of the hierarchical list of FileDirEntry objects starting with the g...
Structure representing a File (FileEntry) or Directory (DirEntry) entry. This is included as the firs...

C#

public void Run()
{
Console.WriteLine();
Console.WriteLine("Composite Exercise");
try
{
string filepath = "root";
FileDirEntry rootEntry = Composite_FileAccess.GetEntry(filepath);
Console.WriteLine(" Showing object '{0}'", filepath);
filepath = "root/subdir1/FileD.txt";
rootEntry = Composite_FileAccess.GetEntry(filepath);
Console.WriteLine(" Showing object '{0}'", filepath);
}
catch (System.IO.FileNotFoundException e)
{
Console.WriteLine("{0}: {1}", e.GetType().Name, e.Message);
}
Console.WriteLine(" Done.");
}

Python

def Composite_Exercise():
print()
print("Composite Exercise")
try:
filepath = "root"
rootEntry = Composite_FileAccess.GetEntry(filepath)
print(" Showing object '{}'".format(filepath))
filepath = "root/subdir1/FileD.txt"
rootEntry = Composite_FileAccess.GetEntry(filepath)
print(" Showing object '{}'".format(filepath))
except FileNotFoundError as ex:
print("Error! {} ".format(ex))
print(" Done.")

C

void Composite_Exercise(void)
{
printf("\nComposite_Exercise\n");
const char* filepath = "root";
if (rootEntry != NULL)
{
printf(" Showing object '%s'\n", filepath);
filepath = "root/subdir1/FileD.txt";
rootEntry = Composite_FileAccess_GetEntry(filepath);
if (rootEntry != NULL)
{
printf(" Showing object '%s'\n", filepath);
}
else
{
printf(" Error! Unable to get a FileDirEntry for the path \"%s\"!", filepath);
}
}
else
{
printf(" Error! Unable to get a FileDirEntry for the path \"%s\"!", filepath);
}
printf(" Done.\n");
}
FileDirEntry * Composite_FileAccess_GetEntry(const char *path)
Return a FileDirEntry object representing the specified file "path" from an internal list of data ent...

Rust

(Apologies. Doxygen does not understand Rust syntax and therefore cannot colorize the code.)

pub fn composite_exercise() -> Result<(), String> {
println!("");
println!("Composite Exercise");
let mut file_path = "root";
let root: Rc<RefCell<dyn FileDirEntry>> = construct_tree();
println!(" Showing object '{file_path}'");
composite_show_entry(root.clone());
file_path = "root/subdir1/FileD.txt";
let path_entry = match composite_fileaccess::get_entry(root.clone(), &file_path) {
Some(entry) => entry,
None => return Err(String::from("Could not find path \"{file_path}\"")),
};
println!(" Showing object '{file_path}'");
composite_show_entry(path_entry.clone());
println!(" Done.");
Ok(())
}

See Also