Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"creational/builder",
"creational/factory-method/maze-game",
"creational/factory-method/render-dialog",
"creational/factory-method/render-dialog-alternative/app",
"creational/prototype",
"creational/simple-factory",
"creational/singleton",
Expand Down
9 changes: 6 additions & 3 deletions creational/factory-method/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ Ordinary Room: #1

## Render Dialog

Implementing the Factory Method via **dynamic dispatch**.
This example shows a GUI framework can organize its classes into independent libraries:

1. The `gui` library defines interfaces for all the components. It has no external dependencies.
2. The `html_gui` library provides HTML implementation of the base GUI. Depends on `gui`.
3. The `windows_gui` library provides Windows implementation of the base GUI. Depends on `gui`.

See [Factory Method Java Example](https://refactoring.guru/design-patterns/factory-method/java/example)
as a reference.
The `app` is a client application that can use several implementations of the GUI framework, depending on the current environment or configuration. However, most of the `app` code doesn't depend on specific types of GUI elements. All the client code works with GUI elements through abstract interfaces defined by the `gui` lib.

### How to Run

Expand Down
13 changes: 13 additions & 0 deletions creational/factory-method/render-dialog-alternative/app/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
edition = "2021"
name = "render-dialog-alternative"
version = "1.0.0"

[[bin]]
name = "factory-method-render-dialog"
path = "main.rs"

[dependencies]
gui = { path = "../gui", version = "1.0" }
html_gui = { path = "../html_gui", version = "1.0" }
windows_gui = { path = "../windows_gui", version = "1.0" }
14 changes: 14 additions & 0 deletions creational/factory-method/render-dialog-alternative/app/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use gui::dialog::Dialog;
use html_gui::dialog::HtmlDialog;
use windows_gui::dialog::WindowsDialog;

pub fn initialize() -> Box<dyn Dialog> {
// The dialog type is selected depending on the environment settings or configuration.
if cfg!(windows) {
println!("-- Windows detected, creating Windows GUI --");
return Box::new(WindowsDialog)
} else {
println!("-- No OS detected, creating the HTML GUI --");
return Box::new(HtmlDialog)
};
}
12 changes: 12 additions & 0 deletions creational/factory-method/render-dialog-alternative/app/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use gui::dialog::Dialog;

mod init;
use crate::init::initialize;

fn main() {
// The rest of the code doesn't depend on specific dialog types, because it works with all
// dialog objects via the abstract Dialog trait.
let dialog: Box<dyn Dialog> = initialize();
dialog.render();
dialog.refresh();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
edition = "2021"
name = "gui"
version = "1.0.0"

[lib]
path = "lib.rs"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub trait Button {
fn render(&self);
fn on_click(&self);
}
18 changes: 18 additions & 0 deletions creational/factory-method/render-dialog-alternative/gui/dialog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::button::Button;

/// Dialog has a factory method `create_button`.
///
/// It creates different buttons depending on a factory implementation.
pub trait Dialog {
fn create_button(&self) -> Box<dyn Button>;

fn render(&self) {
println!("Dialog: Using base render method.");
let button = self.create_button();
button.render();
}

fn refresh(&self) {
println!("Dialog: Using base refresh method.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod button;
pub mod dialog;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
edition = "2021"
name = "html_gui"
version = "1.0.0"

[lib]
path = "lib.rs"

[dependencies]
gui = { path = "../gui", version = "1.0" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use gui::button::Button;

pub struct HtmlButton;

impl Button for HtmlButton {
fn render(&self) {
println!("<button>Test Button</button>");
self.on_click();
}

fn on_click(&self) {
println!("Button: I'm an HTML button!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use gui::button::Button;
use gui::dialog::Dialog;

use crate::button::HtmlButton;

pub struct HtmlDialog;

impl Dialog for HtmlDialog {
fn create_button(&self) -> Box<dyn Button> {
Box::new(HtmlButton)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod button;
pub mod dialog;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
edition = "2021"
name = "windows_gui"
version = "1.0.0"

[lib]
path = "lib.rs"

[dependencies]
gui = { path = "../gui", version = "1.0" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use gui::button::Button;

pub struct WindowsButton;

impl Button for WindowsButton {
fn render(&self) {
println!("Drawing a Windows button");
self.on_click();
}

fn on_click(&self) {
println!("Button: I'm a Windows button!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use gui::button::Button;
use gui::dialog::Dialog;

use crate::button::WindowsButton;

pub struct WindowsDialog;

impl Dialog for WindowsDialog {
fn create_button(&self) -> Box<dyn Button> {
Box::new(WindowsButton)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod button;
pub mod dialog;