Skip to content

Instantly share code, notes, and snippets.

@buxx
Last active February 9, 2024 14:21
Show Gist options
  • Save buxx/0f40ab3d4f8b7ebef898c70978a0b952 to your computer and use it in GitHub Desktop.
Save buxx/0f40ab3d4f8b7ebef898c70978a0b952 to your computer and use it in GitHub Desktop.
Examples of Builder in Rust, Python and TypeScript
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}")
#[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(())
}
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}`);
}
}
@benjaminaudet
Copy link

damn boi, that's beautiful code over here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment