-
Notifications
You must be signed in to change notification settings - Fork 18
/
03-Api_OO_Core.fsx
166 lines (136 loc) · 4.88 KB
/
03-Api_OO_Core.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
(* ======================================
03-Api_OO_Core.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 #3: API (OO Approach) -- OO API calling stateful core class
In this design, an API layer communicates with a turtle class
and the client talks to the API layer.
The input to the API are strings, and so the API validates the
input and returns a Result containing any errors.
====================================== *)
#load "Common.fsx"
#load "OOTurtleLib.fsx"
open Common
// ======================================
// Turtle Api Layer
// ======================================
module TurtleApiLayer =
open OOTurtleLib
/// Define the exception for API errors
exception TurtleApiException of string
/// Function to log a message
let log message =
printfn "%s" message
type TurtleApi() =
let turtle = Turtle(log)
// convert the distance parameter to a float, or throw an exception
let validateDistance distanceStr =
try
float distanceStr
with
| ex ->
let msg = sprintf "Invalid distance '%s' [%s]" distanceStr ex.Message
raise (TurtleApiException msg)
// convert the angle parameter to a float<Degrees>, or throw an exception
let validateAngle angleStr =
try
(float angleStr) * 1.0<Degrees>
with
| ex ->
let msg = sprintf "Invalid angle '%s' [%s]" angleStr ex.Message
raise (TurtleApiException msg)
// convert the color parameter to a PenColor, or throw an exception
let validateColor colorStr =
match colorStr with
| "Black" -> Black
| "Blue" -> Blue
| "Red" -> Red
| _ ->
let msg = sprintf "Color '%s' is not recognized" colorStr
raise (TurtleApiException msg)
/// Execute the command string, or throw an exception
/// (Exec : commandStr:string -> unit)
member this.Exec (commandStr:string) =
let tokens = commandStr.Split(' ') |> List.ofArray |> List.map trimString
match tokens with
| [ "Move"; distanceStr ] ->
let distance = validateDistance distanceStr
turtle.Move distance
| [ "Turn"; angleStr ] ->
let angle = validateAngle angleStr
turtle.Turn angle
| [ "Pen"; "Up" ] ->
turtle.PenUp()
| [ "Pen"; "Down" ] ->
turtle.PenDown()
| [ "SetColor"; colorStr ] ->
let color = validateColor colorStr
turtle.SetColor color
| _ ->
let msg = sprintf "Instruction '%s' is not recognized" commandStr
raise (TurtleApiException msg)
// ======================================
// Turtle Api Client
// ======================================
module TurtleApiClient =
open TurtleApiLayer
let drawTriangle() =
let api = TurtleApi()
api.Exec "Move 100"
api.Exec "Turn 120"
api.Exec "Move 100"
api.Exec "Turn 120"
api.Exec "Move 100"
api.Exec "Turn 120"
// back home at (0,0) with angle 0
let drawThreeLines() =
let api = TurtleApi()
// draw black line
api.Exec "Pen Down"
api.Exec "SetColor Black"
api.Exec "Move 100"
// move without drawing
api.Exec "Pen Up"
api.Exec "Turn 90"
api.Exec "Move 100"
api.Exec "Turn 90"
// draw red line
api.Exec "Pen Down"
api.Exec "SetColor Red"
api.Exec "Move 100"
// move without drawing
api.Exec "Pen Up"
api.Exec "Turn 90"
api.Exec "Move 100"
api.Exec "Turn 90"
// back home at (0,0) with angle 0
// draw diagonal blue line
api.Exec "Pen Down"
api.Exec "SetColor Blue"
api.Exec "Turn 45"
api.Exec "Move 100"
let drawPolygon n =
let angle = 180.0 - (360.0/float n)
let api = TurtleApi()
// define a function that draws one side
let drawOneSide() =
api.Exec "Move 100.0"
api.Exec (sprintf "Turn %f" angle)
// repeat for all sides
for i in [1..n] do
drawOneSide()
let triggerError() =
let api = TurtleApi()
api.Exec "Move bad"
// ======================================
// Turtle API tests
// ======================================
(*
TurtleApiClient.drawTriangle()
TurtleApiClient.drawThreeLines()
TurtleApiClient.drawPolygon 4
// test errors
TurtleApiClient.triggerError()
// Exception of type 'TurtleApiException' was thrown.
*)