Implementation

This time the implementation of the design pattern is more visual (finally!) and would make more sense in Flutter context (yes, I do consider your feedback, so do not hesitate to share your insights about the series — it helps me improve the quality of the content a lot!). Let’s say, we want to represent the structure of our file system. The file system consists of directories and files of various types: audio, video, images, text files, etc. Files are stored inside directories, also, directories could be stored inside other directories. For instance, our file structure could look like this:

Besides, we want to show the size of each file or directory. It is easy to show it for a concrete file, but the directory size depends on the items inside it and should be calculated. To implement this, the Composite design pattern is a great option!

Class diagram

The class diagram below shows the implementation of the Composite design pattern.

Class Diagram — Implementation of the Composite design pattern

IFile defines a common interface for both File (leaf) and Directory (composite) classes:

getSize() — returns size of the file;

render() — renders the component’s UI.

File class implements the getSize() and render() methods, additionally contains title, size and icon properties. Directory implements the same required methods, but it also contains title, isInitiallyExpanded and files list, containing the IFile objects, defines addFile() method, which allows adding IFile objects to the directory (files list). AudioFile, ImageFile, TextFile and VideoFile classes extend the File class to specify a concrete type of the file.

IFile

An interface which defines methods to be implemented by leaf and composite components. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.

File

A concrete implementation of the IFile interface which matches the leaf class in the Composite design pattern. In File class, getSize() method simply returns the file size, render() — returns file’s UI widget which is used in the example screen.

Concrete classes extending File

All of these classes extend the File class and specify the concrete file type by providing a unique icon for the corresponding file type.

Directory

A concrete implementation of the IFile interface which matches the composite class in the Composite design pattern. Similar as in File class, render() returns directory’s UI widget which is used in the example screen. However, in this class getSize() method calculates the directory size by calling the getSize() method for each item in the files list and adding up the results. This is the main idea of the Composite design pattern which allows the composite class to treat all the elements in the containing list uniformly as long as they implement the same interface.

FileSizeConverter

To represent the file size in a more appealing format, FileSizeConverter helper class was created which provides a static method bytesToString() which converts the value of the file size in bytes to the human-readable text.

Example

First of all, a markdown file is prepared and provided as a pattern’s description:

CompositeExample widget contains the buildMediaDirectory() method which builds the file structure for the example. This method illustrates the Composite design pattern — even though the components are of a different type, they could be handled in the same manner since the implemented interface of IFile is the same for all components. This allows us to put Directory objects inside other directories, mix them along with concrete File objects hence building the tree structure of IFile components.

The final result of the Composite design pattern’s implementation looks like this:

As you can see in the example, the file size is shown for each file directly and for directories, it is calculated by adding up each file size inside the directory.

All of the code changes for the Composite design pattern and its example implementation could be found here.