-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy path07-DependencyInjection_Functions-1.fsx
148 lines (112 loc) · 4.66 KB
/
07-DependencyInjection_Functions-1.fsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
(* ======================================
07-DependencyInjection_Functions-1.fsx
Part of "Thirteen ways of looking at a turtle"
Related blog post: http://fsharpforfunandprofit.com/posts/13-ways-of-looking-at-a-turtle/
======================================
Way #7: Dependency injection using functions (v1: pass in all functions)
In this design, an API layer communicates via one or more functions that are passed in as parameters to the API call.
These functions are typically partially applied so that the call site is decoupled from the "injection"
No interface is passed to the constructor.
====================================== *)
#load "Common.fsx"
#load "FPTurtleLib.fsx"
#load "TurtleApiHelpers.fsx"
open Common
open FPTurtleLib
open TurtleApiHelpers // helpers for API validation, etc
// ======================================
// TurtleApi - all Turtle functions are passed in as parameters
// ======================================
module TurtleApi_PassInAllFunctions =
open Result
// No functions in constructor
type TurtleApi() =
let mutable state = Turtle.initialTurtleState
/// Update the mutable state value
let updateState newState =
state <- newState
/// Execute the command string, and return a Result
/// Exec : commandStr:string -> Result<unit,ErrorMessage>
member this.Exec move turn penUp penDown setColor (commandStr:string) =
let tokens = commandStr.Split(' ') |> List.ofArray |> List.map trimString
// return Ok of unit, or Error
match tokens with
| [ "Move"; distanceStr ] -> result {
let! distance = validateDistance distanceStr
let newState = move distance state // use `move` function that was passed in
updateState newState
}
| [ "Turn"; angleStr ] -> result {
let! angle = validateAngle angleStr
let newState = turn angle state // use `turn` function that was passed in
updateState newState
}
| [ "Pen"; "Up" ] -> result {
let newState = penUp state
updateState newState
}
| [ "Pen"; "Down" ] -> result {
let newState = penDown state
updateState newState
}
| [ "SetColor"; colorStr ] -> result {
let! color = validateColor colorStr
let newState = setColor color state
updateState newState
}
| _ ->
Error (InvalidCommand commandStr)
// -----------------------------
// Turtle Implementations for "Pass in all functions" design
// -----------------------------
module TurtleImplementation_PassInAllFunctions =
open TurtleApi_PassInAllFunctions
let log = printfn "%s"
let move = Turtle.move log
let turn = Turtle.turn log
let penUp = Turtle.penUp log
let penDown = Turtle.penDown log
let setColor = Turtle.setColor log
let normalSize() =
let api = TurtleApi()
// partially apply the functions
api.Exec move turn penUp penDown setColor
// the return value is a function:
// string -> Result<unit,ErrorMessage>
let halfSize() =
let moveHalf dist = move (dist/2.0)
let api = TurtleApi()
// partially apply the functions
api.Exec moveHalf turn penUp penDown setColor
// the return value is a function:
// string -> Result<unit,ErrorMessage>
// -----------------------------
// Turtle API Client for "Pass in all functions" design
// -----------------------------
module TurtleApiClient_PassInAllFunctions =
open Result
// the API type is just a function
type ApiFunction = string -> Result<unit,ErrorMessage>
let drawTriangle(api:ApiFunction) =
result {
do! api "Move 100"
do! api "Turn 120"
do! api "Move 100"
do! api "Turn 120"
do! api "Move 100"
do! api "Turn 120"
} |> ignore
// -----------------------------
// Turtle Api Tests for "Pass in all functions" design
// -----------------------------
do
let apiFn = TurtleImplementation_PassInAllFunctions.normalSize() // string -> Result<unit,ErrorMessage>
TurtleApiClient_PassInAllFunctions.drawTriangle(apiFn)
do
let apiFn = TurtleImplementation_PassInAllFunctions.halfSize()
TurtleApiClient_PassInAllFunctions.drawTriangle(apiFn)
do
let mockApi s =
printfn "[MockAPI] %s" s
Ok ()
TurtleApiClient_PassInAllFunctions.drawTriangle(mockApi)