CPSC 210
D4: Composite Pattern
Learning Goals
- Justify use of design patterns
- Explain where design patterns come from
- Apply the Composite Design Pattern to a given problem
We make mistakes
We identify our mistakes
We fix our mistakes
We learn to prevent them in future
We Evolve Design Patterns
Lessons Learned!
Why Design Patterns?
Maybe...
Then we iterate!
What is a design pattern?
"General, reusable solution to a commonly occurring problem within a given context in software design"
"Guidelines for software design that help you avoid mistakes that others repeatedly made in the past"
What is a design pattern?
- a re-usable approach to solving a design problem
What is NOT design pattern?
- a re-usable library (e.g. Swing) or a reusable piece of code (e.g. ArrayList or HashSet)
What's the challenge?
- to adopt or adapt a design pattern for one's use case
- to decide whether a design pattern makes sense for a use case
The Composite Pattern
Products and Boxes
- Two types of objects: Products and Boxes
- Boxes can contain a product or other boxes
- How to determine the price of an order?
- Unwrap all the boxes
- Recursively go over all the products
- Calculate the total
- This is not simple in a program!
- What are the classes of products?
- What is the nesting level?
- The naive approach will either be painful or even impossible!
- Work with Products and Boxes through a common interface
- Interface declares a method for calculating the total price
Products and Boxes (2)
Product receipt = new Product("Receipt", 0.0);
Product hammer = new Product("Hammer", 20.00);
Product phone = new Product("Phone", 500.00);
Product headphones = new Product("Headphones", 50.00);
Product charger = new Product("Charger", 35.00);
Box toolBox = new Box("Tool Box", hammer);
Box techBox = new Box("Tech Box", phone, headphones);
Box chargerBox = new Box("Charger Box", charger);
Box electronicsBox = new Box("Electronics Box", techBox, chargerBox);
Box orderBox = new Box("Shipment Box", toolBox, electronicsBox, receipt);
orderBox.printContent();
orderBox.printTotalPrice();
Products and Boxes (3)
private void addButtonPanel() {
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(4,2));
buttonPanel.add(new JButton(new AddCodeAction()));
buttonPanel.add(new JButton(new RemoveCodeAction()));
buttonPanel.add(new JButton(new ArmAction()));
buttonPanel.add(new JButton(new DisarmAction()));
buttonPanel.add(new JButton(new AddSensorAction()));
buttonPanel.add(new JButton(new ClearLogAction()));
buttonPanel.add(new JButton(new PrintLogAction()));
buttonPanel.add(createPrintCombo());
controlPanel.add(buttonPanel, BorderLayout.WEST);
}
Where have I seen
something like this ?
Alarm Controller UI
Swing Containers/Components form a Component hierarchy, and Swing can paint them (and various other actions) recursively!
Composite: Motivation
I have a tree structure and I don't know all the types of objects in the tree.
I want to run some operations recursively through my tree without knowing the exact types in my tree.
Composite Pattern
Composite node: arbitrarily many children
Leaf node:
no children
This is the basic idea..
..we can have many variants on both types of nodes!
How to use it?
-
Identify your tree structure
- composite objects and leaf objects
- if you don't have such a structure, this pattern makes no sense!
- Introduce abstract type that captures common behaviours of both
- Composite objects
- Leaf objects
Composite Class Diagram
When should I (not) use it?
- You want to implement a tree-like structure
- You have common functionality for all components in a tree
- Sometimes it's hard to find a common interface for components
- Components might sometimes not be "common enough" (overgeneralizing)
In other words
Image source: https://refactoring.guru/
Lecture Ticket
Lecture Ticket
- You have branches and leaves.
- Branches can have offshoots of either branches or leaves.
- Leaves, obviously, cannot have any offshoots.
- You choose to model this in Object Orientation using the Composite Pattern.
- You identify three classes: Branch, Leaf and the third class, Offshoot.
Lecture Ticket - Part 1
What hierarchical relationship do these classes have to one another?
- Leaf extends Offshoot
- Branch extends Offshoot
- Offshoot extends nothing
We identify Offshoot as the component, Branch as the composite, and Leaf as the the leaf
Lecture Ticket - Part 2
Which of the following fields would exist in Offshoot, Branch and Leaf?
- Branch has a list of Offshoot
The aggregation from Branch to Offshoot would be implemented by adding a field of type List<Offshoot> to the Branch class.
Lecture Ticket - Part 3
Imagine that you want to change the color of all the leaves to red. You want to do this by adding a changeColor(Color) method. Where might the method be specified but not implemented?
The changeColor(Color) method is one that we would want to call on both a Leaf (to change the color of that individual leaf) and on a Branch (to change the color of all the leaves attached to that branch). This is therefore common behaviour that we want all offshoots (whether they be a branch or a leaf) to support. The method is therefore specified in the Offshoot class.
Lecture Ticket - Part 4
Where would the changeColor method be implemented?
- In Branch, where it has a for-loop that iterates over its entire internal collection recursively invoking changeColor(Color) on each element
- In Leaf, where it changes the field that holds the color
See part 3 answer!
Lecture Lab
- Draw a UML Class Diagram (including all methods!) with Branch, Leaf, and Offshoot
- Implement the behaviour in startfromscratch
- Try again via refactoring in refactortogetthere
D4: Composite Pattern
The End - Thank You!
CPSC210 - D4: Composite
By Steven Wolfman
CPSC210 - D4: Composite
- 215