Skip to content

Instantly share code, notes, and snippets.

@vkostyukov
Created April 5, 2014 12:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vkostyukov/9991383 to your computer and use it in GitHub Desktop.
Save vkostyukov/9991383 to your computer and use it in GitHub Desktop.
/**
* This is an immutable builder patter implemented in Scala within OO style.
*
* There is might be a better approach (i.e., pattern matching, case classes, etc)
* in implementing this idea within FP style, but the overall picture is robust:
*
* (a) there is no mutable state
* (b) it's a pure FSM (Finite State Machine)
*
* I'll try to keep this gist updated along with any new ideas regarding this
* implementation. Please, do not hesitate to suggest any ideas.
*
* Why do we need such things like an immutable builder?
*
* 1. It's fun
* 2. It deals with parameters with no default values
* 3. It avoids the cascade of if-elseif-else blocks for the complex building logic
* (i.e., depending on the specified parameters some object post-processing
* may be involved or different object types may be produced)
*
* How to use this builder?
*
* val o = TerminalBuilder // always start from a terminal state
* .foo(10) // state-transition: Terminal -> Foo
* .bar("Bar") // state-transition: Foo -> Bar
* .build // commits the building process and rollbacks the state-machine to
* // to its initial state (a terminal state) along with accumulating
* // parameters being used for final building
*/
// A placeholder for a type being built
object Obj
// An interface being exposed to user via API
trait ImmutableBuilder {
def foo(i: Int): ImmutableBuilder
def bar(s: String): ImmutableBuilder
def build: Obj
}
// An internal builder API
trait UnderlyingBuilder extends ImmutableBuilder { self =>
// state transition routines
def foo(i: Int): ImmutableBuilder = new FooedBuilder(i, self)
def bar(s: String): ImmutableBuilder = new BarredBuilder(s, self)
// an internal API for building
def build(i: Int): Obj
def build(s: String): Obj
def build(i: Int, s: String): Obj
}
// A non-terminal builder, which should be inherited
abstract class NonTerminalBuilder(underlying: UnderlyingBuilder)
with UnderlyingBuilder {
// just forward all the request
def build(i: Int) = underlying.build(i)
def build(s: String) = underlying.build(s)
def build(i: Int, s: String) = underlying.build(i, s)
}
// Foo state
class FooedBuilder(i: Int, underlying: UnderlyingBuilder)
extends NonTerminalBuilder(underlying) {
// there's something new we can tell the underlying builder -
// a new integer parameter
def build = underlying.build(i)
def build(s: String) = underlying.build(i, s)
}
// Bar state
class BarredBuilder(s: String, underlying: UnderlyingBuilder)
extends NonTerminalBuilder(underlying) {
// there's something new we can tell the underlying builder -
// a new string parameter
def build = underlying.build(s)
def build(i: Int) = underlying.build(i, s)
}
// A terminal state builder
object TerminalBuilder
with UnderlyingBuilder {
// an actual building process
// every methods represents a final state within a finite set of
// available parameters that should be used for building of a concrete instance
def build = Obj
def build(i: Int) = Obj
def build(s: String) = Obj
def build(i: Int, s: String) = Obj
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment