Skip to content

Commit e0ac43a

Browse files
authored
Merge pull request #4822 from luishendrix92/main
#28 - OCaml
2 parents a05ef6c + 31418da commit e0ac43a

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
open Printf
2+
3+
(******************************************************************************)
4+
(* *)
5+
(* Liskov Substitution Principle *)
6+
(* *)
7+
(* The LSP states that subclasses of a parent class should be able to beha- *)
8+
(* ve just like their parent classes. Or the official statement: let phi(x) *)
9+
(* be a property provable about objects of type T, then phi(y) should be *)
10+
(* true for objects y ofthe type S where S is a subtype of T. *)
11+
(* All of this means that we should be able to replace objects of a super- *)
12+
(* class with objects of its subclasses without breaking the client's code. *)
13+
(* *)
14+
(* It also states some rules: *)
15+
(* 1. A subclasss method should not strengthen its pre-condition: In human- *)
16+
(* readable terms: overriden methods should not change their parameters *)
17+
(* for subtypes of those parameter types. *)
18+
(* 2. A subclass method should not weaken its post-condition: We should not *)
19+
(* return a more abstract type than the specified return type, but we *)
20+
(* can definitely return a subtype of the return type. *)
21+
(* 3. Methods should not break the invariance of the superclass' method. *)
22+
(* *)
23+
(******************************************************************************)
24+
25+
class virtual lsp_non_compliant_acount (owner' : string) =
26+
object
27+
val mutable balance = 0.0
28+
val owner = owner'
29+
method virtual deposit : float -> unit
30+
method virtual withdraw : float -> unit
31+
method show_balance = printf "Current balance for %s: $%f\n" owner balance
32+
end
33+
34+
class savings_account (owner' : string) =
35+
object
36+
inherit lsp_non_compliant_acount owner'
37+
38+
method deposit amount =
39+
balance <- balance +. amount;
40+
printf "%s deposited %f into their account!\n" owner amount
41+
42+
method withdraw amount =
43+
if balance < amount
44+
then failwith "Not enough funds for withdrawal"
45+
else begin
46+
balance <- balance -. amount;
47+
printf "%s withdrew %f from their account!\n" owner amount
48+
end
49+
end
50+
51+
class fixed_term_account (owner' : string) =
52+
object
53+
inherit lsp_non_compliant_acount owner'
54+
55+
method deposit amount =
56+
balance <- balance +. amount;
57+
printf "%s deposited %f into a fixed term!\n" owner amount
58+
59+
method withdraw = failwith "Unsupported method"
60+
end
61+
62+
let _ =
63+
(* Client Code for an LSP-breaking example *)
64+
let acct : lsp_non_compliant_acount ref =
65+
ref @@ new savings_account "Luis Lopez"
66+
in
67+
!acct#deposit 456.53;
68+
!acct#withdraw 245.35;
69+
!acct#show_balance;
70+
(* The code above worked great, but what if I were to swap the account ref
71+
value for a subclass that doen't do withdrawals and instead throws an
72+
exception which completely breaks the LSP. *)
73+
acct := new fixed_term_account "Moure Dev";
74+
!acct#deposit 456.53;
75+
begin
76+
try !acct#withdraw 245.35 with
77+
| Failure msg -> print_endline msg
78+
end;
79+
!acct#show_balance
80+
;;
81+
82+
(* Let's try to make the example LSP compliant. The most feasible solution is
83+
to stop making assumptions about the withdrawal capabilities of these accts.
84+
Next step is to break a superclass [Account] (or interface) into their own
85+
subclasses or subinterfaces with extended capabilities that other concrete
86+
classes can inherit or extend in turn. This way if the client code requires
87+
accounts that have withdrawal capabilities, then we can start using our
88+
specialized subtype that performs a withdrawal operation. *)
89+
90+
class virtual account (owner' : string) =
91+
object
92+
val mutable balance = 0.0
93+
val owner = owner'
94+
method virtual deposit : float -> unit
95+
method show_balance = printf "Current balance for %s: $%f\n" owner balance
96+
end
97+
98+
class virtual withdrawable owner' =
99+
object
100+
inherit account owner'
101+
method virtual withdraw : float -> unit
102+
end
103+
104+
class fixed_term owner' =
105+
object
106+
inherit account owner'
107+
108+
(* Any other methods or members corresponding to a specialized fixed-term
109+
class go here, otherwise there would be no point in extending.*)
110+
method deposit amount =
111+
balance <- balance +. amount;
112+
printf "%s deposited %f into their fixed term!\n" owner amount
113+
end
114+
115+
class savings owner' =
116+
object
117+
inherit withdrawable owner'
118+
119+
method deposit amount =
120+
balance <- balance +. amount;
121+
printf "%s deposited %f into their savings account!\n" owner amount
122+
123+
method withdraw amount =
124+
if balance < amount
125+
then failwith "Not enough funds for withdrawal"
126+
else begin
127+
balance <- balance -. amount;
128+
printf "%s withdrew %f from their savings account!\n" owner amount
129+
end
130+
end
131+
132+
let _ =
133+
(* Client Code for an LSP-compliant example *)
134+
let acct : withdrawable = new savings_account "Luis Lopez" in
135+
acct#deposit 319.35;
136+
acct#withdraw 300.00;
137+
acct#show_balance
138+
;;
139+
140+
(* Now, the client code is specific in that it only accepts subclasses that
141+
are 'withdrawable' and if I were to pass it an instance of [fixed_term]
142+
then the compiler would let us know, thus not breaking the LSP.
143+
144+
The compiler error says:
145+
- This expression has type fixed_term but an expression was expdected of
146+
type withdrawable The first object type has no method withdraw. *)
147+
(* let acct2 : withdrawable = new fixed_term "John Doe" in *)
148+
(* acct#withdraw 100.0 *)
149+
150+
(*
151+
* DIFICULTAD EXTRA (opcional):
152+
* ============================
153+
* Crea una jerarquía de vehículos. Todos ellos deben poder acelerar y frenar, así como
154+
* cumplir el LSP.
155+
* Instrucciones:
156+
* 1. Crea la clase Vehículo.
157+
* 2. Añade tres subclases de Vehículo.
158+
* 3. Implementa las operaciones "acelerar" y "frenar" como corresponda.
159+
* 4. Desarrolla un código que compruebe que se cumple el LSP.
160+
*)
161+
162+
let clamp a b x = if x < a then a else if x > b then b else x
163+
164+
class virtual vehicle (top_speed' : float) (accel : float) =
165+
object
166+
val top_speed = top_speed'
167+
val acceleration = accel
168+
val mutable speed = 0.0
169+
170+
method accelerate' =
171+
let clamp_speed = clamp 0.0 top_speed in
172+
speed <- clamp_speed (speed +. acceleration)
173+
174+
method virtual accelerate : unit
175+
method get_speed = speed
176+
method brake = speed <- 0.0
177+
end
178+
179+
class boat =
180+
object (self)
181+
inherit vehicle 80.0 1.52
182+
method anchor = print_endline "Anchoring boat..."
183+
184+
method accelerate =
185+
self#accelerate';
186+
print_endline "Boat accelerated"
187+
end
188+
189+
class motorbike =
190+
object (self)
191+
inherit vehicle 186.0 25.4
192+
193+
method accelerate =
194+
self#accelerate';
195+
print_endline "Motorbike accelerated"
196+
end
197+
198+
class tesla =
199+
object (self)
200+
inherit vehicle 200.0 9.82
201+
val mutable charge = 0
202+
203+
method accelerate =
204+
self#accelerate';
205+
charge <- clamp 0 100 (charge - 1);
206+
print_endline "Tesla accelerated"
207+
208+
method recharge = charge <- 100
209+
end
210+
211+
let _ =
212+
let v : vehicle = new motorbike in
213+
print_newline ();
214+
for i = 1 to 10 do
215+
v#accelerate;
216+
printf "Motorbike's speed: %f\n" v#get_speed
217+
done;
218+
v#brake;
219+
print_endline "Invoking #brake on a motorbike";
220+
printf "Motorbike's speed: %f\n" v#get_speed
221+
;;

0 commit comments

Comments
 (0)