Wednesday, June 8, 2022

Design Pattern: Composite Pattern

Chapters

Composite Pattern

Composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object.

The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

These diagrams demonstrates composite pattern.
Diagram
Courtesy of Wikipedia

We can think of this method as a tree structure. A branch holds leaves and leaves are in a branch. In the diagram above, Component is a root branch and Composite is a child branch. Take a look at this example.
import java.util.List;
import java.util.ArrayList;

public class ClientCode{
  
  public static void main(String[] args){
    Package myRedPackage = 
    new RedPackage("Delivery Package", "packages");
    
    myRedPackage.addPackage(
    new RedPackage("My Colleague's Package", 
                   "Ballpens"));
    myRedPackage.addPackage(
    new RedPackage("My Package", 
                   "Pens"));
    myRedPackage.addPackage(
    new BluePackage("My Friend's Package", 
                    "Documents", true));
    
    myRedPackage.packageInfo();
    System.out.println();
    
    myRedPackage.getPackage(2).packageInfo();
    
    System.out.println();
    myRedPackage.removePackage(2);
    System.out.println();
    
    Package subPackage = 
    myRedPackage.getPackage(0);
    subPackage.packageInfo();
    System.out.println();
    
    subPackage.addPackage(
    new RedPackage("Optional Package", "Stamps"));
    subPackage.getPackage(0).packageInfo();
  }
}

//root 
abstract class Package{
  private List<Package> packageList;
  private String content;
  private String packageName;
  
  Package(String packageName, String content){
    packageList = new ArrayList<>();
    this.packageName = packageName;
    this.content = content;
  }
  
  public String getPackageName(){
    return packageName;
  }
  
  public String getContent(){
    return content;
  }
  
  public void addPackage(Package element){
    packageList.add(element);
  }
  
  public Package getPackage(int index){
    Package target = null;
    
    try{
      target = packageList.get(index);
    }
    catch(ArrayIndexOutOfBoundsException e){
      System.out.println("Invalid Index!");
    }
    return target;
  }
  
  public void removePackage(int index){
    String target = null;
  
    try{
      target = packageList.get(index).
      getPackageName();
      packageList.remove(index);
    }
    catch(ArrayIndexOutOfBoundsException e){
      System.out.println("Invalid Index!");
      return;
    }
    System.out.println
    ("Package "+ target +" is removed!");
  }
  
  abstract public void packageInfo();
}

//leaf
class RedPackage extends Package{
  
  RedPackage(String packageName, String content){
    super(packageName, content);
  }
  
  @Override
  public void packageInfo(){
    System.out.println("Name: " + 
    getPackageName());
    System.out.println("Color: Red");
    System.out.println("Parent: Package");
    System.out.println("Content: " + getContent());
  }
}

//composite
abstract class CompositeStandardPackage extends Package{
  private boolean isSpecial;
  
  CompositeStandardPackage(String packageName, 
                           String content, 
                           boolean isSpecial){
    super(packageName, content);
    this.isSpecial = isSpecial;
  }
  
  public boolean isSpecial(){
    return isSpecial;
  }
}

//leaf
class BluePackage extends CompositeStandardPackage{
  
  BluePackage(String packageName, String content,
              boolean isSpecial){
    super(packageName, content, isSpecial);
  }
  
  @Override
  public void packageInfo(){
    System.out.println("Name: " + 
    getPackageName());
    System.out.println("Color: Blue");
    System.out.println
    ("Parent: CompositeStandardPackage");
    System.out.println("Content: " + getContent());
    System.out.println("Is Special? " + isSpecial());
  }
}

Result
Name: Delivery Package
Color: Red
Parent: Package
Content: Packages

Name: My Friend's Package
Color: Blue
Parent: CompositeStandardPackage
Content: Documents
Is Special? true

Package My Friend's Package is removed!

Name: My Colleague's Package
Color: Red
Parent: Package
Content: Ballpens

Name: Optional Package
Color: Red
Parent: Package
Content: Stamps
In the example above, leaf and Composite classes can add, get and remove children(packages in this case). There are two types of child-related designs that we can use to design our composite tree structures. Take a look at this diagram.
Diagram
Courtesy of Wikipedia

In the example above, I used the "Design for Uniformity" design because the composite design pattern emphasizes uniformity over type safety. In "Design for type safety" design, only composite class and its subclasses can have children. This example in wikipedia demonstrates "Design for type safety" design.

No comments:

Post a Comment