Skip to content
Merged
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ members = [
"behavioral/strategy",
"behavioral/template-method",
"behavioral/visitor",
"creational/abstract-factory",
"creational/abstract-factory/app",
"creational/abstract-factory/app-dyn",
"creational/builder",
"creational/factory-method/maze-game",
"creational/factory-method/render-dialog",
Expand Down
12 changes: 0 additions & 12 deletions creational/abstract-factory/Cargo.toml

This file was deleted.

43 changes: 35 additions & 8 deletions creational/abstract-factory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,40 @@ without specifying their concrete classes._

## GUI Factory Example

There is a GUI Factory trait and two implementations: Windows and Mac OS factories. A factory can create buttons and checkboxes, however, depending on
thr factory subtype, it will create either Windows or Mac OS components.
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 `windows-gui` library provides Windows implementation of the base GUI.
Depends on `gui`.
3. The `macos-gui` library provides Mac OS implementation of the base GUI.
Depends on `gui`.

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 (traits) defined by the `gui` lib.

There are also 2 approaches to implementing abstract factory in Rust:
using generics and using dynamic allocation.
using generics (_static dispatch_) and using dynamic allocation
(_dynamic dispatch_).

## `main.rs`
## `app/main.rs`

Here, abstract factory is implemented via **generics** which allow the compiler
to create a code that doesn't require dynamic dispatch in runtime.
to create a code that does NOT require dynamic dispatch in runtime.

See `GuiFactory` in [gui/mod.rs](gui/mod.rs).
```rust
pub trait GuiFactory {
type B: Button;
type C: Checkbox;

fn create_button(&self) -> Self::B;
fn create_checkbox(&self) -> Self::C;
}
```

### How to Run

Expand All @@ -33,12 +55,17 @@ Windows checkbox has switched
Windows checkbox has switched
```

## `main_dyn.rs`
## `app-dyn/main.rs`

If a concrete type of abstract factory is not known at the compilation time,
then is should be implemented using `Box` pointers.

See See `GuiFactoryDynamic` in [gui/mod.rs](gui/mod.rs).
```rust
pub trait GuiFactoryDynamic {
fn create_button(&self) -> Box<dyn Button>;
fn create_checkbox(&self) -> Box<dyn Checkbox>;
}
```

### How to Run

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

[[bin]]
name = "abstract-factory-dyn"
path = "main.rs"

[dependencies]
gui = {path = "../gui", version = "0.1.0"}
macos-gui = {path = "../macos-gui", version = "0.1.0"}
windows-gui = {path = "../windows-gui", version = "0.1.0"}
25 changes: 25 additions & 0 deletions creational/abstract-factory/app-dyn/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
mod render;

use render::render;

use gui::GuiFactoryDynamic;
use macos_gui::factory::MacFactory;
use windows_gui::factory::WindowsFactory;

fn main() {
let windows = false;

// Allocate a factory object in runtime depending on unpredictable input.
let factory: &dyn GuiFactoryDynamic = if windows {
&WindowsFactory
} else {
&MacFactory
};

// Factory invocation can be inlined right here.
let button = factory.create_button();
button.press();

// Factory object can be passed to a function as a parameter.
render(factory);
}
17 changes: 17 additions & 0 deletions creational/abstract-factory/app-dyn/render.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! The code demonstrates that it doesn't depend on a concrete
//! factory implementation.

use gui::GuiFactoryDynamic;

/// Renders GUI.
pub fn render(factory: &dyn GuiFactoryDynamic) {
let button1 = factory.create_button();
let button2 = factory.create_button();
let checkbox1 = factory.create_checkbox();
let checkbox2 = factory.create_checkbox();

button1.press();
button2.press();
checkbox1.switch();
checkbox2.switch();
}
13 changes: 13 additions & 0 deletions creational/abstract-factory/app/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
edition = "2021"
name = "abstract_factory"
version = "0.1.0"

[[bin]]
name = "abstract-factory"
path = "main.rs"

[dependencies]
gui = {path = "../gui", version = "0.1.0"}
macos-gui = {path = "../macos-gui", version = "0.1.0"}
windows-gui = {path = "../windows-gui", version = "0.1.0"}
16 changes: 16 additions & 0 deletions creational/abstract-factory/app/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mod render;

use render::render;

use macos_gui::factory::MacFactory;
use windows_gui::factory::WindowsFactory;

fn main() {
let windows = true;

if windows {
render(WindowsFactory);
} else {
render(MacFactory);
}
}
20 changes: 20 additions & 0 deletions creational/abstract-factory/app/render.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! The code demonstrates that it doesn't depend on a concrete
//! factory implementation.

use gui::GuiFactory;

// Renders GUI. Factory object must be passed as a parameter to such the
// generic function with factory invocation to utilize static dispatch.
pub fn render(factory: impl GuiFactory) {
let button1 = factory.create_button();
let button2 = factory.create_button();
let checkbox1 = factory.create_checkbox();
let checkbox2 = factory.create_checkbox();

use gui::{Button, Checkbox};

button1.press();
button2.press();
checkbox1.switch();
checkbox2.switch();
}
7 changes: 7 additions & 0 deletions creational/abstract-factory/gui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
edition = "2021"
name = "gui"
version = "0.1.0"

[lib]
path = "lib.rs"
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
pub mod macos;
pub mod windows;

pub trait Button {
fn press(&self);
}
Expand Down
10 changes: 10 additions & 0 deletions creational/abstract-factory/macos-gui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
edition = "2021"
name = "macos-gui"
version = "0.1.0"

[lib]
path = "lib.rs"

[dependencies]
gui = {path = "../gui", version = "0.1.0"}
9 changes: 9 additions & 0 deletions creational/abstract-factory/macos-gui/button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use gui::Button;

pub struct MacButton;

impl Button for MacButton {
fn press(&self) {
println!("MacOS button has pressed");
}
}
9 changes: 9 additions & 0 deletions creational/abstract-factory/macos-gui/checkbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use gui::Checkbox;

pub struct MacCheckbox;

impl Checkbox for MacCheckbox {
fn switch(&self) {
println!("MacOS checkbox has switched");
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
use super::{Button, Checkbox, GuiFactory, GuiFactoryDynamic};
use gui::{Button, Checkbox, GuiFactory, GuiFactoryDynamic};

pub struct MacButton;
pub struct MacCheckbox;
pub struct MacFactory;

impl Button for MacButton {
fn press(&self) {
println!("MacOS button has pressed");
}
}
use crate::{button::MacButton, checkbox::MacCheckbox};

impl Checkbox for MacCheckbox {
fn switch(&self) {
println!("MacOS checkbox has switched");
}
}
pub struct MacFactory;

impl GuiFactory for MacFactory {
type B = MacButton;
Expand Down
3 changes: 3 additions & 0 deletions creational/abstract-factory/macos-gui/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod button;
pub mod checkbox;
pub mod factory;
32 changes: 0 additions & 32 deletions creational/abstract-factory/main.rs

This file was deleted.

39 changes: 0 additions & 39 deletions creational/abstract-factory/main_dyn.rs

This file was deleted.

10 changes: 10 additions & 0 deletions creational/abstract-factory/windows-gui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
edition = "2021"
name = "windows-gui"
version = "0.1.0"

[lib]
path = "lib.rs"

[dependencies]
gui = {path = "../gui", version = "0.1.0"}
9 changes: 9 additions & 0 deletions creational/abstract-factory/windows-gui/button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use gui::Button;

pub struct WindowsButton;

impl Button for WindowsButton {
fn press(&self) {
println!("Windows button has pressed");
}
}
9 changes: 9 additions & 0 deletions creational/abstract-factory/windows-gui/checkbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use gui::Checkbox;

pub struct WindowsCheckbox;

impl Checkbox for WindowsCheckbox {
fn switch(&self) {
println!("Windows checkbox has switched");
}
}
Loading