@icarusmx/creta 0.10.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env node
2
+
3
+ // LECCIÓN 3: Las solicitudes son la única forma de conseguir que un objeto lleve a cabo una operación
4
+ // Enfoque práctico: Demostrar que NO hay alternativas a las solicitudes para ejecutar operaciones
5
+
6
+ import { createInterface } from 'readline'
7
+
8
+ export class Lesson3OnlyWay {
9
+ constructor() {
10
+ this.rl = createInterface({
11
+ input: process.stdin,
12
+ output: process.stdout
13
+ })
14
+ }
15
+
16
+ async start() {
17
+ console.log("🎯 LECCIÓN 3: Solicitudes como Única Forma")
18
+ console.log("=" .repeat(50))
19
+ console.log("📚 Enunciado: Las solicitudes son la única forma de conseguir que un objeto lleve a cabo una operación.")
20
+ console.log("\n💡 ¿Por qué ÚNICA forma?")
21
+ console.log("- No puedes forzar a un objeto a ejecutar algo")
22
+ console.log("- No puedes manipular directamente su estado interno")
23
+ console.log("- Toda operación DEBE pasar por una solicitud")
24
+ console.log("- El objeto decide si acepta o rechaza la solicitud")
25
+
26
+ await this.waitForEnter("\nPresiona Enter para ver qué significa esto en la práctica...")
27
+
28
+ await this.practicalExercise()
29
+ await this.conclusion()
30
+
31
+ this.rl.close()
32
+ }
33
+
34
+ async practicalExercise() {
35
+ console.clear()
36
+ console.log("🛠️ EJERCICIO PRÁCTICO: Caja Fuerte Digital")
37
+ console.log("=" .repeat(50))
38
+ console.log("🎯 Vamos a demostrar por qué las solicitudes son la ÚNICA forma")
39
+ console.log("\nSistema: Una caja fuerte que protege documentos importantes")
40
+ console.log("Objeto: DigitalSafe")
41
+
42
+ await this.waitForEnter("\nPresiona Enter para ver todas las formas que NO funcionan...")
43
+
44
+ await this.step1_ShowWhatDoesntWork()
45
+ await this.step2_ShowOnlyWay()
46
+ await this.step3_ObjectDecision()
47
+ await this.step4_CompleteDemo()
48
+ }
49
+
50
+ async step1_ShowWhatDoesntWork() {
51
+ console.clear()
52
+ console.log("❌ FORMAS QUE NO FUNCIONAN")
53
+ console.log("=" .repeat(35))
54
+ console.log("Veamos todas las cosas que NO puedes hacer:")
55
+
56
+ console.log("\n🚫 1. ACCESO DIRECTO A DATOS (Imposible)")
57
+ console.log(`// ❌ Esto NO funciona en objetos bien diseñados
58
+ class DigitalSafe {
59
+ constructor() {
60
+ this.#documents = [] // PRIVADO
61
+ this.#isLocked = true // PRIVADO
62
+ this.#password = "1234" // PRIVADO
63
+ }
64
+ }
65
+
66
+ const safe = new DigitalSafe()
67
+ // ❌ ERROR: No puedes acceder a datos privados
68
+ console.log(safe.#documents) // SyntaxError!
69
+ safe.#isLocked = false // SyntaxError!`)
70
+
71
+ await this.waitForEnter("\nPresiona Enter para ver más formas que no funcionan...")
72
+
73
+ console.log("\n🚫 2. MANIPULACIÓN EXTERNA (Imposible)")
74
+ console.log(`// ❌ No puedes forzar cambios desde afuera
75
+ safe.documents = ["documento_falso.pdf"] // undefined (no existe)
76
+ safe.isLocked = false // undefined (no existe)
77
+ safe.forceOpen() // TypeError: no es función
78
+
79
+ // ❌ No puedes inyectar funciones
80
+ safe.hackOpen = function() { this.#isLocked = false } // No funciona`)
81
+
82
+ await this.waitForEnter("\nPresiona Enter para ver la última forma que no funciona...")
83
+
84
+ console.log("\n🚫 3. HERENCIA FORZADA (Violación de diseño)")
85
+ console.log(`// ❌ Incluso extendiendo la clase, no puedes saltarte las reglas
86
+ class HackedSafe extends DigitalSafe {
87
+ forceOpen() {
88
+ // ❌ ERROR: No puedes acceder a campos privados del padre
89
+ this.#isLocked = false // SyntaxError!
90
+ return this.#documents // SyntaxError!
91
+ }
92
+ }`)
93
+
94
+ console.log("\n💡 Observación clave:")
95
+ console.log("El objeto está completamente protegido. No hay forma de")
96
+ console.log("manipularlo externamente sin su consentimiento.")
97
+
98
+ await this.waitForEnter("\nPresiona Enter para ver la ÚNICA forma que funciona...")
99
+ }
100
+
101
+ async step2_ShowOnlyWay() {
102
+ console.clear()
103
+ console.log("✅ LA ÚNICA FORMA: SOLICITUDES")
104
+ console.log("=" .repeat(40))
105
+ console.log("Solo hay UNA manera de que el objeto ejecute operaciones:")
106
+
107
+ console.log("\n🔑 IMPLEMENTACIÓN COMPLETA:")
108
+ console.log(`class DigitalSafe {
109
+ constructor() {
110
+ this.#documents = []
111
+ this.#isLocked = true
112
+ this.#password = "1234"
113
+ this.#attempts = 0
114
+ this.#maxAttempts = 3
115
+ }
116
+
117
+ // ✅ SOLICITUD: Intentar desbloquear
118
+ unlock(password) {
119
+ if (this.#attempts >= this.#maxAttempts) {
120
+ return { success: false, message: "Bloqueado por seguridad" }
121
+ }
122
+
123
+ if (password === this.#password) {
124
+ this.#isLocked = false
125
+ this.#attempts = 0
126
+ return { success: true, message: "Caja fuerte desbloqueada" }
127
+ } else {
128
+ this.#attempts++
129
+ return {
130
+ success: false,
131
+ message: \`Contraseña incorrecta. Intentos restantes: \${this.#maxAttempts - this.#attempts}\`
132
+ }
133
+ }
134
+ }
135
+
136
+ // ✅ SOLICITUD: Agregar documento
137
+ addDocument(document) {
138
+ if (this.#isLocked) {
139
+ return { success: false, message: "Caja fuerte bloqueada" }
140
+ }
141
+
142
+ this.#documents.push(document)
143
+ return { success: true, message: "Documento agregado" }
144
+ }
145
+
146
+ // ✅ SOLICITUD: Obtener documentos
147
+ getDocuments() {
148
+ if (this.#isLocked) {
149
+ return { success: false, message: "Caja fuerte bloqueada", documents: [] }
150
+ }
151
+
152
+ return { success: true, documents: [...this.#documents] }
153
+ }
154
+
155
+ // ✅ SOLICITUD: Bloquear
156
+ lock() {
157
+ this.#isLocked = true
158
+ return { success: true, message: "Caja fuerte bloqueada" }
159
+ }
160
+
161
+ // ✅ SOLICITUD: Consultar estado
162
+ getStatus() {
163
+ return {
164
+ isLocked: this.#isLocked,
165
+ documentsCount: this.#documents.length,
166
+ attemptsRemaining: this.#maxAttempts - this.#attempts
167
+ }
168
+ }
169
+ }`)
170
+
171
+ await this.waitForEnter("\nPresiona Enter para ver por qué esto es la ÚNICA forma...")
172
+ }
173
+
174
+ async step3_ObjectDecision() {
175
+ console.clear()
176
+ console.log("🧠 EL OBJETO DECIDE")
177
+ console.log("=" .repeat(25))
178
+ console.log("Observa que cada solicitud permite al objeto DECIDIR:")
179
+
180
+ console.log("\n🔍 EJEMPLO 1: Solicitud aceptada")
181
+ console.log(`const safe = new DigitalSafe()
182
+
183
+ // SOLICITUD: unlock con contraseña correcta
184
+ const result = safe.unlock("1234")
185
+ console.log(result) // { success: true, message: "Caja fuerte desbloqueada" }
186
+
187
+ // El OBJETO decidió aceptar la solicitud porque:
188
+ // - La contraseña era correcta
189
+ // - No se habían agotado los intentos`)
190
+
191
+ await this.waitForEnter("\nPresiona Enter para ver una solicitud rechazada...")
192
+
193
+ console.log("\n🚫 EJEMPLO 2: Solicitud rechazada")
194
+ console.log(`// SOLICITUD: addDocument sin desbloquear
195
+ const result2 = safe.addDocument("documento_secreto.pdf")
196
+ console.log(result2) // { success: false, message: "Caja fuerte bloqueada" }
197
+
198
+ // El OBJETO decidió rechazar la solicitud porque:
199
+ // - La caja fuerte está bloqueada
200
+ // - No cumple las condiciones internas`)
201
+
202
+ await this.waitForEnter("\nPresiona Enter para ver el control total del objeto...")
203
+
204
+ console.log("\n🛡️ EJEMPLO 3: Protección automática")
205
+ console.log(`// Intentos fallidos múltiples
206
+ safe.unlock("wrong1") // { success: false, message: "Contraseña incorrecta..." }
207
+ safe.unlock("wrong2") // { success: false, message: "Contraseña incorrecta..." }
208
+ safe.unlock("wrong3") // { success: false, message: "Contraseña incorrecta..." }
209
+ safe.unlock("1234") // { success: false, message: "Bloqueado por seguridad" }
210
+
211
+ // El OBJETO decidió bloquearse completamente porque:
212
+ // - Se agotaron los intentos permitidos
213
+ // - Sus reglas internas de seguridad se activaron`)
214
+
215
+ console.log("\n💡 Punto clave:")
216
+ console.log("Incluso con la contraseña correcta, el objeto puede rechazar")
217
+ console.log("la solicitud si sus condiciones internas no se cumplen.")
218
+
219
+ await this.waitForEnter("\nPresiona Enter para la demostración completa...")
220
+ }
221
+
222
+ async step4_CompleteDemo() {
223
+ console.clear()
224
+ console.log("🎭 DEMOSTRACIÓN: Flujo Completo")
225
+ console.log("=" .repeat(40))
226
+ console.log("Veamos un flujo completo de solicitudes:")
227
+
228
+ console.log("\n// Crear caja fuerte")
229
+ console.log("const safe = new DigitalSafe()")
230
+ console.log("")
231
+ console.log("// 🔍 SOLICITUD: Consultar estado inicial")
232
+ console.log("console.log(safe.getStatus())")
233
+ console.log("// { isLocked: true, documentsCount: 0, attemptsRemaining: 3 }")
234
+ console.log("")
235
+ console.log("// 🚫 SOLICITUD: Intentar agregar documento (será rechazada)")
236
+ console.log("safe.addDocument('mi_testamento.pdf')")
237
+ console.log("// { success: false, message: 'Caja fuerte bloqueada' }")
238
+ console.log("")
239
+ console.log("// 🔑 SOLICITUD: Desbloquear")
240
+ console.log("safe.unlock('1234')")
241
+ console.log("// { success: true, message: 'Caja fuerte desbloqueada' }")
242
+ console.log("")
243
+ console.log("// ✅ SOLICITUD: Ahora sí agregar documento")
244
+ console.log("safe.addDocument('mi_testamento.pdf')")
245
+ console.log("// { success: true, message: 'Documento agregado' }")
246
+ console.log("")
247
+ console.log("safe.addDocument('contrato_importante.pdf')")
248
+ console.log("// { success: true, message: 'Documento agregado' }")
249
+
250
+ await this.waitForEnter("\nPresiona Enter para ver la recuperación...")
251
+
252
+ console.log("\n// 📄 SOLICITUD: Recuperar documentos")
253
+ console.log("const docs = safe.getDocuments()")
254
+ console.log("console.log(docs)")
255
+ console.log("// { success: true, documents: ['mi_testamento.pdf', 'contrato_importante.pdf'] }")
256
+ console.log("")
257
+ console.log("// 🔒 SOLICITUD: Bloquear nuevamente")
258
+ console.log("safe.lock()")
259
+ console.log("// { success: true, message: 'Caja fuerte bloqueada' }")
260
+ console.log("")
261
+ console.log("// 🚫 SOLICITUD: Intentar recuperar (será rechazada)")
262
+ console.log("safe.getDocuments()")
263
+ console.log("// { success: false, message: 'Caja fuerte bloqueada', documents: [] }")
264
+
265
+ console.log("\n🎯 Observaciones finales:")
266
+ console.log("• Cada operación requirió una SOLICITUD específica")
267
+ console.log("• El objeto controló completamente qué solicitudes aceptar")
268
+ console.log("• No hubo forma de saltarse este mecanismo")
269
+ console.log("• Las solicitudes son verdaderamente la ÚNICA forma")
270
+
271
+ await this.waitForEnter("\nPresiona Enter para la conclusión...")
272
+ }
273
+
274
+ async conclusion() {
275
+ console.clear()
276
+ console.log("🎓 CONCLUSIÓN: Solicitudes como Única Forma")
277
+ console.log("=" .repeat(50))
278
+ console.log("🎯 Hemos demostrado que:")
279
+
280
+ console.log("\n1️⃣ NO es posible acceder directamente a datos privados")
281
+ console.log("2️⃣ NO es posible manipular el objeto externamente")
282
+ console.log("3️⃣ NO es posible forzar operaciones sin consentimiento")
283
+ console.log("4️⃣ Las SOLICITUDES son literalmente la única forma")
284
+
285
+ console.log("\n🔑 Características de las solicitudes:")
286
+ console.log("• El objeto DECIDE si acepta o rechaza")
287
+ console.log("• Cada solicitud pasa por validaciones internas")
288
+ console.log("• El objeto mantiene control total de su estado")
289
+ console.log("• Las reglas internas se respetan siempre")
290
+
291
+ console.log("\n🛡️ Beneficios de este diseño:")
292
+ console.log("• Seguridad garantizada")
293
+ console.log("• Integridad de datos preservada")
294
+ console.log("• Comportamiento predecible")
295
+ console.log("• Control total del objeto sobre sí mismo")
296
+
297
+ console.log("\n📚 Conexión con otras lecciones:")
298
+ console.log("• Lección 1: Los objetos que identificamos tienen este comportamiento")
299
+ console.log("• Lección 2: Esas interacciones son siempre solicitudes")
300
+ console.log("• Lección 3: ✅ Y son la ÚNICA forma de ejecutar operaciones")
301
+ console.log("• Lecciones 4-6: Cómo definir formalmente estas solicitudes")
302
+
303
+ console.log("\n💭 Reflexión:")
304
+ console.log("Imagina que tu casa tuviera esta protección: nadie puede entrar")
305
+ console.log("a menos que tú decidas abrir la puerta después de verificar")
306
+ console.log("quién es. Los objetos funcionan exactamente igual.")
307
+
308
+ console.log("\n🏆 Ahora entiendes por qué las solicitudes son la ÚNICA forma:")
309
+ console.log("porque los objetos están diseñados para protegerse y decidir.")
310
+
311
+ await this.waitForEnter("\n✨ ¡Lección 3 completada! Presiona Enter para salir...")
312
+ }
313
+
314
+ async waitForEnter(message) {
315
+ return new Promise((resolve) => {
316
+ this.rl.question(message, () => {
317
+ resolve()
318
+ })
319
+ })
320
+ }
321
+ }
322
+
323
+ // Para usar la lección independientemente
324
+ if (import.meta.url === `file://${process.argv[1]}`) {
325
+ const lesson = new Lesson3OnlyWay()
326
+ await lesson.start()
327
+ }
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env node
2
+
3
+ // LECCIÓN 4: Cada operación declarada por un objeto debe incluir: a) nombre de la operación; b) insumos necesarios para realizar la operación; c) el valor que regresa tras ejecutar la operación. Estos tres elementos constituyen la firma de operación.
4
+ // Enfoque práctico: Diseñar firmas de operación precisas y completas
5
+
6
+ import { createInterface } from 'readline'
7
+
8
+ export class Lesson4OperationSignatures {
9
+ constructor() {
10
+ this.rl = createInterface({
11
+ input: process.stdin,
12
+ output: process.stdout
13
+ })
14
+ }
15
+
16
+ async start() {
17
+ console.log("🎯 LECCIÓN 4: Firmas de Operación")
18
+ console.log("=" .repeat(50))
19
+ console.log("📚 Enunciado: Cada operación declarada por un objeto debe incluir:")
20
+ console.log(" a) nombre de la operación")
21
+ console.log(" b) insumos necesarios para realizar la operación")
22
+ console.log(" c) el valor que regresa tras ejecutar la operación")
23
+ console.log(" Estos tres elementos constituyen la firma de operación.")
24
+
25
+ console.log("\n💡 ¿Por qué son importantes las firmas?")
26
+ console.log("- Define exactamente QUÉ hace la operación (nombre)")
27
+ console.log("- Define exactamente QUÉ necesita (insumos)")
28
+ console.log("- Define exactamente QUÉ devuelve (valor de retorno)")
29
+ console.log("- Permite predecir el comportamiento sin ver la implementación")
30
+
31
+ await this.waitForEnter("\nPresiona Enter para comenzar con ejemplos prácticos...")
32
+
33
+ await this.practicalExercise()
34
+ await this.conclusion()
35
+
36
+ this.rl.close()
37
+ }
38
+
39
+ async practicalExercise() {
40
+ console.clear()
41
+ console.log("🛠️ EJERCICIO PRÁCTICO: Calculadora Financiera")
42
+ console.log("=" .repeat(50))
43
+ console.log("🎯 Vamos a diseñar firmas de operación precisas y completas")
44
+ console.log("\nSistema: Calculadora para operaciones financieras")
45
+ console.log("Objeto: FinancialCalculator")
46
+
47
+ await this.waitForEnter("\nPresiona Enter para ver firmas mal diseñadas...")
48
+
49
+ await this.step1_BadSignatures()
50
+ await this.step2_GoodSignatures()
51
+ await this.step3_SignatureAnalysis()
52
+ await this.step4_CompleteExample()
53
+ }
54
+
55
+ async step1_BadSignatures() {
56
+ console.clear()
57
+ console.log("❌ FIRMAS MAL DISEÑADAS")
58
+ console.log("=" .repeat(30))
59
+ console.log("Veamos firmas incompletas o confusas:")
60
+
61
+ console.log("\n🚫 PROBLEMA 1: Nombre poco claro")
62
+ console.log(`// ❌ ¿Qué hace exactamente 'calc'?
63
+ calc(x, y) → ?
64
+
65
+ // ❌ ¿Es 'do' suma, resta, multiplicación?
66
+ do(amount, rate) → ?`)
67
+
68
+ console.log("\n🚫 PROBLEMA 2: Insumos ambiguos")
69
+ console.log(`// ❌ ¿Qué tipo de datos espera?
70
+ calculateInterest(amount, rate) → number
71
+ // ¿amount es string o number?
72
+ // ¿rate es porcentaje (0.05) o entero (5)?`)
73
+
74
+ console.log("\n🚫 PROBLEMA 3: Valor de retorno incierto")
75
+ console.log(`// ❌ ¿Qué devuelve exactamente?
76
+ calculatePayment() → ?
77
+ // ¿Un número? ¿Un objeto? ¿null si falla?
78
+
79
+ // ❌ ¿Y si hay error?
80
+ divideNumbers(a, b) → number
81
+ // ¿Qué pasa si b es 0?`)
82
+
83
+ await this.waitForEnter("\nPresiona Enter para ver más problemas...")
84
+
85
+ console.log("\n🚫 PROBLEMA 4: Firmas inconsistentes")
86
+ console.log(`// ❌ Métodos similares con firmas diferentes
87
+ addMoney(amount) → boolean
88
+ subtractCash(value, currency) → {success: boolean, balance: number}
89
+ multiply(x, y, precision) → number | null
90
+
91
+ // ❌ Imposible predecir qué esperar de cada método`)
92
+
93
+ console.log("\n💥 Consecuencias:")
94
+ console.log("• Imposible usar el objeto sin ver el código interno")
95
+ console.log("• Errores constantes por malentender los parámetros")
96
+ console.log("• Código frágil y difícil de mantener")
97
+ console.log("• Colaboración entre objetos impredecible")
98
+
99
+ await this.waitForEnter("\nPresiona Enter para ver cómo debe ser...")
100
+ }
101
+
102
+ async step2_GoodSignatures() {
103
+ console.clear()
104
+ console.log("✅ FIRMAS BIEN DISEÑADAS")
105
+ console.log("=" .repeat(35))
106
+ console.log("Ahora veamos firmas completas y precisas:")
107
+
108
+ console.log("\n🎯 ELEMENTO A: Nombre claro y específico")
109
+ console.log(`// ✅ Nombres que describen exactamente la operación
110
+ calculateSimpleInterest(principal, rate, time) → number
111
+ calculateCompoundInterest(principal, rate, time, frequency) → number
112
+ calculateMonthlyPayment(loanAmount, interestRate, termInMonths) → number`)
113
+
114
+ console.log("\n📥 ELEMENTO B: Insumos específicos y tipados")
115
+ console.log(`// ✅ Cada parámetro claramente definido
116
+ calculateSimpleInterest(
117
+ principal: number, // Monto principal en pesos
118
+ rate: number, // Tasa anual como decimal (0.05 = 5%)
119
+ time: number // Tiempo en años
120
+ ) → number
121
+
122
+ calculateMonthlyPayment(
123
+ loanAmount: number, // Monto del préstamo en pesos
124
+ interestRate: number, // Tasa mensual como decimal
125
+ termInMonths: number // Plazo en meses
126
+ ) → number`)
127
+
128
+ console.log("\n📤 ELEMENTO C: Valor de retorno específico")
129
+ console.log(`// ✅ Especifica exactamente qué devuelve y cuándo
130
+ calculateLoanPayment(amount, rate, term) → {
131
+ success: boolean,
132
+ monthlyPayment: number | null,
133
+ totalInterest: number | null,
134
+ error: string | null
135
+ }
136
+
137
+ validateLoanAmount(amount) → {
138
+ isValid: boolean,
139
+ minimumRequired: number,
140
+ maximumAllowed: number
141
+ }`)
142
+
143
+ await this.waitForEnter("\nPresiona Enter para ver la implementación completa...")
144
+ }
145
+
146
+ async step3_SignatureAnalysis() {
147
+ console.clear()
148
+ console.log("🔍 ANÁLISIS DE FIRMAS COMPLETAS")
149
+ console.log("=" .repeat(40))
150
+ console.log("Examinemos cada elemento de las firmas:")
151
+
152
+ console.log("\n💼 EJEMPLO: Calculadora financiera completa")
153
+ console.log(`class FinancialCalculator {
154
+ // 🎯 FIRMA 1: Interés simple
155
+ calculateSimpleInterest(
156
+ principal: number, // a) Monto principal en pesos
157
+ rate: number, // b) Tasa anual (0.05 = 5%)
158
+ time: number // c) Tiempo en años
159
+ ): number { // RETORNA: Interés total calculado
160
+ return principal * rate * time
161
+ }
162
+
163
+ // 🎯 FIRMA 2: Validación con resultado estructurado
164
+ validateLoanApplication(
165
+ income: number, // a) Ingreso mensual
166
+ existingDebt: number, // b) Deuda actual
167
+ requestedAmount: number // c) Monto solicitado
168
+ ): { // RETORNA: Objeto con análisis completo
169
+ approved: boolean,
170
+ maxLoanAmount: number,
171
+ debtToIncomeRatio: number,
172
+ reasons: string[]
173
+ }
174
+
175
+ // 🎯 FIRMA 3: Cálculo que puede fallar
176
+ calculateCompoundInterest(
177
+ principal: number, // a) Monto principal
178
+ rate: number, // b) Tasa anual
179
+ time: number, // c) Años
180
+ frequency: number // d) Frecuencia de composición
181
+ ): number | null { // RETORNA: Resultado o null si hay error
182
+ if (principal <= 0 || rate < 0 || time <= 0 || frequency <= 0) {
183
+ return null
184
+ }
185
+ return principal * Math.pow(1 + rate/frequency, frequency * time)
186
+ }
187
+ }`)
188
+
189
+ await this.waitForEnter("\nPresiona Enter para analizar cada firma...")
190
+
191
+ console.log("\n📋 ANÁLISIS FIRMA POR FIRMA:")
192
+ console.log("\n1️⃣ calculateSimpleInterest:")
193
+ console.log(" • NOMBRE: 'calculateSimpleInterest' - claro y específico")
194
+ console.log(" • INSUMOS: (principal, rate, time) - 3 números con propósito definido")
195
+ console.log(" • RETORNO: number - interés calculado, siempre un número")
196
+
197
+ console.log("\n2️⃣ validateLoanApplication:")
198
+ console.log(" • NOMBRE: 'validateLoanApplication' - describe validación completa")
199
+ console.log(" • INSUMOS: (income, existingDebt, requestedAmount) - datos financieros")
200
+ console.log(" • RETORNO: objeto estructurado - análisis completo con múltiples campos")
201
+
202
+ console.log("\n3️⃣ calculateCompoundInterest:")
203
+ console.log(" • NOMBRE: 'calculateCompoundInterest' - específico del tipo de interés")
204
+ console.log(" • INSUMOS: (principal, rate, time, frequency) - 4 parámetros necesarios")
205
+ console.log(" • RETORNO: number | null - puede fallar si datos inválidos")
206
+
207
+ await this.waitForEnter("\nPresiona Enter para ver cómo usar estas firmas...")
208
+ }
209
+
210
+ async step4_CompleteExample() {
211
+ console.clear()
212
+ console.log("🎭 DEMOSTRACIÓN: Uso con Firmas Claras")
213
+ console.log("=" .repeat(45))
214
+ console.log("Veamos cómo las firmas bien diseñadas facilitan el uso:")
215
+
216
+ console.log("\n// Crear calculadora")
217
+ console.log("const calc = new FinancialCalculator()")
218
+ console.log("")
219
+ console.log("// 💡 Con las firmas claras, sabemos exactamente qué esperar:")
220
+ console.log("")
221
+ console.log("// FIRMA: calculateSimpleInterest(principal, rate, time) → number")
222
+ console.log("const interest = calc.calculateSimpleInterest(10000, 0.05, 2)")
223
+ console.log("console.log(interest) // 1000 (siempre un número)")
224
+
225
+ await this.waitForEnter("\nPresiona Enter para ver validación...")
226
+
227
+ console.log("\n// FIRMA: validateLoanApplication(income, debt, amount) → objeto")
228
+ console.log("const validation = calc.validateLoanApplication(5000, 1000, 50000)")
229
+ console.log("console.log(validation)")
230
+ console.log("// {")
231
+ console.log("// approved: false,")
232
+ console.log("// maxLoanAmount: 20000,")
233
+ console.log("// debtToIncomeRatio: 0.2,")
234
+ console.log("// reasons: ['Monto solicitado excede capacidad de pago']")
235
+ console.log("// }")
236
+ console.log("")
237
+ console.log("// 💡 Podemos usar cada campo porque la firma nos dice qué esperar")
238
+ console.log("if (validation.approved) {")
239
+ console.log(" console.log('Préstamo aprobado!')")
240
+ console.log("} else {")
241
+ console.log(" console.log('Máximo permitido:', validation.maxLoanAmount)")
242
+ console.log(" validation.reasons.forEach(reason => console.log('❌', reason))")
243
+ console.log("}")
244
+
245
+ await this.waitForEnter("\nPresiona Enter para ver manejo de errores...")
246
+
247
+ console.log("\n// FIRMA: calculateCompoundInterest(...) → number | null")
248
+ console.log("const compound1 = calc.calculateCompoundInterest(10000, 0.05, 2, 12)")
249
+ console.log("const compound2 = calc.calculateCompoundInterest(-1000, 0.05, 2, 12)")
250
+ console.log("")
251
+ console.log("// 💡 La firma nos dice que puede retornar null, así que verificamos")
252
+ console.log("if (compound1 !== null) {")
253
+ console.log(" console.log('Interés compuesto:', compound1) // 11049.41")
254
+ console.log("}")
255
+ console.log("")
256
+ console.log("if (compound2 === null) {")
257
+ console.log(" console.log('Error: datos inválidos') // Se ejecuta")
258
+ console.log("}")
259
+
260
+ console.log("\n🎯 Beneficios observados:")
261
+ console.log("• Sabemos exactamente qué pasar a cada método")
262
+ console.log("• Sabemos exactamente qué esperar de vuelta")
263
+ console.log("• Podemos manejar errores apropiadamente")
264
+ console.log("• El código es predecible y confiable")
265
+
266
+ await this.waitForEnter("\nPresiona Enter para la conclusión...")
267
+ }
268
+
269
+ async conclusion() {
270
+ console.clear()
271
+ console.log("🎓 CONCLUSIÓN: Firmas de Operación")
272
+ console.log("=" .repeat(40))
273
+ console.log("🎯 Hemos demostrado que cada firma debe incluir:")
274
+
275
+ console.log("\n🎯 A) NOMBRE de la operación:")
276
+ console.log(" • Claro y específico")
277
+ console.log(" • Describe exactamente qué hace")
278
+ console.log(" • Ejemplo: 'calculateSimpleInterest' vs 'calc'")
279
+
280
+ console.log("\n📥 B) INSUMOS necesarios:")
281
+ console.log(" • Tipo y propósito de cada parámetro")
282
+ console.log(" • Valores esperados y restricciones")
283
+ console.log(" • Ejemplo: 'rate: number (decimal, 0.05 = 5%)'")
284
+
285
+ console.log("\n📤 C) VALOR de retorno:")
286
+ console.log(" • Tipo exacto que retorna")
287
+ console.log(" • Qué significa el valor")
288
+ console.log(" • Cómo manejar casos de error")
289
+ console.log(" • Ejemplo: 'number | null' vs solo 'number'")
290
+
291
+ console.log("\n🌟 Beneficios de firmas completas:")
292
+ console.log("• Predictibilidad total del comportamiento")
293
+ console.log("• Colaboración segura entre objetos")
294
+ console.log("• Código autodocumentado")
295
+ console.log("• Detección temprana de errores")
296
+
297
+ console.log("\n📚 Conexión con otras lecciones:")
298
+ console.log("• Lección 1-3: Las operaciones que identificamos necesitan firmas")
299
+ console.log("• Lección 4: ✅ Cada operación tiene nombre, insumos y retorno")
300
+ console.log("• Lección 5: El conjunto de firmas forma la interfaz")
301
+ console.log("• Lección 6: El énfasis está en diseñar estas firmas primero")
302
+
303
+ console.log("\n💭 Reflexión:")
304
+ console.log("¿Te imaginas usar una función de la que no sabes qué parámetros")
305
+ console.log("espera ni qué devuelve? ¡Sería imposible! Las firmas son el")
306
+ console.log("contrato que hace posible la colaboración entre objetos.")
307
+
308
+ console.log("\n🏆 Ahora entiendes por qué cada operación necesita una firma")
309
+ console.log("completa: es la base de la comunicación predecible.")
310
+
311
+ await this.waitForEnter("\n✨ ¡Lección 4 completada! Presiona Enter para salir...")
312
+ }
313
+
314
+ async waitForEnter(message) {
315
+ return new Promise((resolve) => {
316
+ this.rl.question(message, () => {
317
+ resolve()
318
+ })
319
+ })
320
+ }
321
+ }
322
+
323
+ // Para usar la lección independientemente
324
+ if (import.meta.url === `file://${process.argv[1]}`) {
325
+ const lesson = new Lesson4OperationSignatures()
326
+ await lesson.start()
327
+ }