Back to: Design Patterns in C# With Real-Time Examples
Real-Time Example of Composite Design Pattern in C#
In this article, I am going to discuss the Real-Time Example of the Composite Design Pattern in C#. Please read our previous article where we discussed the basic concepts of Composite Design Patterns in C#. The Composite Design Pattern belongs to Structural Design Pattern Category and hence deals with Object Composition.
What is Composite Design Pattern?
The Composite Design Pattern composes the objects in the form of a tree structure to represent part as well as a whole hierarchy. Using this Design Pattern, we can access the operations of an individual object as well as we can access the same operations on a composite object in the same manner. That means the client can access the individual objects or the composition of objects in a uniform manner.
Real-Time Example to Understand Composite Design Pattern:
Now, we need to create a File and Folder structure hierarchy. In our example, if we need to store some file, then that file should be stored under some directory or you can say folder. For a better understanding, please have a look at the following image which shows the Folder and File Structure of the Root Directory. Let us assume the following is the File directory on our computer.
Whatever you see in the above diagram, everything is an object. So, here, Root Directory, Folder1, Folder2, SubFolder1, MyBook.txt, MyVideo.mp4, MyMusic.mp3, MyResume.pdf, MySoftware.exe, and MyDocument.doc, all are objects.
A composite object is an object which contains other objects. The point that you need to remember is a composite object may also contain other composite objects along with leaf objects. The object which does not contain any other objects is simply treated as a leaf object.
So, in our example, Root, Folder1, Folder2, and SubFolder1 are composite objects while MyBook.txt, MyVideo.mp4, MyMusic.mp3, MyResume.pdf, MySoftware.exe, and MyDocument.doc are the leaf object which is shown in the above diagram.
What is our Requirement?
Our Requirement is to get the file size or folder size in KB. Whenever we create or save any file it will take some memory in bytes. So, we need to design an application where we should be able to access the size of any particular file like MyBook.txt, MyVideo.mp4, MyMusic.mp3, etc, i.e. the size of the leaf object. Also, we need to allow the client to get the size of a particular directory or you can say folder. For example, if the client wants to know the size of Folder2, then our application should return the sum of the size of the files which are stored inside the Folder2 directory.
Similarly, if the client wants to know the size of Folder1, then it should the sum of the size of the files which are stored inside Folder1, and as Folder1 is a composite object, and as it contains another composite object i.e. SubFolder1, it should also return the size of files which are stored inside the SubFolder1 directory.
If the client wants to know the size of the Root directory, then the application should return the sum of the size of the files which are present under the Folder1, Folder2, and SubFolder1 directories.
That means we need to define one method, let us say GetSizeinKB and the client should be able to call this method on individual leaf objects as well as should be able to call this method on composite objects in the same manner. This is possible by using Composite Design Pattern in C#.
Real-Time Example Implementation using Composite Design Pattern in C#:
Let us implement the above File and Folder Structure example using Composite Design Pattern in C#. First, we will implement the example and then I will show you the UML Diagram of the Composite Design Pattern by comparing it with our example.
Step1: Creating Component Interface or Abstract Class
Create a class file with the name FileSystemItem.cs and then copy and paste the following code into it. The following FileSystemItem abstract class declares the common operations for both leaf and complex objects. For our example, we need to show the size of a filet as well as we also need to show the size of the folder. As you can see in the below code, we have declared one method called GetSizeinKB which is going to be implemented by both Leaf and Composite components.
namespace CompositeDesignPatternRealTimeExample { // The Base Component Abstract class declares the common operations for both Leaf and Composite objects. public abstract class FileSystemItem { public string Name { get; } public FileSystemItem(string name) { this.Name = name; } //The following method is going to be overridden in both Leaf and Composite class public abstract decimal GetSizeinKB(); } }
Step2: Creating Leaf Object
The Leaf class represents the actual objects i.e. the actual file which has some size. A leaf object cannot have any children. So, create a class file with the name FileItem.cs and then copy and paste the following code into it. The following FileItem class implements the FileSystemItem abstract class and provides implementations for the GetSizeinKB method. As part of this class, we are creating one property i.e. FileBytes to return the size of the file. We are initializing that FileBytes Property using the parameterized constructor of the class. As the file is going to store in the form of bytes, we are converting bytes into KB and then returning the file size in KB from the GetSizeinKB method. The following example code is self-explained, so please go through the comment lines for a better understanding.
namespace CompositeDesignPatternRealTimeExample { // This is going to be our Leaf class that represents the end objects. // A leaf cannot have any children. // The Leaf object is the Object which does the actual work public class FileItem : FileSystemItem { //The following Property is going to hold the size of the file public long FileBytes { get; } //While creating the Leaf Object i.e. while Creating the FileItem Object, //we need to pass the File Name and the Size of the File //The File Name we need to pass the Base class constructor public FileItem(string name, long fileBytes) : base(name) { this.FileBytes = fileBytes; } //We need to override the following method to provide the actual implementation public override decimal GetSizeinKB() { //Divide the size which will be in bytes with the value of 1024 to convert into KB return decimal.Divide(this.FileBytes, 1024); } } }
Step3: Creating Composite Object
The Composite class in Composite Design Pattern represents the composite components that have children. The Composite objects combine the result of the children. That means the composite component is going to return the sum of the file size that is stored under them.
So, create a class file with the name Directory.cs and then copy and paste the following code into it. As this class is going to be our composite class, so we are creating a variable i.e. Childrens to hold its child components. Again, we are initializing the component name property using the class constructor. Using the AddComponent() method we are adding child components inside the Childrens variable. Again, using the RemoveComponent() method, we are removing the child component from the Childrens variable.
This class also implements the FileSystemItem abstract class and overrides the GetSizeinKB method. As part of the GetSizeinKB() method, we are using the LINQ query to calculate the sum of the file size of its children component, and to do this, we are internally calling the GetSizeinKB method on the child component object. The following example code is self-explained, so please go through the comment lines for a better understanding.
using System.Collections.Generic; using System.Linq; namespace CompositeDesignPatternRealTimeExample { // This is going to be our Composite class that represents the Composite objects. // A Composite Object has children. // The Children Can be a Leaf Object or Can be another Composite Object // The Composite objects delegate the actual work to their children and then combine the result. public class Directory : FileSystemItem { //The Following variable is going to hold all the child components of a composite components private List<FileSystemItem> Childrens = new List<FileSystemItem>(); //The Constructor takes the Composite name as the input parameter public Directory(string name) : base(name) { } //The following Method is used to add Child Components inside the Composite Component public void AddComponent(FileSystemItem NewNode) { this.Childrens.Add(NewNode); } //The following Method is used to Remove Child Components inside the Composite Component public void RemoveComponent(FileSystemItem RemoveNode) { this.Childrens.Remove(RemoveNode); } //Override the FileSystemItem class GetSizeinKB Method public override decimal GetSizeinKB() { // Summarizing the size of children return this.Childrens.Sum(x => x.GetSizeinKB()); } } }
Step4: Client Code
The client code works with all of the components (Both Leaf and Composite) via the base abstract class i.e. FileSystemItem i.e. the class that implements the FileSystemItem abstract class. So, please modify the Main method of the Program class as shown below. Here, we are creating the tree structure and then showing the respective component size by calling the GetSizeinKB method. Here, you can see, we are creating leaf objects, we are creating composite objects, and then calling the GetSizeinKB method on both Leaf objects as well as Composite objects. The following example code is self-explained, so please go through the comment lines for a better understanding.
using System; namespace CompositeDesignPatternRealTimeExample { class Program { static void Main(string[] args) { // The Client Code will work with all of the components (Both Leaf and Composite) via the base abstract class i.e. FileSystemItem. // FileSystemItem means the class that implements the FileSystemItem abstract class //Creating Leaf Objects i.e. Creating Files FileSystemItem MyBook = new FileItem("MyBook.txt", 12000); FileSystemItem MyVideo = new FileItem("MyVideo.mp4", 1000000); FileSystemItem MyMusic = new FileItem("MyMusic.mp3", 20000); FileSystemItem MyResume = new FileItem("MyResume.pdf", 18000); FileSystemItem MySoftware = new FileItem("MySoftware.exe", 250000); FileSystemItem MyDocument = new FileItem("MyDocument.doc", 87000000); //Create the Root Directory i.e. Composite Object Directory RootDirectory = new Directory("RootDirectory"); //Add 2 More Folders i.e. two more composite objects Directory Folder1 = new Directory("Folder1"); Directory Folder2 = new Directory("Folder2"); //Add the above two folders under Root Directory RootDirectory.AddComponent(Folder1); RootDirectory.AddComponent(Folder1); //Add files to Folder 1 Folder1.AddComponent(MyBook); Folder1.AddComponent(MyVideo); //Create a Sub Folder1 Directory SubFolder1 = new Directory("Sub Folder1"); //Add files under Sub Folder1 SubFolder1.AddComponent(MyMusic); SubFolder1.AddComponent(MyResume); //Add Sub Folder1 under Folder 1 Folder1.AddComponent(SubFolder1); //Add files to folder 2 Folder2.AddComponent(MySoftware); Folder2.AddComponent(MyDocument); Console.WriteLine("Composite Objects:"); Console.WriteLine($"Total size of (RootDirectory): { RootDirectory.GetSizeinKB() } KB"); Console.WriteLine($"Total size of (Folder 1): { Folder1.GetSizeinKB() }KB"); Console.WriteLine($"Total size of (Folder 2): { Folder2.GetSizeinKB() }KB"); Console.WriteLine($"Total size of (SubFolder 1): { SubFolder1.GetSizeinKB() }KB"); Console.WriteLine("\nLeaf Objects:"); Console.WriteLine($"Total size of MyVideo File: { MyVideo.GetSizeinKB() }KB"); Console.WriteLine($"Total size of MyResume File: { MyResume.GetSizeinKB() }KB"); Console.WriteLine($"Total size of MyBook File: { MyBook.GetSizeinKB() }KB"); Console.WriteLine($"Total size of MyDocument File: { MyDocument.GetSizeinKB() }KB"); Console.Read(); } } }
Output:
UML or Class Diagram of Composite Design Pattern:
Please have a look at the following image to understand the Class Diagram or UML Diagram of the Composite Design Pattern and understand the different components involved in the Composite Design Pattern. Here, I am representing the UML Diagram by comparing it with our Example.
There are four participants involved in the Composite Design Pattern. They are as follows:
- Component: This is going to be an abstract class or interface containing the members that will be implemented by both Leaf and Composite Classes. In our example, it is the FileSystemItem abstract class.
- Leaf: This is going to be a class that represents the leaf object. A leaf object means an object which does not have any children. In our example, it is the FileItem class.
- Composite: This going to be a class that defines the behavior of the Composite Components. A Composite Object means a component having children. The children can be another composite object or can be a leaf object. In our example, it is the Directory class.
- Client: In our example, the Main method of the Program class is going to be the client. The Client Code will work with all of the components (Both Leaf and Composite) via the base abstract class i.e. FileSystemItem. FileSystemItem means the class that implements the FileSystemItem abstract class. In our example, FileItem and Directory classes implement the FileSystemItem abstract class.
In the next article, I am going to discuss Another Composite Design Pattern Real-Time Example. Here, in this article, I try to explain the Real-Time Example of the Composite Design Pattern in C#. I hope, now you enjoy this Real-Time Example of the Composite Design Pattern article.