F#: Domain Driven Design (Refactoring)

Intro

In my last post, I discussed my attempt at making illegal states unrepresentable. In this post I will discuss the opportunities I had to refactor that code. Thus, I will show a cleaner implementation.

Refactoring

For implementing field shots, I had wrote the following functions:

let makeTwoPointer (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, TwoPointer) }, defender

let makeThreePointer (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, ThreePointer) }, defender

For implementing foul shots, I had wrote the following functions:

let makeFoulShot  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, FoulShot) }, defender

let makeTwoFoulShots  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, TwoFoulShots) }, defender

let makeThreeFoulShots  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, ThreeFoulShots) }, defender

Thus, my client’s implementation was the following:

let results = (player1, player2) |> makeTwoPointer
                                 |> makeFoulShot
                                 |> turnover

                                 |> makeTwoPointer
                                 |> makeTwoFoulShots
                                 |> turnover

The code-smell with the functions that I wrote is that they’re duplicated with a difference in one argument when returning the result of the function. Thus, I decided to refactor this model to reduce the amount of code so that I can have better maintainability.

As a result, I refactored making foul shots from:

let makeFoulShot  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, FoulShot) }, defender

let makeTwoFoulShots  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, TwoFoulShots) }, defender

let makeThreeFoulShots  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, ThreeFoulShots) }, defender

To:

let makeFoulShots foulShots (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, foulShots) }, defender

Then, I refactored making field shots from:

let makeTwoPointer (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, TwoPointer) }, defender

let makeThreePointer (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, ThreePointer) }, defender

To:

let makeFieldBasket fieldBasket (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, fieldBasket) }, defender

Thus, my client was then updated from:

let results = (player1, player2) |> makeTwoPointer
                                 |> makeFoulShot
                                 |> turnover

                                 |> makeTwoPointer
                                 |> makeTwoFoulShots
                                 |> turnover

To:

let results = (player1, player2) |> makeFieldBasket TwoPointer
                                 |> makeFoulShots   FoulShot
                                 |> turnover

                                 |> makeFieldBasket TwoPointer
                                 |> makeFoulShots   TwoFoulShots
                                 |> turnover

The old domain model was this:

(*Types*)
type Player = { Score:int }

type FieldShot = TwoPointer| ThreePointer
type FoulShots = FoulShot  | TwoFoulShots | ThreeFoulShots

type FoulShooter  = FoulShooter  of Player
type FieldShooter = FieldShooter of Player

(*Functions*)
let shoot lastShot player =
    (player.Score + lastShot)

let turnover (shooter, defender) =
    (defender, shooter)

// ** Field shots **
let fieldShot (fieldShooter, shot) =

    let player = match fieldShooter with
                 | FieldShooter player -> player

    match player.Score with
    | score when score >= 33 -> score
    | _ ->  match (fieldShooter, shot) with
            | FieldShooter player, shot -> match shot with
                                           | TwoPointer   -> player |> shoot 2
                                           | ThreePointer -> player |> shoot 3
let makeTwoPointer (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, TwoPointer) }, defender

let makeThreePointer (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, ThreePointer) }, defender

// ** Foul shots **
let foulShot (foulShooter, shot) =

    let player = match foulShooter with
                 | FoulShooter player -> player

    match player.Score with
    | score when score >= 33 -> score
    | _ ->  match (foulShooter, shot) with
            | FoulShooter player, shot -> match shot with
                                          | FoulShot       -> player |> shoot 1
                                          | TwoFoulShots   -> player |> shoot 2
                                          | ThreeFoulShots -> player |> shoot 3
let makeFoulShot  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, FoulShot) }, defender

let makeTwoFoulShots  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, TwoFoulShots) }, defender

let makeThreeFoulShots  (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, ThreeFoulShots) }, defender

The refactored domain model is the following:

(*Types*)
type Player = { Score:int }

type FieldShot = TwoPointer| ThreePointer
type FoulShots = FoulShot  | TwoFoulShots | ThreeFoulShots

type FoulShooter  = FoulShooter  of Player
type FieldShooter = FieldShooter of Player

(*Functions*)
let shoot lastShot player =
    (player.Score + lastShot)

let fieldShot (fieldShooter, shot) =

    let player = match fieldShooter with
                 | FieldShooter player -> player

    match player.Score with
    | score when score >= 33 -> score
    | _ ->  match (fieldShooter, shot) with
            | FieldShooter player, shot -> match shot with
                                           | TwoPointer   -> player |> shoot 2
                                           | ThreePointer -> player |> shoot 3

let makeFieldBasket fieldBasket (shooter, defender) =
    FoulShooter { Score= fieldShot (shooter, fieldBasket) }, defender

let foulShot (foulShooter, shot) =

    let player = match foulShooter with
                 | FoulShooter player -> player

    match player.Score with
    | score when score >= 33 -> score
    | _ ->  match (foulShooter, shot) with
            | FoulShooter player, shot -> match shot with
                                          | FoulShot       -> player |> shoot 1
                                          | TwoFoulShots   -> player |> shoot 2
                                          | ThreeFoulShots -> player |> shoot 3

let makeFoulShots foulShots (shooter, defender) =
    FieldShooter { Score= foulShot (shooter, foulShots) }, defender

let turnover (shooter, defender) =
    (defender, shooter)

Conclusion

In conclusion, In my last post, I discussed my attempt at making illegal states unrepresentable. In this post I discussed the opportunities I had to refactor that code. I then refactored it.

Advertisements
4 comments
  1. Hi Scott. I appreciate what you are doing with the F# exploration. Are you still looking for a contract? I emailed Developer on Hire host, Dave Ruel, and to make a long story short: I might have been wrong in saying you were looking. I unfortunately do not have any leads for you but I do have a related idea. If you email/message me your email address then I will say more. I follow you on Twitter so I believe you can message me.

    • Hi Rick. Yes, I’m still looking for my next contract in Miami.
      I appreciate any help for securing my next opportunity here.

      My email is the following:
      scott.nimrod bizmonger.net

  2. On a different note, I believe you want “had written” instead of “had wrote” in the post. I totally understand if you delete this comment.

    • Rick Pack said:

      I researched this for a bit and I have no idea which is correct. Either “had written” or “had wrote” is easily deciphered so I think the answer is, “no one cares”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: