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;
use std::any::type_name;

// Define a trait with a default method to print the type name
trait GetTypeName {
fn get_type_name(&self) -> &'static str {
type_name::<Self>()
}
}

// Implement the trait for any type
impl<T> GetTypeName for T {}

fn print_type_name(obj: &dyn GetTypeName) {
println!("The type name is: {}", obj.get_type_name());
}
trait Shape : ShapeClone{
fn new() -> Self where Self: Sized;
fn add(&mut self, shape : Box<dyn Shape>){
println!("Aloha... It's a leaf.... can't add more components to it...");//default behavior
}
fn remove(&mut self,shape : Box<dyn Shape>){
println!("Aloha... 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: 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 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.clone());
}
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: Box<dyn Shape>){
self.parent = Option::from(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: Box<dyn Shape>) {
self.parent = Option::from(shape);
}

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

fn main() {
let mut line1 : Box<dyn Shape> = Box ::new(CompositeShape::new());
let mut quadrilateral1 : Box<dyn Shape> = Box ::new(CompositeShape::new());
// Create points
let mut p1:Box<dyn Shape> = Box::new(Point { x: 1, y: 1, parent: None });
let mut p2:Box <dyn Shape> = Box::new(Point { x: 2, y: 2, parent: None });
let mut p3:Box<dyn Shape> = Box::new(Point { x: 10, y: 20, parent: None });
let mut p4:Box <dyn Shape> = Box::new(Point { x: 60, y: 90, parent: None });

line1.add(p1.clone());
line1.add(p2.clone());

quadrilateral1.add(p1.clone());
quadrilateral1.add(p2.clone());
quadrilateral1.add(p3.clone());
quadrilateral1.add(p4.clone());

//setting the parents of the two points
p1.set_parent(line1.clone());
p2.set_parent(line1.clone());

//Trying to add to a leaf
p1.add(p2.clone());

println!("The starting and ending points of the line1 are...");
line1.display();
println!("The four vertices of the quadrilateral are...");
quadrilateral1.display();
}
And here's my exploration of the Composite Design Pattern back in 2008 using C++.



And here we go... Composite Design Pattern in Python...


Enjoy...

No comments: