Last active
February 9, 2024 14:21
-
-
Save buxx/0f40ab3d4f8b7ebef898c70978a0b952 to your computer and use it in GitHub Desktop.
Examples of Builder in Rust, Python and TypeScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import dataclasses | |
import abc | |
import typing | |
@dataclasses.dataclass | |
class Person: | |
name: str | |
class RuleSet(abc.ABC): | |
@abc.abstractmethod | |
def min_players(self) -> int: | |
pass | |
@abc.abstractmethod | |
def max_players(self) -> int: | |
pass | |
@abc.abstractmethod | |
def need_referee(self) -> bool: | |
pass | |
@dataclasses.dataclass | |
class Game[T: RuleSet]: | |
ruleset: T | |
referee: typing.Optional[Person] | |
players: typing.List[Person] | |
class GameBuilder[T: RuleSet]: | |
ruleset: T | |
referee: typing.Optional[Person] | |
players: typing.List[Person] | |
def __init__(self, ruleset: T) -> None: | |
self.ruleset = ruleset | |
self.referee = None | |
self.players = [] | |
def referee(self, value: typing.Optional[Person]) -> "GameBuilder": | |
self._referee = value | |
return self | |
def players(self, value: typing.List[Person]) -> "GameBuilder": | |
self._players = value | |
return self | |
def build(self) -> "GameBuilder": | |
if ( | |
self.players.len() < self.ruleset.min_players() | |
or self.players.len() > self.ruleset.max_players() | |
or self.referee.is_some() != self.ruleset.need_referee() | |
): | |
raise NotRespectedGameRuleset() | |
return Game(self.ruleset, self._referee, self._players) | |
class GameBuilderError(Exception): | |
pass | |
class NotRespectedGameRuleset(GameBuilderError): | |
pass | |
class Football(RuleSet): | |
def min_players(self) -> int: | |
return 12 | |
def max_players(self) -> int: | |
return | |
def need_referee(self) -> bool: | |
return True | |
if __name__ == "__main__": | |
players = [] # Players... | |
john = Person("John") | |
try: | |
football_game = ( | |
GameBuilder(Football()) | |
.players(players) | |
.referee(john) | |
.build() | |
) | |
except GameBuilderError as exc: | |
print(f"ERROR: {exc}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[derive(Debug)] | |
struct Person { | |
name: String, | |
} | |
trait RuleSet { | |
fn min_players(&self) -> usize; | |
fn max_players(&self) -> usize; | |
fn need_referee(&self) -> bool; | |
} | |
#[derive(Debug)] | |
struct Game<T: RuleSet> { | |
ruleset: T, | |
referee: Option<Person>, | |
players: Vec<Person>, | |
} | |
struct GameBuilder<T: RuleSet> { | |
ruleset: T, | |
referee: Option<Person>, | |
players: Vec<Person>, | |
} | |
impl<T: RuleSet> GameBuilder<T> { | |
fn new(ruleset: T) -> Self { | |
Self { | |
ruleset, | |
referee: None, | |
players: vec![], | |
} | |
} | |
fn referee(mut self, value: Option<Person>) -> Self { | |
self.referee = value; | |
self | |
} | |
fn players(mut self, value: Vec<Person>) -> Self { | |
self.players = value; | |
self | |
} | |
fn build(self) -> Result<Game<T>, GameBuilderError> { | |
if self.players.len() < self.ruleset.min_players() | |
|| self.players.len() > self.ruleset.max_players() | |
|| self.referee.is_some() != self.ruleset.need_referee() | |
{ | |
return Err(GameBuilderError::NotRespectedGameRuleset); | |
} | |
Ok(Game { | |
ruleset: self.ruleset, | |
referee: self.referee, | |
players: self.players, | |
}) | |
} | |
} | |
#[derive(Debug)] | |
enum GameBuilderError { | |
NotRespectedGameRuleset, | |
} | |
#[derive(Debug)] | |
struct Football; | |
impl RuleSet for Football { | |
fn min_players(&self) -> usize { | |
12 | |
} | |
fn max_players(&self) -> usize { | |
12 | |
} | |
fn need_referee(&self) -> bool { | |
true | |
} | |
} | |
fn main() -> Result<(), GameBuilderError> { | |
let players = vec![]; // [...] | |
let john = Person { | |
name: "John".to_string(), | |
}; | |
let football_game = GameBuilder::new(Football) | |
.players(players) | |
.referee(Some(john)) | |
.build()?; | |
println!("Result : {:?}", football_game); | |
Ok(()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Person { | |
name: string; | |
} | |
interface RuleSet { | |
min_players(): number; | |
max_players(): number; | |
need_referee(): boolean; | |
} | |
class Game<T extends RuleSet> { | |
ruleset: T; | |
referee: Person | null; | |
players: Person[]; | |
constructor(ruleset: T, referee: Person | null, players: Person[]) { | |
this.ruleset = ruleset; | |
this.referee = referee; | |
this.players = players; | |
} | |
} | |
class GameBuilder<T extends RuleSet> { | |
ruleset: T; | |
_referee: Person | null; | |
_players: Person[]; | |
constructor(ruleset: T) { this.ruleset = ruleset; this._referee = null; this._players = []; } | |
referee(value: Person | null): GameBuilder<T> { | |
this._referee = value; | |
return this; | |
} | |
players(value: Person[]): GameBuilder<T> { | |
this._players = value; | |
return this; | |
} | |
build(): Game<T> { | |
if ( | |
this._players.length < this.ruleset.min_players() || | |
this._players.length > this.ruleset.max_players() || | |
(this._referee !== null) !== this.ruleset.need_referee() | |
) { | |
throw GameBuilderError.NotRespectedGameRuleset | |
} | |
return new Game(this.ruleset, this._referee, this._players); | |
} | |
} | |
enum GameBuilderError { | |
NotRespectedGameRuleset, | |
} | |
class Football implements RuleSet { | |
min_players(): number { | |
return 12; | |
} | |
max_players(): number { | |
return 12; | |
} | |
need_referee(): boolean { | |
return true; | |
} | |
} | |
function main() { | |
const players: Person[] = [/** players... */]; | |
const john: Person = { name: "John" }; | |
try { | |
const football_game = new GameBuilder(new Football()) | |
.players(players) | |
.referee(john) | |
.build(); | |
console.log(`OK: ${football_game}`); | |
} catch (e: any){ | |
console.log(`ERROR: ${e}`); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
damn boi, that's beautiful code over here