1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Linq ;
4
+
5
+ /*
6
+ * PRINCIPIO ABIERTO-CERRADO (OCP)
7
+ *
8
+ * El principio establece que las entidades de software (clases, módulos, funciones, etc.)
9
+ * deberían estar:
10
+ * - ABIERTAS para la extensión: Podemos agregar nuevo comportamiento
11
+ * - CERRADAS para la modificación: No debemos modificar el código existente
12
+ *
13
+ * Beneficios:
14
+ * 1. Código más mantenible y escalable
15
+ * 2. Reduce el riesgo de bugs en código existente
16
+ * 3. Facilita la adición de nuevas funcionalidades
17
+ */
18
+
19
+ namespace OCP . Calculator
20
+ {
21
+ // EJEMPLO INCORRECTO (Violando OCP)
22
+ // ❌ Cada vez que queremos agregar una nueva operación, debemos modificar la clase existente
23
+ public class BadCalculator
24
+ {
25
+ public decimal Calculate ( decimal a , decimal b , string operation )
26
+ {
27
+ switch ( operation )
28
+ {
29
+ case "sum" :
30
+ return a + b ;
31
+ case "subtract" :
32
+ return a - b ;
33
+ case "multiply" :
34
+ return a * b ;
35
+ case "divide" :
36
+ return a / b ;
37
+ // Si queremos agregar una nueva operación, debemos modificar esta clase
38
+ // violando el principio OCP
39
+ default :
40
+ throw new ArgumentException ( "Operación no soportada" ) ;
41
+ }
42
+ }
43
+ }
44
+
45
+ // EJEMPLO CORRECTO (Siguiendo OCP)
46
+ // Definimos una interfaz para las operaciones
47
+ public interface IOperation
48
+ {
49
+ decimal Execute ( decimal a , decimal b ) ;
50
+ string Symbol { get ; }
51
+ string Name { get ; }
52
+ }
53
+
54
+ // Implementamos cada operación como una clase separada
55
+ public class Addition : IOperation
56
+ {
57
+ public decimal Execute ( decimal a , decimal b ) => a + b ;
58
+ public string Symbol => "+" ;
59
+ public string Name => "Suma" ;
60
+ }
61
+
62
+ public class Subtraction : IOperation
63
+ {
64
+ public decimal Execute ( decimal a , decimal b ) => a - b ;
65
+ public string Symbol => "-" ;
66
+ public string Name => "Resta" ;
67
+ }
68
+
69
+ public class Multiplication : IOperation
70
+ {
71
+ public decimal Execute ( decimal a , decimal b ) => a * b ;
72
+ public string Symbol => "*" ;
73
+ public string Name => "Multiplicación" ;
74
+ }
75
+
76
+ public class Division : IOperation
77
+ {
78
+ public decimal Execute ( decimal a , decimal b )
79
+ {
80
+ if ( b == 0 )
81
+ throw new DivideByZeroException ( "No se puede dividir por cero" ) ;
82
+ return a / b ;
83
+ }
84
+ public string Symbol => "/" ;
85
+ public string Name => "División" ;
86
+ }
87
+
88
+ // Podemos agregar nuevas operaciones sin modificar el código existente
89
+ public class Power : IOperation
90
+ {
91
+ public decimal Execute ( decimal a , decimal b )
92
+ {
93
+ return ( decimal ) Math . Pow ( ( double ) a , ( double ) b ) ;
94
+ }
95
+ public string Symbol => "^" ;
96
+ public string Name => "Potencia" ;
97
+ }
98
+
99
+ // Clase para resultados de operaciones
100
+ public class OperationResult
101
+ {
102
+ public decimal Value { get ; set ; }
103
+ public string Operation { get ; set ; }
104
+ public decimal FirstOperand { get ; set ; }
105
+ public decimal SecondOperand { get ; set ; }
106
+ public bool IsSuccess { get ; set ; }
107
+ public string ErrorMessage { get ; set ; }
108
+
109
+ public override string ToString ( ) =>
110
+ IsSuccess
111
+ ? $ "{ FirstOperand } { Operation } { SecondOperand } = { Value } "
112
+ : $ "Error: { ErrorMessage } ";
113
+ }
114
+
115
+ // La calculadora que cumple con OCP
116
+ public class Calculator
117
+ {
118
+ private readonly Dictionary < string , IOperation > _operations ;
119
+
120
+ public Calculator ( )
121
+ {
122
+ _operations = new Dictionary < string , IOperation > ( ) ;
123
+ }
124
+
125
+ // Método para registrar nuevas operaciones
126
+ public void RegisterOperation ( IOperation operation )
127
+ {
128
+ _operations [ operation . Symbol ] = operation ;
129
+ }
130
+
131
+ // Método para realizar cálculos con manejo de errores
132
+ public OperationResult Calculate ( decimal a , decimal b , string symbol )
133
+ {
134
+ try
135
+ {
136
+ if ( ! _operations . TryGetValue ( symbol , out var operation ) )
137
+ {
138
+ return new OperationResult
139
+ {
140
+ IsSuccess = false ,
141
+ ErrorMessage = $ "Operación { symbol } no soportada"
142
+ } ;
143
+ }
144
+
145
+ return new OperationResult
146
+ {
147
+ IsSuccess = true ,
148
+ Value = operation . Execute ( a , b ) ,
149
+ Operation = symbol ,
150
+ FirstOperand = a ,
151
+ SecondOperand = b
152
+ } ;
153
+ }
154
+ catch ( Exception ex )
155
+ {
156
+ return new OperationResult
157
+ {
158
+ IsSuccess = false ,
159
+ ErrorMessage = ex . Message ,
160
+ Operation = symbol ,
161
+ FirstOperand = a ,
162
+ SecondOperand = b
163
+ } ;
164
+ }
165
+ }
166
+
167
+ // Método para obtener operaciones disponibles
168
+ public IEnumerable < ( string Symbol , string Name ) > GetAvailableOperations ( )
169
+ {
170
+ return _operations . Values . Select ( op => ( op . Symbol , op . Name ) ) ;
171
+ }
172
+ }
173
+
174
+ // Clase principal para demostración
175
+ public class Program
176
+ {
177
+ public static void Main ( )
178
+ {
179
+ // Creamos una instancia de la calculadora
180
+ var calculator = new Calculator ( ) ;
181
+
182
+ // Registramos las operaciones básicas
183
+ calculator . RegisterOperation ( new Addition ( ) ) ;
184
+ calculator . RegisterOperation ( new Subtraction ( ) ) ;
185
+ calculator . RegisterOperation ( new Multiplication ( ) ) ;
186
+ calculator . RegisterOperation ( new Division ( ) ) ;
187
+
188
+ // Mostramos las operaciones disponibles
189
+ Console . WriteLine ( "Operaciones disponibles:" ) ;
190
+ foreach ( var ( symbol , name ) in calculator . GetAvailableOperations ( ) )
191
+ {
192
+ Console . WriteLine ( $ "- { name } ({ symbol } )") ;
193
+ }
194
+
195
+ // Probamos las operaciones básicas
196
+ var testCases = new [ ]
197
+ {
198
+ ( a : 10m , b : 5m , symbol : "+" ) ,
199
+ ( a : 10m , b : 5m , symbol : "-" ) ,
200
+ ( a : 10m , b : 5m , symbol : "*" ) ,
201
+ ( a : 10m , b : 5m , symbol : "/" )
202
+ } ;
203
+
204
+ Console . WriteLine ( "\n Probando operaciones básicas:" ) ;
205
+ foreach ( var ( a , b , symbol ) in testCases )
206
+ {
207
+ var result = calculator . Calculate ( a , b , symbol ) ;
208
+ Console . WriteLine ( result ) ;
209
+ }
210
+
211
+ // Agregamos una nueva operación (potencia) sin modificar el código existente
212
+ calculator . RegisterOperation ( new Power ( ) ) ;
213
+
214
+ Console . WriteLine ( "\n Probando nueva operación (potencia):" ) ;
215
+ var powerResult = calculator . Calculate ( 2 , 3 , "^" ) ;
216
+ Console . WriteLine ( powerResult ) ;
217
+
218
+ // Probamos el manejo de errores
219
+ Console . WriteLine ( "\n Probando manejo de errores:" ) ;
220
+
221
+ // División por cero
222
+ var divByZeroResult = calculator . Calculate ( 5 , 0 , "/" ) ;
223
+ Console . WriteLine ( divByZeroResult ) ;
224
+
225
+ // Operación no existente
226
+ var invalidOpResult = calculator . Calculate ( 5 , 2 , "%" ) ;
227
+ Console . WriteLine ( invalidOpResult ) ;
228
+ }
229
+ }
230
+ }
231
+
232
+ /*
233
+ * EXPLICACIÓN DE POR QUÉ ESTE DISEÑO CUMPLE CON OCP:
234
+ *
235
+ * 1. ABIERTO PARA EXTENSIÓN:
236
+ * - Podemos agregar nuevas operaciones implementando la interfaz IOperation
237
+ * - No necesitamos modificar ninguna clase existente
238
+ * - Cada operación está encapsulada en su propia clase
239
+ *
240
+ * 2. CERRADO PARA MODIFICACIÓN:
241
+ * - La clase Calculator no necesita ser modificada para agregar nuevas operaciones
242
+ * - El código existente permanece intacto cuando agregamos nuevas funcionalidades
243
+ * - La interfaz IOperation define un contrato claro que no cambia
244
+ *
245
+ * 3. VENTAJAS DE ESTE DISEÑO:
246
+ * - Fácil de mantener y extender
247
+ * - Cada operación está aislada y puede ser probada independientemente
248
+ * - Reduce el acoplamiento entre componentes
249
+ * - Facilita la adición de nuevas operaciones sin riesgo de afectar las existentes
250
+ *
251
+ * 4. CARACTERÍSTICAS ESPECÍFICAS DE LA IMPLEMENTACIÓN EN C#:
252
+ * - Uso de interfaces y genéricos
253
+ * - Manejo de errores con try-catch y tipos específicos de excepciones
254
+ * - Uso de tipos decimales para precisión en cálculos
255
+ * - Clase específica para resultados con estado de éxito/error
256
+ * - Uso de características modernas de C# como tuplas y expression-bodied members
257
+ * - Organización en namespace
258
+ * - Uso de LINQ para consultas
259
+ */
0 commit comments