Skip to content

Commit 7f39a7a

Browse files
committed
Factory Method: decouple traits and implementations
The aim is to demonstrate a value of the Factory Method by decoupling interfaces and implementations. Although, the full decoupling is going to be demonstrated in the Abstract Factory example.
1 parent f8497c1 commit 7f39a7a

File tree

9 files changed

+72
-54
lines changed

9 files changed

+72
-54
lines changed

creational/factory-method/README.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ of objects that will be created._
66

77
## Maze Game
88

9-
Implementing the Factory Method pattern using **static dispatch** (generics).
10-
119
This example reproduces one from the GoF Design Patterns book:
12-
https://en.wikipedia.org/wiki/Factory_method_pattern
10+
https://en.wikipedia.org/wiki/Factory_method_pattern, implementing
11+
the Factory Method pattern using generics (_static dispatch_).
1312

1413
### How to Run
1514

@@ -32,10 +31,24 @@ Ordinary Room: #1
3231

3332
## Render Dialog
3433

35-
Implementing the Factory Method via **dynamic dispatch**.
34+
This example shows a GUI framework can organize its types into
35+
independent modules:
36+
37+
1. The `gui` module defines interfaces for all the components.
38+
It has no external dependencies.
39+
2. The `html_gui` module provides HTML implementation of the base GUI.
40+
Depends on `gui`.
41+
3. The `windows_gui` module provides Windows implementation of the base GUI.
42+
Depends on `gui`.
43+
44+
The app is a client application that can use several implementations
45+
of the GUI framework, depending on the current environment or configuration.
46+
However, most of the app code doesn't depend on specific types of GUI elements.
47+
All the client code works with GUI elements through abstract interfaces
48+
defined by the `gui` module.
3649

37-
See [Factory Method Java Example](https://refactoring.guru/design-patterns/factory-method/java/example)
38-
as a reference.
50+
The [Abstract Factory example](../abstract-factory/) demonstrates even greater
51+
separation of Factory interface and its implementations.
3952

4053
### How to Run
4154

@@ -46,7 +59,12 @@ cargo run --bin factory-method-render-dialog
4659
### Output
4760

4861
```
62+
-- No OS detected, creating the HTML GUI --
4963
<button>Test Button</button>
5064
Click! Button says - 'Hello World!'
5165
Dialog - Refresh
5266
```
67+
68+
### Reference
69+
70+
This example reproduces [Factory Method Java Example](https://refactoring.guru/design-patterns/factory-method/java/example).

creational/factory-method/render-dialog/button/mod.rs

Lines changed: 0 additions & 7 deletions
This file was deleted.

creational/factory-method/render-dialog/dialog/html.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.

creational/factory-method/render-dialog/dialog/windows.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.

creational/factory-method/render-dialog/dialog/mod.rs renamed to creational/factory-method/render-dialog/gui.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
pub mod html;
2-
pub mod windows;
3-
4-
use crate::button::Button;
1+
pub trait Button {
2+
fn render(&self);
3+
fn on_click(&self);
4+
}
55

66
/// Dialog has a factory method `create_button`.
77
///
88
/// It creates different buttons depending on a factory implementation.
99
pub trait Dialog {
10+
/// The factory method. It must be overridden with a concrete implementation.
1011
fn create_button(&self) -> Box<dyn Button>;
1112

1213
fn render(&self) {

creational/factory-method/render-dialog/button/html.rs renamed to creational/factory-method/render-dialog/html_gui.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::Button;
1+
use crate::gui::{Button, Dialog};
22

33
pub struct HtmlButton;
44

@@ -12,3 +12,12 @@ impl Button for HtmlButton {
1212
println!("Click! Button says - 'Hello World!'");
1313
}
1414
}
15+
16+
pub struct HtmlDialog;
17+
18+
impl Dialog for HtmlDialog {
19+
/// Creates an HTML button.
20+
fn create_button(&self) -> Box<dyn Button> {
21+
Box::new(HtmlButton)
22+
}
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use crate::gui::Dialog;
2+
use crate::html_gui::HtmlDialog;
3+
use crate::windows_gui::WindowsDialog;
4+
5+
pub fn initialize() -> &'static dyn Dialog {
6+
// The dialog type is selected depending on the environment settings or configuration.
7+
if cfg!(windows) {
8+
println!("-- Windows detected, creating Windows GUI --");
9+
return &WindowsDialog;
10+
} else {
11+
println!("-- No OS detected, creating the HTML GUI --");
12+
return &HtmlDialog;
13+
};
14+
}
Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1-
mod button;
2-
mod dialog;
1+
mod gui;
2+
mod html_gui;
3+
mod init;
4+
mod windows_gui;
35

4-
use dialog::{html::HtmlDialog, windows::WindowsDialog, Dialog};
6+
use init::initialize;
57

68
fn main() {
7-
// The dialog factory is constructed depending on unpredictable input
8-
// (not really unpredictable in this case, but let's imagine it).
9-
// We need to put a new object into a `Box` pointer, then it's going to be
10-
// used uniformly throughout a code.
11-
let dialog: Box<dyn Dialog> = if cfg!(windows) {
12-
Box::new(WindowsDialog)
13-
} else {
14-
Box::new(HtmlDialog)
15-
};
16-
9+
// The rest of the code doesn't depend on specific dialog types, because
10+
// it works with all dialog objects via the abstract `Dialog` trait
11+
// which is defined in the `gui` module.
12+
let dialog = initialize();
1713
dialog.render();
1814
dialog.refresh();
1915
}

creational/factory-method/render-dialog/button/windows.rs renamed to creational/factory-method/render-dialog/windows_gui.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::Button;
1+
use crate::gui::{Button, Dialog};
22

33
pub struct WindowsButton;
44

@@ -12,3 +12,12 @@ impl Button for WindowsButton {
1212
println!("Click! Hello, Windows!");
1313
}
1414
}
15+
16+
pub struct WindowsDialog;
17+
18+
impl Dialog for WindowsDialog {
19+
/// Creates a Windows button.
20+
fn create_button(&self) -> Box<dyn Button> {
21+
Box::new(WindowsButton)
22+
}
23+
}

0 commit comments

Comments
 (0)