1
+ #27 { Retos para Programadores } Principio SOLID Abierto-Cerrado (Open-Close Principle, OCP)
2
+
3
+ # Bibliography reference:
4
+ # https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle
5
+ # I use GPT as a reference and sometimes to correct or generate proper comments.
6
+
7
+ """
8
+ * EJERCICIO:
9
+ * Explora el "Principio SOLID Abierto-Cerrado (Open-Close Principle, OCP)"
10
+ * y crea un ejemplo simple donde se muestre su funcionamiento
11
+ * de forma correcta e incorrecta.
12
+ *
13
+ * DIFICULTAD EXTRA (opcional):
14
+ * Desarrolla una calculadora que necesita realizar diversas operaciones matemáticas.
15
+ * Requisitos:
16
+ * - Debes diseñar un sistema que permita agregar nuevas operaciones utilizando el OCP.
17
+ * Instrucciones:
18
+ * 1. Implementa las operaciones de suma, resta, multiplicación y división.
19
+ * 2. Comprueba que el sistema funciona.
20
+ * 3. Agrega una quinta operación para calcular potencias.
21
+ * 4. Comprueba que se cumple el OCP.
22
+
23
+ """
24
+
25
+
26
+ """ In object-oriented programming, the open–closed principle (OCP) states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification";[1] that is, such an entity can allow its behaviour to be extended without modifying its source code.
27
+
28
+ The name open–closed principle has been used in two ways. Both ways use generalizations (for instance, inheritance or delegate functions) to resolve the apparent dilemma, but the goals, techniques, and results are different. """
29
+
30
+ # Not using Open-Close Principle (OCP)
31
+
32
+ class SimpleCalculator :
33
+ def calculate (self , operation , a , b ):
34
+ if operation == 'add' :
35
+ return a + b
36
+ elif operation == 'subtract' :
37
+ return a - b
38
+ elif operation == 'multiply' :
39
+ return a * b
40
+ elif operation == 'divide' :
41
+ if b == 0 :
42
+ raise ValueError ("Cannot divide by zero" )
43
+ return a / b
44
+ else :
45
+ raise ValueError ("Operation not supported" )
46
+
47
+ # Example usage
48
+ calculator1 = SimpleCalculator ()
49
+ print (calculator1 .calculate ('add' , 856 , 30 )) # 886
50
+ print (calculator1 .calculate ('divide' , 220 , 4423 )) # 0.04973999547818223
51
+
52
+ # Using Open-Close Principle (OCP)
53
+
54
+ import logging
55
+
56
+ # Set up logging
57
+ logging .basicConfig (level = logging .INFO )
58
+ log = logging .getLogger (__name__ )
59
+
60
+ log .info ('Retos para Programadores #27' ) # Retos para Programadores #27
61
+
62
+ # Base class for operations
63
+ class Operation :
64
+ def execute (self , a : float , b : float ) -> float :
65
+ raise NotImplementedError ("Method not implemented" )
66
+
67
+ # Concrete operation classes
68
+ class Addition (Operation ):
69
+ def execute (self , a : float , b : float ) -> float :
70
+ return a + b
71
+
72
+ class Subtraction (Operation ):
73
+ def execute (self , a : float , b : float ) -> float :
74
+ return a - b
75
+
76
+ class Multiplication (Operation ):
77
+ def execute (self , a : float , b : float ) -> float :
78
+ return a * b
79
+
80
+ class Division (Operation ):
81
+ def execute (self , a : float , b : float ) -> float :
82
+ if b == 0 :
83
+ raise ValueError ("Cannot divide by zero" )
84
+ return a / b
85
+
86
+ class Power (Operation ):
87
+ def execute (self , a : float , b : float ) -> float :
88
+ return a ** b
89
+
90
+ # Calculator class that uses the operations
91
+ class Calculator :
92
+ def __init__ (self ):
93
+ self .operations = {}
94
+
95
+ def add_operation (self , name : str , operation : Operation ):
96
+ self .operations [name ] = operation
97
+ log .info (f"Added operation: [ { name } ]" )
98
+
99
+ def calculate (self , operation_name : str , a : float , b : float ) -> float :
100
+ operation = self .operations .get (operation_name )
101
+ if not operation :
102
+ raise ValueError (f"Operation '{ operation_name } ' not supported" )
103
+ return operation .execute (a , b )
104
+
105
+ def setup_calculator () -> Calculator :
106
+ calculator = Calculator ()
107
+ calculator .add_operation ('add' , Addition ())
108
+ calculator .add_operation ('subtract' , Subtraction ())
109
+ calculator .add_operation ('multiply' , Multiplication ())
110
+ calculator .add_operation ('divide' , Division ())
111
+ calculator .add_operation ('power' , Power ())
112
+ return calculator
113
+
114
+ def add_custom_operation (calculator : Calculator ):
115
+ name = input ("Enter operation name: " )
116
+ action = input ("Enter operation action (use 'a' and 'b' for inputs, e.g., a + b): " )
117
+ sign = input ("Enter operation sign: " )
118
+
119
+ if not name or not action or not sign :
120
+ log .warning ("You have to fill all the fields: name, operation, and sign to add a new operation!" )
121
+ return
122
+
123
+ # Create a new operation class for the custom operation
124
+ class CustomOperation (Operation ):
125
+ def __init__ (self , action : str ):
126
+ self .action = action
127
+
128
+ def execute (self , a : float , b : float ) -> float :
129
+ try :
130
+ # Replace 'a' and 'b' in the action string with the actual values
131
+ expression = self .action .replace ('a' , str (a )).replace ('b' , str (b ))
132
+ return eval (expression )
133
+ except Exception as e :
134
+ log .error (f"Error in operation: { e } " )
135
+ return None
136
+
137
+ # Add the new operation to the calculator
138
+ calculator .add_operation (name , CustomOperation (action ))
139
+ log .info (f"Custom operation '{ name } ' added successfully!" )
140
+
141
+ def get_float_input (prompt : str ) -> float :
142
+ while True :
143
+ try :
144
+ value = float (input (prompt ))
145
+ return value
146
+ except ValueError :
147
+ log .error ("Invalid input. Please enter a valid number." )
148
+
149
+ if __name__ == "__main__" :
150
+ calculator = setup_calculator ()
151
+
152
+ # Example calculations
153
+ try :
154
+ result_add = calculator .calculate ('add' , 856 , 30 )
155
+ log .info (f"Result of addition: { result_add } " ) # 886
156
+
157
+ result_divide = calculator .calculate ('divide' , 220 , 4423 )
158
+ log .info (f"Result of division: { result_divide } " ) # 0.04973999547818223
159
+ except ValueError as e :
160
+ log .error (e )
161
+
162
+ # Adding a custom operation
163
+ add_custom_operation (calculator )
164
+
165
+ # Example of using the custom operation
166
+ custom_operation_name = input ("Enter the name of the custom operation to use: " )
167
+ a = get_float_input ("Enter first number: " )
168
+ b = get_float_input ("Enter second number: " )
169
+
170
+ try :
171
+ result_custom = calculator .calculate (custom_operation_name , a , b )
172
+ log .info (f"Result of custom operation '{ custom_operation_name } ': { result_custom } " )
173
+ except ValueError as e :
174
+ log .error (e )
175
+
176
+ # Output:
177
+ """
178
+ INFO:__main__:Retos para Programadores #27
179
+ INFO:__main__:Added operation: [ add ]
180
+ INFO:__main__:Added operation: [ subtract ]
181
+ INFO:__main__:Added operation: [ multiply ]
182
+ INFO:__main__:Added operation: [ divide ]
183
+ INFO:__main__:Added operation: [ power ]
184
+ INFO:__main__:Result of addition: 886
185
+ INFO:__main__:Result of division: 0.04973999547818223
186
+ Enter operation name: add4
187
+ Enter operation action (use 'a' and 'b' for inputs, e.g., a + b): a + 4 + b
188
+ Enter operation sign: +4+
189
+ INFO:__main__:Added operation: [ add4 ]
190
+ INFO:__main__:Custom operation 'add4' added successfully!
191
+ Enter the name of the custom operation to use: add4
192
+ Enter first number: 12
193
+ Enter second number: 4
194
+ INFO:__main__:Result of custom operation 'add4': 20.0
195
+
196
+ """
0 commit comments