Skip to content

Commit 220dfcc

Browse files
authored
Merge pull request #4 from AndresCdo/mdabir1203-Main
Add design patterns and clean up code
2 parents f4a5749 + 51a6bc4 commit 220dfcc

File tree

32 files changed

+1124
-212
lines changed

32 files changed

+1124
-212
lines changed

.gitignore

+11-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,24 @@ target/
33

44
# Ignore IDE-specific files
55
.idea/
6+
7+
# Ignore generated files
8+
Cargo.lock
9+
10+
# Ignore editor-specific files
611
*.iml
712

813
# Ignore editor-specific files
914
.vscode/
1015

11-
# Ignore generated files
12-
Cargo.lock
16+
# Ignore test files
17+
*.rs
18+
19+
# Ignore documentation files
20+
*.md
1321

1422
# Ignore example files
1523
examples/
1624

1725
# Ignore git-specific files
18-
.git/
26+
.git/

.idea/vcs.xml

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/workspace.xml

+36-206
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

DesignModule04/Observer.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use std::cell::RefCell;
2+
3+
fn main()
4+
{
5+
let mut weather_station = WeatherStation::new();
6+
let display1 = Rc::new(RefCell::new(DisplayDevice{
7+
name: String::from("Display 1"),
8+
}));
9+
let display2 = Rc::new(RefCell::new(DisplayDevice{
10+
name: String::from("Display 2"),
11+
}));
12+
13+
weather_station.add_observer(display1);
14+
weather_station.add_observer(display2);
15+
16+
weather_station.set_measurements(25.5, 60.0);
17+
weather_station.set_measurements(26.0, 58.5);
18+
19+
}

DesignModule04/Singleton.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use std::collections::HashMap;
2+
use std::sync::{Arc, Mutex, Once};
3+
use std::fs;
4+
use std::io;
5+
6+
struct ConfigManager {
7+
config: HashMap<String, String>,
8+
}
9+
10+
impl ConfigManager {
11+
fn new() -> Result<Self, io::Error> {
12+
// Simulate loading configuration from a file
13+
let config_str = fs::read_to_string("config.toml")?;
14+
let config = parse_config(&config_str);
15+
Ok(ConfigManager { config })
16+
}
17+
18+
fn get(&self, key: &str) -> Option<&String> {
19+
self.config.get(key)
20+
}
21+
22+
fn set(&mut self, key: String, value: String) {
23+
self.config.insert(key, value);
24+
}
25+
26+
fn save(&self) -> Result<(), io::Error> {
27+
// Simulate saving configuration to a file
28+
let config_str = format_config(&self.config);
29+
fs::write("config.toml", config_str)
30+
}
31+
}
32+
33+
fn get_config() -> Arc<Mutex<ConfigManager>> {
34+
static mut SINGLETON: Option<Arc<Mutex<ConfigManager>>> = None;
35+
static ONCE: Once = Once::new();
36+
37+
unsafe {
38+
ONCE.call_once(|| {
39+
match ConfigManager::new() {
40+
Ok(config) => {
41+
SINGLETON = Some(Arc::new(Mutex::new(config)));
42+
}
43+
Err(e) => {
44+
eprintln!("Failed to initialize ConfigManager: {}", e);
45+
std::process::exit(1);
46+
}
47+
}
48+
});
49+
50+
SINGLETON.clone().unwrap()
51+
}
52+
}
53+
54+
fn parse_config(config_str: &str) -> HashMap<String, String> {
55+
// This is a simplified parser. In a real application, you'd use a proper TOML parser.
56+
config_str
57+
.lines()
58+
.filter_map(|line| {
59+
let parts: Vec<&str> = line.splitn(2, '=').collect();
60+
if parts.len() == 2 {
61+
Some((parts[0].trim().to_string(), parts[1].trim().to_string()))
62+
} else {
63+
None
64+
}
65+
})
66+
.collect()
67+
}
68+
69+
fn format_config(config: &HashMap<String, String>) -> String {
70+
config
71+
.iter()
72+
.map(|(k, v)| format!("{} = {}", k, v))
73+
.collect::<Vec<String>>()
74+
.join("\n")
75+
}
76+
77+
use std::thread;
78+
79+
fn main() -> Result<(), Box<dyn std::error::Error>> {
80+
// Simulate multiple parts of an application accessing and modifying the configuration
81+
let handles: Vec<_> = (0..3)
82+
.map(|i| {
83+
thread::spawn(move || {
84+
let config = get_config();
85+
let mut config = config.lock().unwrap();
86+
87+
// Read a configuration value
88+
let db_url = config.get("database_url").cloned().unwrap_or_default();
89+
println!("Thread {} read database_url: {}", i, db_url);
90+
91+
// Modify a configuration value
92+
let new_value = format!("new_value_from_thread_{}", i);
93+
config.set(format!("key_from_thread_{}", i), new_value.clone());
94+
println!("Thread {} set new value: {}", i, new_value);
95+
96+
// Simulate some work
97+
thread::sleep(std::time::Duration::from_millis(100));
98+
})
99+
})
100+
.collect();
101+
102+
for handle in handles {
103+
handle.join().unwrap();
104+
}
105+
106+
// After all threads have finished, save the configuration
107+
let config = get_config();
108+
let config = config.lock().unwrap();
109+
config.save()?;
110+
println!("Configuration saved.");
111+
112+
Ok(())
113+
}

