Composite
With this pattern, we can model object/structs hierarchies independently if the object is simple or composed it will be treated uniformly
Search for a command to run...
With this pattern, we can model object/structs hierarchies independently if the object is simple or composed it will be treated uniformly
No comments yet. Be the first to comment.
In this series, I will publish articles related to my design patterns studies. I am using this blog as a tool to learn more efficiently and hopefully will help others as well.
It's a behavioral pattern which turns possible changes in behavior accordingly to the chosen strategy. Structure Context The use of context is recommended so that the client does not need to know the implementation details of each strategy. It can f...
Uma parte da astrofotografia bem interessante é a planetária. Tenho feito alguns registros. O planeta que tive mais oportunidades de registar foi Júpiter. Jupiter tem sido o planeta mais generoso comigo. Nessa imagem consegue-se ver um eclipse solar ...
Minha história com a Nebulosa de Orion é singular, acredito que com todos seja assim. É talvez a nebulosa mais fácil de ser encontrada no céu. Em noites escuras com pouca poluição luminosa pode ser percebida à olho nu como uma pequena região de luz d...
Galaxia localizada na constelação de Centauro, próximo ao aglomerado Omega Centauro. Tem formato peculiar e tem sido vítima das minhas observações, uma vez que quando não consigo mais visualizar a Nebulosa da Carina. Capturar esse objeto tem se mostr...
Como a região sul é uma das poucas que tenho visada do meu apartamento tenho utilizado a nebulosa da carina como referência pra minhas astrofotografias. Tenho aprendido bastante e consigo comparar a evolução das imagens. Atualmente meu principal prob...
It's a behavioral pattern which turns possible changes in behavior accordingly to the chosen strategy. Structure Context The use of context is recommended so that the client does not need to know the implementation details of each strategy. It can f...
The key point is to have a trait to represent both, simple and composed structs.
Clients should ignore the difference between compositions and individual objects.
Avoid case statements to specify individual treatment to every struct, making it easier to put new types of components
An abstract representation of the component
The simplest component, which has no children
The sort of Component which has children
The software part that uses the composite api
In the example I am going to model glasses because I already used them in my professional career and did not have any knowledge of the composite pattern at that time, the final solution was very close to the pattern but I would suffer much less if I knew it.
Firstly I will show a possible approach to use as a more OO driven language, such as Java, Kotlin etc

Secondly, I will show the approach that I choose using rust, because of its differences from the language cited earlier

pub trait Item {
fn price(&self) -> f64;
fn add_item(&mut self, _: Box<dyn Item>) -> Result<(), &str>{
return Err("not implemented on a simple item")
}
fn remove_item(&mut self, _: usize) -> Result<(), &str>{
return Err("not implemented on a simple item")
}
}
I decided to use a default implementation to deal with component children since it would simplify the use for the Composite client. It's important to evaluate the tradeoffs here.
struct ItemComposite {
items: Vec<Box<dyn Item>>
}
impl ItemComposite {
fn new() -> Self {
return ItemComposite{
items: Vec::new()
}
}
}
impl Item for ItemComposite {
fn price(&self) -> f64 {
let prices = self.items.iter().map(|x| x.price());
return prices.sum()
}
fn add_item(&mut self, i: Box<dyn Item>) -> Result<(), &str>{
self.items.push(i);
return Ok(())
}
fn remove_item(&mut self, position: usize) -> Result<(), &str>{
_ = self.items.remove(position);
return Ok(())
}
}
Since there's no inheritance in rust, I had to state the behavior in terms of traits, just exposing the behavior and not attributes like items for example. Therefore I decided to aggregate the composed structs with an ItemComposite in contrast to inheriting it as I would do in a OO language. When composed-related operations such as price are invoked by the client the composed class does part of the operation and delegates the children-related part to the Item composite.
pub struct Lens {
composite: ItemComposite,
pub(crate) price: f64,
}
impl Lens {
pub fn new(price: f64) -> Self {
return Lens{
composite : ItemComposite::new(),
price,
}
}
}
impl Item for Lens {
fn price(&self) -> f64 {
return &self.price + &self.composite.price()
}
fn add_item(&mut self, i: Box<dyn Item>) -> Result<(), &str>{
return self.composite.add_item(i);
}
fn remove_item(&mut self, i: usize) -> Result<(), &str>{
return self.composite.remove_item(i);
}
}
pub struct Frame {
composite: ItemComposite,
price: f64,
}
impl Item for Frame {
fn price(&self) -> f64 {
return &self.price + &self.composite.price()
}
fn add_item(&mut self, i: Box<dyn Item>) -> Result<(), &str>{
return self.composite.add_item(i);
}
fn remove_item(&mut self, i: usize) -> Result<(), &str>{
return self.composite.remove_item(i)
}
}
impl Frame {
pub fn new(price: f64) -> Self {
return Frame{
composite : ItemComposite::new(),
price,
}
}
}
pub struct Treatment {
pub price: f64,
}
impl Item for Treatment {
fn price(&self) -> f64 {
return self.price
}
}
impl Treatment {
pub fn new(price: f64) -> Self {
return Treatment{
price,
}
}
}
pub struct Signature {
pub price: f64,
}
impl Signature {
pub fn new(price: f64) -> Self {
return Signature{
price,
}
}
}
impl Item for Signature {
fn price(&self) -> f64 {
return self.price
}
}
#[cfg(test)]
mod test {
use crate::items::*;
#[test]
fn calling_add_item_from_an_not_composed_item() {
let mut treatment = Treatment::new(200.0);
let sig = Signature::new(100.0);
let res = treatment.add_item(Box::new(sig));
assert!(res.is_err())
}
#[test]
fn assemblying_an_complte_glass_product() {
let treatment = Treatment::new(200.0);
let mut lens = Lens::new(300.0);
let _ = lens.add_item(Box::new(treatment));
let sig = Signature::new(100.0);
let mut frame = Frame::new(500.0);
let _ = frame.add_item(Box::new(lens));
let _ = frame.add_item(Box::new(sig));
assert_eq!(frame.price(), 1100.0);
}
}
https://github.com/igorcavalcante/design_patterns/blob/main/composite
A third approach would be to create a trait that extends the item trait and adds children-related behavior. It would work but could add complexity to the API client since probably they would need to differentiate between a Leaf and a Composed struct.

Basically, all my problems were hot to adapt an OO pattern to rust. Such as
No abstract classes with attributes
Some trouble with borrowing and other kinds of compile checks
Some optional aspect is to share behavior to manage the children's objects
It would be easier to use a more Object Oriented language to do the same job, but It could be done using Rust with good effectiveness too. As a rust learner, I had some trouble trying to figure out how to borrow variables, use traits, and use modules as anyone should have. Certainly, I will use this pattern in a case of hierarchy-organized structures
Elements of Reusable Object-Oriented Software Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
https://refactoring.guru/design-patterns/composite/rust/example
AXEL FORTUN https://grapeprogrammer.com/composite-pattern-rust/