Saturday, September 21, 2024

Composite Pattern in Rust - my exploration continues...

In this ever evolving world of programming languages when each day a new language pops up, it's difficult to maintain sanity - that's why impostor syndrome is very common in the software industry. But whatever may be the case, there are some fundamental concepts which remain intact. For example, the event handler loops are almost for every event driven OS. Similarly, the design pattern concepts - it's truly language-agnostic.

Language syntax may change. But the basic principles of software design will remain the same - the same abstraction, encapsulation, inheritance, composition - all remain same.

That's why learning design pattern is such an important goal in the software industry.

Design patterns provide reusable solutions to common problems in software design. The language may differ (like using inheritance in object-oriented languages or using traits in Rust), but the patterns are language-agnostic in concept.

It enforces us to rise above the language and look into a piece of code from a designer's perspective.

So.... here we go...

My implementation of composite pattern in Rust.


use std::vec;
trait Shape : ShapeClone{
fn new() -> Self where Self: Sized;
fn add(&mut self, shape : Box<dyn Shape>){
println!("It's a leaf.... can't add more components to it");//default behavior
}
fn remove(&mut self,shape : Box<dyn Shape>){
println!("It's a leaf.... can't remove components from it");//default behaviour
}
fn get_parent(&self) -> Option<Box<dyn Shape>>;
fn set_parent(&mut self, shape: Option<Box<dyn Shape>>);
fn display(&self);
}
trait ShapeClone {
fn clone_box(&self) -> Box<dyn Shape>;
}

impl<T> ShapeClone for T
where
T: 'static + Shape + Clone,
{
fn clone_box(&self) -> Box<dyn Shape> {
Box::new(self.clone())
}
}

impl Clone for Box<dyn Shape> {
fn clone(&self) -> Box<dyn Shape> {
self.clone_box()
}
}

#[derive(Clone)]
struct Point{
x:i32,
y:i32,
parent: Option<Box<dyn Shape>>,
}
#[derive(Clone)]
struct Line {
p1:Point,
p2:Point,
}
#[derive(Clone)]
struct Quadrilateral {
p1:Point,
p2:Point,
p3:Point,
p4:Point,
}
#[derive(Clone)]
struct CompositeShape{
parent : Option<Box<dyn Shape>>,
list_of_shapes : Vec<Box<dyn Shape>>,
}

impl Shape for CompositeShape{
fn new() -> Self
where
Self: Sized
{
Self {
parent: None,
list_of_shapes: Vec::new(),
}
}

fn add(&mut self, shape : Box<dyn Shape>){
self.list_of_shapes.push(shape);
}
fn remove(&mut self, shape : Box<dyn Shape>){
self.list_of_shapes.pop();
}
fn get_parent(&self) -> Option<Box<dyn Shape>>{
self.parent.clone()
}
fn set_parent(&mut self, shape: Option<Box<dyn Shape>>){
self.parent = shape;
}
fn display(&self){
for shape in self.list_of_shapes.iter(){
shape.display();
}
}
}

impl Shape for Point{
fn new() -> Self
where
Self: Sized
{
Self { x:0, y:0, parent: None } // Can also write Point { x, y }
}

fn get_parent(&self) -> Option<Box<dyn Shape>>{
self.parent.clone()
}

fn set_parent(&mut self, shape: Option<Box<dyn Shape>>) {
self.parent = shape;
}

fn display(&self) {
println!("X : {}, Y: {}", self.x, self.y);
}
}


fn main() {

let mut composite = CompositeShape::new();

// Create points
let mut p1 = Box::new(Point { x: 1, y: 1, parent: None });
let mut p2 = Box::new(Point { x: 2, y: 2, parent: None });

// Set composite as parent of points
p1.set_parent(Some(Box::new(composite.clone())));
p2.set_parent(Some(Box::new(composite.clone())));

// Add points to composite shape
composite.add(p1.clone());
composite.add(p2.clone());

p1.add(p2.clone());

composite.remove(p2);

composite.display();
}
And here's my exploration of the Composite Design Pattern back in 2008 using C++.



Enjoy...

No comments: