1+ """
2+ EJERCICIO:
3+ Explora el "Principio SOLID Abierto-Cerrado (Open-Close Principle,
4+ OCP)" y crea un ejemplo simple donde se muestre su funcionamiento de
5+ forma correcta e incorrecta.
6+
7+ DIFICULTAD EXTRA(opcional):
8+ Desarrolla una calculadora que necesita realizar diversas operaciones
9+ matemáticas.
10+ Requisitos:
11+ - Debes diseñar un sistema que permita agregar nuevas operaciones
12+ utilizando el OCP.
13+
14+ Instrucciones:
15+ 1. Implementa las operaciones de suma, resta, multiplicación y
16+ división.
17+ 2. Comprueba que el sistema funciona.
18+ 3. Agrega una quinta operación para calcular potencias.
19+ 4. Comprueba que se cumple el OCP.
20+
21+ by adra-dev
22+ """
23+
24+ """
25+ Open-Closed Principle (OCP):
26+ El principio open closed para el diseño orientado a objetos fue
27+ introducido por Bertran Meyer en 1988 y este declara:
28+
29+ "Las entidades del software(clases, módulos, funciones. etc.)
30+ deberían estar abiertas para extender su funcionalidad pero
31+ cerradas para su modificación."
32+
33+ documentacion:"https://realpython.com/solid-principles-python/"
34+
35+ Para entender a que se refiere el principio open-closed considera el
36+ siguiente ejemplo.
37+ """
38+
39+
40+ from math import pi
41+
42+ class Shape :
43+ def __init__ (self , shape_type , ** kwargs ):
44+ self .shape_type = shape_type
45+ if self .shape_type == "rectangle" :
46+ self .width = kwargs ["width" ]
47+ self .height = kwargs ["height" ]
48+ elif self .shape_type == "circle" :
49+ self .radius = kwargs ["radius" ]
50+
51+ def calculate_area (self ):
52+ if self .shape_type == "rectangle" :
53+ return self .width * self .height
54+ elif self .shape_type == "circle" :
55+ return pi * self .radius ** 2
56+
57+
58+ rectangle = Shape ("rectangle" , width = 10 , height = 5 )
59+ print (rectangle .calculate_area ())
60+
61+ circle = Shape ("circle" , radius = 5 )
62+ print (circle .calculate_area ())
63+
64+ """
65+ La clase shape funciona. Tu puedes crear circulos y rectangulos,
66+ calcular su área, etc. a pesar de eso la clase luce muy mal. algo se
67+ ve mal a primera vista.
68+
69+ Imagina que necesitas agregar una nueva figura cuadrado, ¿cómo sería?
70+ Bueno, la opcion aqui seria agregar más cláusulas elif al método .__
71+ init__() y al método .calculate_area(), para que así puedas agregar
72+ los requerimientos a la figura cuadrado.
73+
74+ Tener que realizar estos cambias para agregar una nueva figura
75+ significa que tu clase está abierta a modificación lo cual viola el
76+ principio open-closed. aquí hay una posible corrección del código
77+
78+
79+ """
80+
81+ from abc import ABC , abstractmethod
82+
83+ class Shape (ABC ):
84+ def __init__ (self , shape_type ):
85+ self .shape_type = shape_type
86+
87+ @abstractmethod
88+ def calculate_area (self ):
89+ pass
90+
91+
92+ class Circle (Shape ):
93+ def __init__ (self , radius ):
94+ super ().__init__ ("Circle" )
95+ self .radius = radius
96+
97+ def calculate_area (self ):
98+ return pi * self .radius ** 2
99+
100+
101+ class Rectangle (Shape ):
102+ def __init__ (self , width , height ):
103+ super ().__init__ ("rectangle" )
104+ self .width = width
105+ self .height = height
106+
107+ def calculate_area (self ):
108+ return self .width * self .height
109+
110+
111+ class Square (Shape ):
112+ def __init__ (self , side ):
113+ super ().__init__ ("square" )
114+ self .side = side
115+
116+ def calculate_area (self ):
117+ return self .side ** 2
118+
119+
120+ """
121+ En este código, se le ha realizado un refactor a la clase Shape,
122+ convirtiéndola en una clase de base abstracta(ABC). Esta clase provee
123+ de una interfaz api para cualquier tipo de forma que se quiera
124+ definir. Esta interfaz consiste del atributo .shape_type y del método
125+ .calculate_area() que se pueden sobrescribir en todas las subclases.
126+
127+ Este refactor cierra la clase a modificaciones. Ahora puedes agregar
128+ nuevas figuras al diseño de tu clase sin la necesidad de modificar la
129+ clase shape en. En cualquier caso, tendrías que modificar la interfaz
130+ necesario, lo cual también convierte la clase a un tipo polimórfica.
131+
132+ """
133+
134+ """
135+ Extra
136+ """
137+
138+ class Operation (ABC ):
139+ @abstractmethod
140+ def execute (self , a , b ):
141+ pass
142+
143+
144+ class Addition (Operation ):
145+ def execute (self , a ,b ):
146+ return a + b
147+
148+
149+ class Substract (Operation ):
150+ def execute (self , a , b ):
151+ return a - b
152+
153+
154+ class Multiply (Operation ):
155+ def execute (self , a , b ):
156+ return a * b
157+
158+
159+ class Division (Operation ):
160+ def execute (self , a , b ):
161+ return a / b
162+
163+
164+ class Power (Operation ):
165+ def execute (self , a , b ):
166+ return a ** b
167+
168+
169+ class Calculator :
170+ def __init__ (self ) -> None :
171+ self .operations = {}
172+
173+ def add_operation (self , name , operation ):
174+ self .operations [name ] = operation
175+
176+ def calculate (self , name , a , b ):
177+ if name not in self .operations :
178+ raise ValueError (f"La operacion { name } no esta soportada." )
179+ return self .operations [name ].execute (a ,b )
180+
181+
182+ calculator = Calculator ()
183+ calculator .add_operation ("addition" , Addition ())
184+ calculator .add_operation ("substract" , Substract ())
185+ calculator .add_operation ("multiply" , Multiply ())
186+ calculator .add_operation ("division" , Division ())
187+ calculator .add_operation ("power" , Power ())
188+
189+ print (calculator .calculate ("addition" , 10 ,2 ))
190+ print (calculator .calculate ("substract" , 10 ,5 ))
191+ print (calculator .calculate ("multiply" , 10 ,5 ))
192+ print (calculator .calculate ("division" , 10 ,5 ))
193+ print (calculator .calculate ("power" , 10 ,5 ))
0 commit comments