Skip to content

Commit df10f89

Browse files
authored
Merge pull request #6 from AndresCdo/dev
Refactor markdown content and code structure in Stock Market Monitor and config_man
2 parents 9bb772a + eb599c0 commit df10f89

File tree

7 files changed

+181
-56
lines changed

7 files changed

+181
-56
lines changed

.github/workflows/CI.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ jobs:
4242
- Module02/Web1
4343
- Module02/Web2
4444
# - Module03/zip_cracker
45+
- DesignModule04/config_man
46+
- DesignModule04/StockMarket
4547
steps:
4648
- name: Checkout code
4749
uses: actions/checkout@v2
@@ -55,4 +57,5 @@ jobs:
5557
working-directory: ${{ matrix.module }}
5658
run: |
5759
cargo build --verbose
58-
cargo test --verbose
60+
cargo test --verbose
61+

DesignModule04/Observer.rs

+45-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,57 @@
11
use std::cell::RefCell;
2+
use std::rc::Rc;
23

3-
fn main()
4-
{
4+
struct WeatherStation {
5+
observers: Vec<Rc<RefCell<dyn Observer>>>,
6+
}
7+
8+
impl WeatherStation {
9+
fn new() -> Self {
10+
WeatherStation {
11+
observers: Vec::new(),
12+
}
13+
}
14+
15+
fn add_observer(&mut self, observer: Rc<RefCell<dyn Observer>>) {
16+
self.observers.push(observer);
17+
}
18+
19+
fn set_measurements(&self, temperature: f32, humidity: f32) {
20+
for observer in &self.observers {
21+
observer.borrow_mut().update(temperature, humidity);
22+
}
23+
}
24+
}
25+
26+
trait Observer {
27+
fn update(&self, temperature: f32, humidity: f32);
28+
}
29+
30+
struct DisplayDevice {
31+
name: String,
32+
}
33+
34+
impl Observer for DisplayDevice {
35+
fn update(&self, temperature: f32, humidity: f32) {
36+
println!(
37+
"{}: Temperature = {}°C, Humidity = {}%",
38+
self.name, temperature, humidity
39+
);
40+
}
41+
}
42+
43+
fn main() {
544
let mut weather_station = WeatherStation::new();
6-
let display1 = Rc::new(RefCell::new(DisplayDevice{
45+
let display1 = Rc::new(RefCell::new(DisplayDevice {
746
name: String::from("Display 1"),
847
}));
9-
let display2 = Rc::new(RefCell::new(DisplayDevice{
48+
let display2 = Rc::new(RefCell::new(DisplayDevice {
1049
name: String::from("Display 2"),
1150
}));
1251

13-
weather_station.add_observer(display1);
14-
weather_station.add_observer(display2);
52+
weather_station.add_observer(display1.clone());
53+
weather_station.add_observer(display2.clone());
1554

1655
weather_station.set_measurements(25.5, 60.0);
1756
weather_station.set_measurements(26.0, 58.5);
18-
1957
}

DesignModule04/Singleton.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use std::collections::HashMap;
22
use std::sync::{Arc, Mutex, Once};
33
use std::fs;
44
use std::io;
5+
use std::thread;
56

67
struct ConfigManager {
78
config: HashMap<String, String>,
89
}
910

1011
impl ConfigManager {
1112
fn new() -> Result<Self, io::Error> {
12-
// Simulate loading configuration from a file
1313
let config_str = fs::read_to_string("config.toml")?;
1414
let config = parse_config(&config_str);
1515
Ok(ConfigManager { config })
@@ -24,7 +24,6 @@ impl ConfigManager {
2424
}
2525

2626
fn save(&self) -> Result<(), io::Error> {
27-
// Simulate saving configuration to a file
2827
let config_str = format_config(&self.config);
2928
fs::write("config.toml", config_str)
3029
}
@@ -52,7 +51,6 @@ fn get_config() -> Arc<Mutex<ConfigManager>> {
5251
}
5352

5453
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.
5654
config_str
5755
.lines()
5856
.filter_map(|line| {
@@ -74,26 +72,21 @@ fn format_config(config: &HashMap<String, String>) -> String {
7472
.join("\n")
7573
}
7674

77-
use std::thread;
7875

7976
fn main() -> Result<(), Box<dyn std::error::Error>> {
80-
// Simulate multiple parts of an application accessing and modifying the configuration
8177
let handles: Vec<_> = (0..3)
8278
.map(|i| {
8379
thread::spawn(move || {
8480
let config = get_config();
8581
let mut config = config.lock().unwrap();
8682

87-
// Read a configuration value
8883
let db_url = config.get("database_url").cloned().unwrap_or_default();
8984
println!("Thread {} read database_url: {}", i, db_url);
9085

91-
// Modify a configuration value
9286
let new_value = format!("new_value_from_thread_{}", i);
9387
config.set(format!("key_from_thread_{}", i), new_value.clone());
9488
println!("Thread {} set new value: {}", i, new_value);
9589

96-
// Simulate some work
9790
thread::sleep(std::time::Duration::from_millis(100));
9891
})
9992
})
@@ -103,11 +96,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
10396
handle.join().unwrap();
10497
}
10598

106-
// After all threads have finished, save the configuration
10799
let config = get_config();
108100
let config = config.lock().unwrap();
109101
config.save()?;
110102
println!("Configuration saved.");
111103

112104
Ok(())
113-
}
105+
}

DesignModule04/StockMarket/src/README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# Stock Market Monitor (Observer Pattern)
22

3-
This project demonstrates the implementation of the Observer pattern in Rust using a Stock Market monitoring system as a real-world example.
3+
This markdown content has been refactored:
44

55
## Overview
66

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.
7+
This project showcases the implementation of the Observer pattern in Rust using a Stock Market monitoring system as an example.
88

99
## Features
1010

1111
- Real-time updates of stock prices to multiple observers
12-
- Different types of observers (displays and alert systems)
12+
- Support for different types of observers (displays and alert systems)
1313
- Easy addition of new observers
1414

1515
## Mermaid Diagram
@@ -81,4 +81,4 @@ stock_market.set_price("AAPL", 150.0);
8181

8282
## Notes
8383

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.
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.
+88-15
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
use std::collections::HashMap;
2-
use std::cell::RefCell;
32
use std::rc::Rc;
3+
use std::borrow::Cow;
44

55
// Observer trait
66
trait StockObserver {
7-
fn update(&self, symbol: &str, price: f64);
7+
fn update(&self, symbol: Cow<str>, price: f64);
88
}
99

1010
// Subject (Observable)
1111
struct StockMarket {
1212
prices: HashMap<String, f64>,
13-
observers: Vec<Rc<RefCell<dyn StockObserver>>>,
13+
observers: Vec<Rc<dyn StockObserver>>,
1414
}
1515

1616
impl StockMarket {
1717
fn new() -> Self {
1818
StockMarket {
19-
prices: HashMap::new(),
20-
observers: Vec::new(),
19+
prices: HashMap::with_capacity(10),
20+
observers: Vec::with_capacity(10),
2121
}
2222
}
2323

24-
fn add_observer(&mut self, observer: Rc<RefCell<dyn StockObserver>>) {
24+
fn add_observer(&mut self, observer: Rc<dyn StockObserver>) {
2525
self.observers.push(observer);
2626
}
2727

@@ -31,8 +31,9 @@ impl StockMarket {
3131
}
3232

3333
fn notify_observers(&self, symbol: &str, price: f64) {
34+
let symbol = Cow::Borrowed(symbol);
3435
for observer in &self.observers {
35-
observer.borrow().update(symbol, price);
36+
observer.update(symbol.clone(), price);
3637
}
3738
}
3839
}
@@ -43,7 +44,7 @@ struct PriceDisplay {
4344
}
4445

4546
impl StockObserver for PriceDisplay {
46-
fn update(&self, symbol: &str, price: f64) {
47+
fn update(&self, symbol: Cow<str>, price: f64) {
4748
println!("{}: {} stock updated to ${:.2}", self.name, symbol, price);
4849
}
4950
}
@@ -53,7 +54,7 @@ struct AlertSystem {
5354
}
5455

5556
impl StockObserver for AlertSystem {
56-
fn update(&self, symbol: &str, price: f64) {
57+
fn update(&self, symbol: Cow<str>, price: f64) {
5758
if price > self.threshold {
5859
println!("ALERT: {} stock price ${:.2} exceeds threshold ${:.2}", symbol, price, self.threshold);
5960
}
@@ -63,15 +64,15 @@ impl StockObserver for AlertSystem {
6364
fn main() {
6465
let mut stock_market = StockMarket::new();
6566

66-
let display1 = Rc::new(RefCell::new(PriceDisplay {
67+
let display1 = Rc::new(PriceDisplay {
6768
name: String::from("Display 1"),
68-
}));
69-
let display2 = Rc::new(RefCell::new(PriceDisplay {
69+
});
70+
let display2 = Rc::new(PriceDisplay {
7071
name: String::from("Display 2"),
71-
}));
72-
let alert_system = Rc::new(RefCell::new(AlertSystem {
72+
});
73+
let alert_system = Rc::new(AlertSystem {
7374
threshold: 100.0,
74-
}));
75+
});
7576

7677
stock_market.add_observer(display1);
7778
stock_market.add_observer(display2);
@@ -82,4 +83,76 @@ fn main() {
8283
stock_market.set_price("GOOGL", 1330.0);
8384
stock_market.set_price("MSFT", 95.0);
8485
stock_market.set_price("MSFT", 120.0);
86+
}
87+
88+
#[cfg(test)]
89+
mod tests {
90+
use super::*;
91+
92+
#[test]
93+
fn test_stock_market() {
94+
let mut stock_market = StockMarket::new();
95+
96+
let display1 = Rc::new(PriceDisplay {
97+
name: String::from("Display 1"),
98+
});
99+
let display2 = Rc::new(PriceDisplay {
100+
name: String::from("Display 2"),
101+
});
102+
let alert_system = Rc::new(AlertSystem {
103+
threshold: 100.0,
104+
});
105+
106+
stock_market.add_observer(display1);
107+
stock_market.add_observer(display2);
108+
stock_market.add_observer(alert_system);
109+
110+
stock_market.set_price("AAPL", 15.0);
111+
stock_market.set_price("GOOGL", 1330.0);
112+
stock_market.set_price("MSFT", 95.0);
113+
stock_market.set_price("MSFT", 120.0);
114+
}
115+
116+
#[test]
117+
fn test_price_display() {
118+
let display = PriceDisplay {
119+
name: String::from("Display 1"),
120+
};
121+
display.update(Cow::Borrowed("AAPL"), 15.0);
122+
}
123+
124+
#[test]
125+
fn test_alert_system() {
126+
let alert = AlertSystem {
127+
threshold: 100.0,
128+
};
129+
alert.update(Cow::Borrowed("AAPL"), 105.0);
130+
}
131+
132+
#[test]
133+
fn test_stock_observer() {
134+
let display = PriceDisplay {
135+
name: String::from("Display 1"),
136+
};
137+
let observer: &dyn StockObserver = &display;
138+
observer.update(Cow::Borrowed("AAPL"), 15.0);
139+
}
140+
141+
#[test]
142+
fn test_stock_observer_vec() {
143+
let display1 = Rc::new(PriceDisplay {
144+
name: String::from("Display 1"),
145+
});
146+
let display2 = Rc::new(PriceDisplay {
147+
name: String::from("Display 2"),
148+
});
149+
150+
let mut observers: Vec<Rc<dyn StockObserver>> = Vec::new();
151+
observers.push(display1);
152+
observers.push(display2);
153+
154+
for observer in &observers {
155+
observer.update(Cow::Borrowed("AAPL"), 15.0);
156+
}
157+
}
85158
}

DesignModule04/config_man/Readme.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ConfigManager Singleton
22

3-
This project demonstrates the implementation of the Singleton pattern in Rust using a `ConfigManager` as a real-world example.
3+
This project demonstrates the Singleton pattern in Rust using a `ConfigManager` as a real-world example.
44

55
## Overview
66

@@ -10,8 +10,8 @@ The `ConfigManager` is a thread-safe, globally accessible configuration manager
1010

1111
- Thread-safe access to configuration
1212
- Lazy initialization
13-
- Ability to read and write configuration values
14-
- Persistence of configuration to a file
13+
- Read and write configuration values
14+
- Persist configuration to a file
1515

1616
## Mermaid Diagram
1717

@@ -32,7 +32,7 @@ graph TD
3232
G & H & I --> J[Save Config to File]
3333
```
3434

35-
## How it works
35+
## How It Works
3636

3737
1. The application requests access to the ConfigManager via the `get_config()` function.
3838
2. If it's the first access, a new ConfigManager is created, the configuration is loaded from a file, and the instance is stored in a static variable.
@@ -42,7 +42,7 @@ graph TD
4242

4343
## Usage
4444

45-
To use the ConfigManager in your code:
45+
To use the ConfigManager Singleton in your code:
4646

4747
```rust
4848
let config = get_config();
@@ -67,4 +67,4 @@ config_guard.save().unwrap();
6767

6868
## Notes
6969

70-
While the Singleton pattern can be useful for managing global state, it should be used judiciously. Consider whether dependency injection or simple module-level variables might be more appropriate for your use case.
70+
While the Singleton pattern can be useful for managing global state, it should be used judiciously. Consider whether dependency injection or simple module-level variables might be more appropriate for your use case.

0 commit comments

Comments
 (0)