DesignModule04/StockMarket/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "StockMarket"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
42.5 KB
Loading
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Stock Market Monitor (Observer Pattern)
2+
3+
This project demonstrates the implementation of the Observer pattern in Rust using a Stock Market monitoring system as a real-world example.
4+
5+
## Overview
6+
7+
The Stock Market Monitor allows multiple observers (displays and alert systems) to receive updates whenever stock prices change. This showcases how the Observer pattern can be used to implement a publish-subscribe model.
8+
9+
## Features
10+
11+
- Real-time updates of stock prices to multiple observers
12+
- Different types of observers (displays and alert systems)
13+
- Easy addition of new observers
14+
15+
## Mermaid Diagram
16+
17+
The following diagram illustrates the structure and flow of the Stock Market Monitor using the Observer pattern:
18+
19+
```mermaid
20+
classDiagram
21+
class StockObserver {
22+
<<interface>>
23+
+update(symbol: str, price: f64)
24+
}
25+
class StockMarket {
26+
-prices: HashMap<String, f64>
27+
-observers: Vec<Rc<RefCell<dyn StockObserver>>>
28+
+add_observer(observer: Rc<RefCell<dyn StockObserver>>)
29+
+set_price(symbol: str, price: f64)
30+
-notify_observers(symbol: str, price: f64)
31+
}
32+
class PriceDisplay {
33+
-name: String
34+
+update(symbol: str, price: f64)
35+
}
36+
class AlertSystem {
37+
-threshold: f64
38+
+update(symbol: str, price: f64)
39+
}
40+
41+
StockMarket ..> StockObserver : notifies
42+
PriceDisplay ..|> StockObserver
43+
AlertSystem ..|> StockObserver
44+
```
45+
46+
## How it works
47+
48+
1. The `StockMarket` (subject) maintains a list of observers and current stock prices.
49+
2. Observers (`PriceDisplay` and `AlertSystem`) implement the `StockObserver` trait.
50+
3. When a stock price is updated via `set_price()`, all registered observers are notified.
51+
4. Each observer type handles the update differently:
52+
- `PriceDisplay` simply displays the new price.
53+
- `AlertSystem` checks if the price exceeds a threshold and issues an alert if it does.
54+
55+
## Usage
56+
57+
To use the Stock Market Monitor in your code:
58+
59+
```rust
60+
let mut stock_market = StockMarket::new();
61+
62+
let display = Rc::new(RefCell::new(PriceDisplay {
63+
name: String::from("Main Display"),
64+
}));
65+
let alert = Rc::new(RefCell::new(AlertSystem {
66+
threshold: 100.0,
67+
}));
68+
69+
stock_market.add_observer(display);
70+
stock_market.add_observer(alert);
71+
72+
// Update a stock price
73+
stock_market.set_price("AAPL", 150.0);
74+
```
75+
76+
## Running the Example
77+
78+
1. Ensure you have Rust installed.
79+
2. Clone this repository.
80+
3. Run `cargo run` to see the Stock Market Monitor in action.
81+
82+
## Notes
83+
84+
The Observer pattern is excellent for implementing distributed event handling systems. It's widely used in implementing distributed event handling systems, MVC architectural pattern, and in designing user interface toolkits. However, if overused, it can lead to complex systems where observers are difficult to track and maintain.
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use std::collections::HashMap;
2+
use std::cell::RefCell;
3+
use std::rc::Rc;
4+
5+
// Observer trait
6+
trait StockObserver {
7+
fn update(&self, symbol: &str, price: f64);
8+
}
9+
10+
// Subject (Observable)
11+
struct StockMarket {
12+
prices: HashMap<String, f64>,
13+
observers: Vec<Rc<RefCell<dyn StockObserver>>>,
14+
}
15+
16+
impl StockMarket {
17+
fn new() -> Self {
18+
StockMarket {
19+
prices: HashMap::new(),
20+
observers: Vec::new(),
21+
}
22+
}
23+
24+
fn add_observer(&mut self, observer: Rc<RefCell<dyn StockObserver>>) {
25+
self.observers.push(observer);
26+
}
27+
28+
fn set_price(&mut self, symbol: &str, price: f64) {
29+
self.prices.insert(symbol.to_string(), price);
30+
self.notify_observers(symbol, price);
31+
}
32+
33+
fn notify_observers(&self, symbol: &str, price: f64) {
34+
for observer in &self.observers {
35+
observer.borrow().update(symbol, price);
36+
}
37+
}
38+
}
39+
40+
// Concrete Observers
41+
struct PriceDisplay {
42+
name: String,
43+
}
44+
45+
impl StockObserver for PriceDisplay {
46+
fn update(&self, symbol: &str, price: f64) {
47+
println!("{}: {} stock updated to ${:.2}", self.name, symbol, price);
48+
}
49+
}
50+
51+
struct AlertSystem {
52+
threshold: f64,
53+
}
54+
55+
impl StockObserver for AlertSystem {
56+
fn update(&self, symbol: &str, price: f64) {
57+
if price > self.threshold {
58+
println!("ALERT: {} stock price ${:.2} exceeds threshold ${:.2}", symbol, price, self.threshold);
59+
}
60+
}
61+
}
62+
63+
fn main() {
64+
let mut stock_market = StockMarket::new();
65+
66+
let display1 = Rc::new(RefCell::new(PriceDisplay {
67+
name: String::from("Display 1"),
68+
}));
69+
let display2 = Rc::new(RefCell::new(PriceDisplay {
70+
name: String::from("Display 2"),
71+
}));
72+
let alert_system = Rc::new(RefCell::new(AlertSystem {
73+
threshold: 100.0,
74+
}));
75+
76+
stock_market.add_observer(display1);
77+
stock_market.add_observer(display2);
78+
stock_market.add_observer(alert_system);
79+
80+
// Simulate stock price changes
81+
stock_market.set_price("AAPL", 15.0);
82+
stock_market.set_price("GOOGL", 1330.0);
83+
stock_market.set_price("MSFT", 95.0);
84+
stock_market.set_price("MSFT", 120.0);
85+
}

DesignModule04/config_man/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "config_man"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]

0 commit comments

Comments
 (0)