1+ ### Principio SOLID de Sustitución de Liskov (Liskov Substitution Principle, LSP)
2+
3+ '''
4+ LSP establece que: Un objeto de una subclase debe ser sustituible por un objeto de su clase base
5+ sin alterar el correcto funcionamiento del programa.
6+
7+ LSP se centra en garantizar que las subclases no modifiquen el comportamiento esperado de la clase base.
8+ En otras palabras, cualquier clase hija debe ser capaz de reemplazar a su clase padre sin introducir errores o
9+ comportamientos inesperados.
10+ '''
11+ '''
12+ Ventajas:
13+ - Mejora la reutilización de código.
14+ - Aporta coherencia en la jerarquía de clases.
15+ - Ayuda a evitar la dependencia de la implementación.
16+ '''
17+
18+ # Ejemplo que viola LSP
19+ # Clase Base
20+ class CuentaBancaria ():
21+ def __init__ (self , saldo ):
22+ self .saldo = saldo
23+
24+ def retirar (self , monto ):
25+ if monto <= self .saldo :
26+ self .saldo -= monto
27+ else :
28+ raise ValueError ("Saldo insuficiente para retirar de la cuenta" )
29+
30+ # Clase Derivada
31+ class CuentaAhorros (CuentaBancaria ):
32+
33+ def retirar (self , monto ):
34+ raise NotImplementedError ("No se puede realizar retirada de la cuenta ahorro" )
35+
36+ # Al aplicar polimorfismo en este caso está violando el principio de Sustitución de Liskov
37+ '''
38+ CuentaAhorros no puede ser sustituida por CuentaBancaria sin alterar
39+
40+ def procesar_retiro(cuenta, monto):
41+ cuenta.retirar(monto)
42+
43+ cuenta = CuentaBancaria(1000)
44+ procesar_retiro(cuenta, 100) # Funciona correctamente
45+
46+ cuenta_ahorros = CuentaAhorros(1000)
47+ procesar_retiro(cuenta_ahorros, 100) # Lanza NotImplementedError
48+ '''
49+
50+ # Arreglemos el ejemplo empleando abstracciones para restricciones específicas
51+ from abc import ABC , abstractmethod
52+
53+ class CuentaBancaria (ABC ):
54+
55+ def __init__ (self , saldo ):
56+ self .saldo = saldo
57+
58+ @abstractmethod
59+ def retirar (self , monto ):
60+ pass
61+
62+ class CuentaCorriente (CuentaBancaria ):
63+ def retirar (self , monto ):
64+ if monto <= self .saldo :
65+ self .saldo -= monto
66+ print ("Retirada de efectivo realizada correctamente" )
67+ else :
68+ raise ValueError ("Su saldo es inferior a la cantidad a retirar, abortando operación" )
69+
70+ class CuentaAhorros (CuentaBancaria ):
71+ def retirar (self , monto ):
72+ if monto > self .saldo :
73+ raise ValueError ("Saldo insuficiente, abortando operación" )
74+
75+ if monto >= 500 :
76+ raise ValueError ("La retirada máxima para este tipo de cuenta es de 500€" ) # Restricción específica para esta clase
77+
78+ self .saldo -= monto
79+
80+ # Prueba de funcionamiento correcto
81+ def procesar_retirada (cuenta , monto ):
82+ try :
83+ cuenta .retirar (monto )
84+ print (f"Proceso finalizado, su saldo actual tras la operación es de { cuenta .saldo } €" )
85+ except ValueError as e :
86+ print (e )
87+
88+ c1 = CuentaCorriente (1000 )
89+ c2 = CuentaAhorros (2000 )
90+
91+ procesar_retirada (c1 , 500 ) # Funciona correctamente
92+ procesar_retirada (c2 , 200 ) # Funciona correctamente
93+ procesar_retirada (c2 , 700 ) # Lanza ValueError
94+
95+ ### EJERCICIO EXTRA
96+
97+ from abc import ABC , abstractmethod
98+
99+ class Vehicle (ABC ):
100+ def __init__ (self , type , max_speed , speed = 0 ):
101+ self .type = type
102+ self .max_speed = max_speed
103+ self .speed = speed if speed <= max_speed else max_speed
104+
105+ @abstractmethod
106+ def speed_up (self , qty ):
107+ pass
108+
109+ @abstractmethod
110+ def speed_down (self , qty ):
111+ pass
112+
113+
114+ class Car (Vehicle ):
115+ def speed_up (self , qty ):
116+ if self .speed + qty <= self .max_speed :
117+ self .speed += qty
118+ print (f"Acelerar terminado, velocidad actual: { self .speed } " )
119+ else :
120+ raise ValueError ("La cantidad a acelerar supera la velocidad máxima del vehículo" )
121+
122+ def speed_down (self , qty ):
123+ if self .speed - qty >= 0 :
124+ self .speed -= qty
125+ print (f"Frenar terminado, velocidad actual: { self .speed } " )
126+ else :
127+ self .speed = 0
128+ print ("Vehículo parado" )
129+
130+
131+ class Truck (Vehicle ):
132+ def speed_up (self , qty ):
133+ if self .speed + qty <= self .max_speed :
134+ self .speed += qty
135+ print (f"Acelerar terminado, velocidad actual: { self .speed } " )
136+ else :
137+ raise ValueError ("La cantidad a acelerar supera la velocidad máxima del camión" )
138+
139+ def speed_down (self , qty ):
140+ if self .speed - qty >= 0 :
141+ self .speed -= qty
142+ print (f"Frenar terminado, velocidad actual: { self .speed } " )
143+ else :
144+ self .speed = 0
145+ print ("Vehículo parado" )
146+
147+
148+ class ElectricCar (Vehicle ):
149+ def speed_up (self , qty ):
150+ if self .speed + qty <= self .max_speed :
151+ self .speed += qty
152+ print (f"Acelerar terminado, velocidad actual: { self .speed } " )
153+ else :
154+ raise ValueError ("La cantidad a acelerar supera la velocidad máxima del vehículo eléctrico" )
155+
156+ def speed_down (self , qty ):
157+ if self .speed - qty >= 0 :
158+ self .speed -= qty
159+ print (f"Frenar terminado, velocidad actual: { self .speed } " )
160+ else :
161+ self .speed = 0
162+ print ("Vehículo parado" )
163+
164+
165+ # Realización de pruebas
166+ def test_speed (vehicle ):
167+ print (f"Inicio de las pruebas con { vehicle .type } " )
168+ try :
169+ vehicle .speed_up (25 )
170+ vehicle .speed_down (5 )
171+ vehicle .speed_up (vehicle .max_speed + 1 )
172+ vehicle .speed_down (vehicle .speed )
173+ except ValueError as e :
174+ print (e )
175+ print ("-" * 60 )
176+
177+ cars = [
178+ Car ("Ferrari F50" , 300 , 125 ),
179+ Truck ("Mercedes-Benz Actros" , 150 , 80 ),
180+ ElectricCar ("Tesla Model S" , 120 , 125 )
181+ ]
182+
183+ for car in cars :
184+ test_speed (car ) # Realizar pruebas con cada tipo de vehículo
185+
186+
187+ ### FIN EJERCICIO EXTRA
188+
189+
190+ ### FIN Principio SOLID de Sustitución de Liskov (Liskov Substitution Principle, LSP)
0 commit comments