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