@icarusmx/creta 0.9.0 → 0.9.1

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // LECCIÓN 6: El énfasis al diseñar los objetos debe estar en la definición de sus solicitudes e interfaces
4
- // Enfoque práctico: Sistema de gestión de tareas con diseño interface-first
4
+ // Enfoque práctico: Sistema bancario con diseño interface-first y encapsulación
5
5
 
6
6
  import { createInterface } from 'readline'
7
7
 
@@ -33,13 +33,13 @@ export class Lesson6InterfaceDesign {
33
33
 
34
34
  async practicalExercise() {
35
35
  console.clear()
36
- console.log("🛠️ EJERCICIO PRÁCTICO: Sistema de Gestión de Tareas")
36
+ console.log("🛠️ EJERCICIO PRÁCTICO: Sistema Bancario")
37
37
  console.log("=" .repeat(50))
38
38
  console.log("🎯 Objetivo: Diseñar interfaces ANTES de implementar")
39
- console.log("\nVamos a crear un sistema donde:")
40
- console.log("- TaskManager gestiona una colección de tareas")
41
- console.log("- Task representa una tarea individual")
42
- console.log("- TaskStorage guarda y recupera tareas")
39
+ console.log("\nVamos a crear un sistema bancario donde:")
40
+ console.log("- BankAccount representa una cuenta con balance privado")
41
+ console.log("- Transaction registra movimientos entre cuentas")
42
+ console.log("- Bank gestiona múltiples cuentas y validaciones")
43
43
 
44
44
  await this.waitForEnter("\nPresiona Enter para ver cómo diseñar las interfaces...")
45
45
 
@@ -54,21 +54,22 @@ export class Lesson6InterfaceDesign {
54
54
  console.log("📋 PASO 1: Definir las Solicitudes")
55
55
  console.log("=" .repeat(40))
56
56
  console.log("Antes de escribir código, pensemos: ¿Qué solicitudes debe manejar cada objeto?")
57
- console.log("\n🎯 TaskManager debe poder:")
58
- console.log(" - Crear una nueva tarea")
59
- console.log(" - Listar todas las tareas")
60
- console.log(" - Marcar tarea como completada")
61
- console.log(" - Eliminar una tarea")
62
-
63
- console.log("\n📝 Task debe poder:")
64
- console.log(" - Proporcionar su información")
65
- console.log(" - Cambiar su estado (pendiente/completada)")
66
- console.log(" - Validar si está completada")
67
-
68
- console.log("\n💾 TaskStorage debe poder:")
69
- console.log(" - Guardar una tarea")
70
- console.log(" - Recuperar todas las tareas")
71
- console.log(" - Eliminar una tarea específica")
57
+ console.log("\n🏦 BankAccount debe poder:")
58
+ console.log(" - Consultar balance (sin exponer el dato interno)")
59
+ console.log(" - Recibir dinero (depósito)")
60
+ console.log(" - Enviar dinero (con validación interna)")
61
+ console.log(" - Proporcionar información de la cuenta")
62
+
63
+ console.log("\n📊 Transaction debe poder:")
64
+ console.log(" - Registrar origen, destino y monto")
65
+ console.log(" - Proporcionar detalles del movimiento")
66
+ console.log(" - Validar si la transacción es válida")
67
+
68
+ console.log("\n🏛️ Bank debe poder:")
69
+ console.log(" - Crear nuevas cuentas")
70
+ console.log(" - Procesar transferencias entre cuentas")
71
+ console.log(" - Validar operaciones antes de ejecutarlas")
72
+ console.log(" - Llevar registro de todas las transacciones")
72
73
 
73
74
  await this.waitForEnter("\n✅ ¡Excelente! Ya definimos QUÉ debe hacer cada objeto. Presiona Enter para definir las interfaces...")
74
75
  }
@@ -79,33 +80,36 @@ export class Lesson6InterfaceDesign {
79
80
  console.log("=" .repeat(50))
80
81
  console.log("Ahora especifiquemos CÓMO se solicitan estas operaciones:")
81
82
 
82
- console.log("\n// TaskManager Interface")
83
- console.log("class TaskManager {")
84
- console.log(" createTask(title, description) → Task")
85
- console.log(" getAllTasks() → Array<Task>")
86
- console.log(" completeTask(taskId) → boolean")
87
- console.log(" deleteTask(taskId) → boolean")
83
+ console.log("\n// BankAccount Interface")
84
+ console.log("class BankAccount {")
85
+ console.log(" constructor(accountNumber, ownerName, initialBalance) → BankAccount")
86
+ console.log(" getBalance() → number // ⚠️ ENCAPSULACIÓN: acceso controlado")
87
+ console.log(" getAccountInfo() → {number, owner, balance}")
88
+ console.log(" deposit(amount) → boolean")
89
+ console.log(" send(amount, targetAccount) → boolean // ⚠️ VALIDACIÓN interna")
88
90
  console.log("}")
89
91
 
90
- console.log("\n// Task Interface")
91
- console.log("class Task {")
92
- console.log(" constructor(id, title, description) → Task")
93
- console.log(" getInfo() → {id, title, description, completed}")
94
- console.log(" markCompleted() → void")
95
- console.log(" isCompleted() → boolean")
92
+ console.log("\n// Transaction Interface")
93
+ console.log("class Transaction {")
94
+ console.log(" constructor(from, to, amount, timestamp) → Transaction")
95
+ console.log(" getDetails() → {from, to, amount, timestamp, status}")
96
+ console.log(" isValid() → boolean")
96
97
  console.log("}")
97
98
 
98
- console.log("\n// TaskStorage Interface")
99
- console.log("class TaskStorage {")
100
- console.log(" save(task) → void")
101
- console.log(" findAll() → Array<Task>")
102
- console.log(" remove(taskId) → boolean")
99
+ console.log("\n// Bank Interface")
100
+ console.log("class Bank {")
101
+ console.log(" createAccount(ownerName, initialBalance) → BankAccount")
102
+ console.log(" transfer(fromAccount, toAccount, amount) → Transaction")
103
+ console.log(" getAllTransactions() → Array<Transaction>")
104
+ console.log(" validateOperation(account, amount) → boolean")
103
105
  console.log("}")
104
106
 
105
107
  console.log("\n💡 Observa que cada operación especifica:")
106
- console.log(" a) Nombre de la operación (createTask, save, etc.)")
107
- console.log(" b) Insumos necesarios (title, description, taskId, etc.)")
108
- console.log(" c) Valor que regresa (Task, boolean, Array, etc.)")
108
+ console.log(" a) Nombre de la operación (getBalance, send, transfer, etc.)")
109
+ console.log(" b) Insumos necesarios (amount, targetAccount, etc.)")
110
+ console.log(" c) Valor que regresa (boolean, Transaction, etc.)")
111
+ console.log("\n🔒 Nota especial: getBalance() encapsula el acceso al estado interno")
112
+ console.log("🛡️ Nota especial: send() incluirá validación antes de ejecutar")
109
113
 
110
114
  await this.waitForEnter("\n✅ Interfaces definidas. Presiona Enter para implementar...")
111
115
  }
@@ -116,90 +120,126 @@ export class Lesson6InterfaceDesign {
116
120
  console.log("=" .repeat(40))
117
121
  console.log("Ahora que tenemos las interfaces claras, implementemos:")
118
122
 
119
- console.log("\n// Implementación de Task")
120
- console.log(`class Task {
121
- constructor(id, title, description) {
122
- this.id = id
123
- this.title = title
124
- this.description = description
125
- this.completed = false
123
+ console.log("\n// Implementación de BankAccount")
124
+ console.log(`class BankAccount {
125
+ constructor(accountNumber, ownerName, initialBalance) {
126
+ this.accountNumber = accountNumber
127
+ this.ownerName = ownerName
128
+ this.#balance = initialBalance // 🔒 PRIVADO: no se accede directamente
126
129
  }
127
130
 
128
- getInfo() {
131
+ // 🔑 ENCAPSULACIÓN: método GET para acceder al balance
132
+ getBalance() {
133
+ return this.#balance
134
+ }
135
+
136
+ getAccountInfo() {
129
137
  return {
130
- id: this.id,
131
- title: this.title,
132
- description: this.description,
133
- completed: this.completed
138
+ number: this.accountNumber,
139
+ owner: this.ownerName,
140
+ balance: this.#balance // Controlamos qué se expone
134
141
  }
135
142
  }
136
143
 
137
- markCompleted() {
138
- this.completed = true
144
+ deposit(amount) {
145
+ if (amount > 0) {
146
+ this.#balance += amount
147
+ return true
148
+ }
149
+ return false
139
150
  }
140
151
 
141
- isCompleted() {
142
- return this.completed
152
+ // 🛡️ VALIDACIÓN: método SEND con lógica interna
153
+ send(amount, targetAccount) {
154
+ // Validación interna ANTES de ejecutar
155
+ if (amount > 0 && this.#balance >= amount) {
156
+ this.#balance -= amount
157
+ targetAccount.deposit(amount)
158
+ return true
159
+ }
160
+ return false // Fondos insuficientes o monto inválido
143
161
  }
144
162
  }`)
145
163
 
146
- await this.waitForEnter("\nPresiona Enter para ver TaskStorage...")
164
+ await this.waitForEnter("\nPresiona Enter para ver Transaction...")
147
165
 
148
- console.log("\n// Implementación de TaskStorage")
149
- console.log(`class TaskStorage {
150
- constructor() {
151
- this.tasks = []
166
+ console.log("\n// Implementación de Transaction")
167
+ console.log(`class Transaction {
168
+ constructor(fromAccount, toAccount, amount) {
169
+ this.fromAccount = fromAccount
170
+ this.toAccount = toAccount
171
+ this.amount = amount
172
+ this.timestamp = new Date()
173
+ this.status = 'pending'
152
174
  }
153
175
 
154
- save(task) {
155
- this.tasks.push(task)
176
+ getDetails() {
177
+ return {
178
+ from: this.fromAccount.accountNumber,
179
+ to: this.toAccount.accountNumber,
180
+ amount: this.amount,
181
+ timestamp: this.timestamp,
182
+ status: this.status
183
+ }
156
184
  }
157
185
 
158
- findAll() {
159
- return [...this.tasks]
186
+ isValid() {
187
+ return this.amount > 0 &&
188
+ this.fromAccount.getBalance() >= this.amount
160
189
  }
161
190
 
162
- remove(taskId) {
163
- const index = this.tasks.findIndex(t => t.id === taskId)
164
- if (index !== -1) {
165
- this.tasks.splice(index, 1)
166
- return true
191
+ execute() {
192
+ if (this.isValid()) {
193
+ const success = this.fromAccount.send(this.amount, this.toAccount)
194
+ this.status = success ? 'completed' : 'failed'
195
+ return success
167
196
  }
197
+ this.status = 'failed'
168
198
  return false
169
199
  }
170
200
  }`)
171
201
 
172
- await this.waitForEnter("\nPresiona Enter para ver TaskManager...")
202
+ await this.waitForEnter("\nPresiona Enter para ver Bank...")
173
203
 
174
- console.log("\n// Implementación de TaskManager")
175
- console.log(`class TaskManager {
176
- constructor(storage) {
177
- this.storage = storage
178
- this.nextId = 1
204
+ console.log("\n// Implementación de Bank")
205
+ console.log(`class Bank {
206
+ constructor() {
207
+ this.accounts = []
208
+ this.transactions = []
209
+ this.nextAccountNumber = 1000
179
210
  }
180
211
 
181
- createTask(title, description) {
182
- const task = new Task(this.nextId++, title, description)
183
- this.storage.save(task)
184
- return task
212
+ createAccount(ownerName, initialBalance) {
213
+ const account = new BankAccount(
214
+ this.nextAccountNumber++,
215
+ ownerName,
216
+ initialBalance
217
+ )
218
+ this.accounts.push(account)
219
+ return account
185
220
  }
186
221
 
187
- getAllTasks() {
188
- return this.storage.findAll()
189
- }
222
+ transfer(fromAccount, toAccount, amount) {
223
+ const transaction = new Transaction(fromAccount, toAccount, amount)
190
224
 
191
- completeTask(taskId) {
192
- const tasks = this.storage.findAll()
193
- const task = tasks.find(t => t.id === taskId)
194
- if (task) {
195
- task.markCompleted()
196
- return true
225
+ // El Bank valida ANTES de ejecutar
226
+ if (this.validateOperation(fromAccount, amount)) {
227
+ const success = transaction.execute()
228
+ this.transactions.push(transaction)
229
+ return transaction
197
230
  }
198
- return false
231
+
232
+ transaction.status = 'failed'
233
+ this.transactions.push(transaction)
234
+ return transaction
199
235
  }
200
236
 
201
- deleteTask(taskId) {
202
- return this.storage.remove(taskId)
237
+ validateOperation(account, amount) {
238
+ return amount > 0 && account.getBalance() >= amount
239
+ }
240
+
241
+ getAllTransactions() {
242
+ return [...this.transactions]
203
243
  }
204
244
  }`)
205
245
 
@@ -212,63 +252,87 @@ export class Lesson6InterfaceDesign {
212
252
  console.log("=" .repeat(45))
213
253
  console.log("Veamos cómo los objetos colaboran usando sus interfaces:")
214
254
 
215
- console.log("\n// Crear el sistema")
216
- console.log("const storage = new TaskStorage()")
217
- console.log("const manager = new TaskManager(storage)")
255
+ console.log("\n// Crear el sistema bancario")
256
+ console.log("const bank = new Bank()")
257
+ console.log("const cuenta1 = bank.createAccount('Ana García', 1000)")
258
+ console.log("const cuenta2 = bank.createAccount('Carlos López', 500)")
259
+ console.log("")
260
+ console.log("// 🔑 ENCAPSULACIÓN: Solo podemos acceder al balance via getBalance()")
261
+ console.log("console.log('Balance Ana:', cuenta1.getBalance()) // 1000")
262
+ console.log("console.log('Balance Carlos:', cuenta2.getBalance()) // 500")
218
263
  console.log("")
219
- console.log("// Los objetos interactúan a través de solicitudes")
220
- console.log("const task1 = manager.createTask('Estudiar OOP', 'Completar lección 6')")
221
- console.log("const task2 = manager.createTask('Practicar código', 'Implementar sistema de tareas')")
264
+ console.log("// 🛡️ VALIDACIÓN: send() verifica internamente antes de ejecutar")
265
+ console.log("const exitoso = cuenta1.send(300, cuenta2) // Tiene fondos")
266
+ console.log("console.log('Transferencia exitosa:', exitoso) // true")
222
267
  console.log("")
223
- console.log("// Cada solicitud respeta la interfaz definida")
224
- console.log("manager.completeTask(task1.id)")
225
- console.log("const allTasks = manager.getAllTasks()")
268
+ console.log("const fallido = cuenta1.send(1500, cuenta2) // Fondos insuficientes")
269
+ console.log("console.log('Transferencia fallida:', fallido) // false")
226
270
  console.log("")
227
- console.log("allTasks.forEach(task => {")
228
- console.log(" const info = task.getInfo() // Solicitud a Task")
229
- console.log(" console.log(`${info.title}: ${info.completed ? '✅' : '❌'}`)")
230
- console.log("})")
271
+ console.log("// El estado interno se mantiene íntegro")
272
+ console.log("console.log('Balance final Ana:', cuenta1.getBalance()) // 700")
273
+ console.log("console.log('Balance final Carlos:', cuenta2.getBalance()) // 800")
231
274
 
232
275
  console.log("\n🎯 Resultado esperado:")
233
- console.log("Estudiar OOP: ")
234
- console.log("Practicar código: ")
276
+ console.log("Balance Ana: 1000")
277
+ console.log("Balance Carlos: 500")
278
+ console.log("Transferencia exitosa: true")
279
+ console.log("Transferencia fallida: false")
280
+ console.log("Balance final Ana: 700")
281
+ console.log("Balance final Carlos: 800")
235
282
 
236
283
  await this.waitForEnter("\nPresiona Enter para ver los beneficios de este enfoque...")
237
284
 
238
285
  console.clear()
239
- console.log("🌟 BENEFICIOS DEL DISEÑO INTERFACE-FIRST")
240
- console.log("=" .repeat(45))
241
- console.log("1. 🎯 Claridad: Cada objeto tiene responsabilidades bien definidas")
242
- console.log("2. 🔗 Colaboración: Los objetos saben exactamente cómo solicitar operaciones")
243
- console.log("3. 🔄 Flexibilidad: Podemos cambiar TaskStorage por DatabaseStorage sin afectar TaskManager")
244
- console.log("4. 🧪 Testeable: Podemos crear mocks fácilmente para testing")
245
- console.log("5. 📚 Mantenible: Las interfaces documentan el contrato de cada objeto")
286
+ console.log("🌟 BENEFICIOS DEL DISEÑO INTERFACE-FIRST EN SISTEMAS BANCARIOS")
287
+ console.log("=" .repeat(60))
288
+ console.log("1. 🔒 Encapsulación: El balance está protegido, solo accesible via getBalance()")
289
+ console.log("2. 🛡️ Validación: send() incluye lógica de negocio interna")
290
+ console.log("3. 🎯 Claridad: Cada objeto tiene responsabilidades financieras bien definidas")
291
+ console.log("4. 🔗 Colaboración: Los objetos saben exactamente cómo solicitar operaciones")
292
+ console.log("5. 🧪 Testeable: Podemos simular cuentas y transacciones fácilmente")
293
+ console.log("6. 📚 Mantenible: Las interfaces documentan el contrato bancario")
246
294
 
247
295
  console.log("\n💡 Ejemplo de extensibilidad:")
248
- console.log("// Podemos cambiar el storage sin modificar TaskManager")
249
- console.log("class DatabaseStorage {")
250
- console.log(" save(task) { /* guardar en BD */ }")
251
- console.log(" findAll() { /* consultar BD */ }")
252
- console.log(" remove(taskId) { /* eliminar de BD */ }")
296
+ console.log("// Podemos crear diferentes tipos de cuenta sin cambiar Bank")
297
+ console.log("class SavingsAccount extends BankAccount {")
298
+ console.log(" getBalance() {")
299
+ console.log(" return super.getBalance() + this.calculateInterest()")
300
+ console.log(" }")
301
+ console.log("}")
302
+ console.log("")
303
+ console.log("class CreditAccount extends BankAccount {")
304
+ console.log(" send(amount, target) {")
305
+ console.log(" // Permite sobregiro hasta el límite de crédito")
306
+ console.log(" if (amount > 0 && (this.getBalance() + this.creditLimit) >= amount) {")
307
+ console.log(" // ... lógica específica")
308
+ console.log(" }")
309
+ console.log(" }")
253
310
  console.log("}")
254
311
  console.log("")
255
- console.log("// TaskManager sigue funcionando igual!")
256
- console.log("const dbStorage = new DatabaseStorage()")
257
- console.log("const manager = new TaskManager(dbStorage)")
312
+ console.log("// Bank sigue funcionando igual con cualquier tipo de cuenta!")
313
+ console.log("const savingsAcc = new SavingsAccount(123, 'María', 1000)")
314
+ console.log("const creditAcc = new CreditAccount(456, 'Juan', 500, 2000) // límite crédito")
315
+ console.log("bank.transfer(savingsAcc, creditAcc, 300) // ¡Funciona!")
258
316
 
259
317
  await this.waitForEnter("\nPresiona Enter para continuar con la conclusión...")
260
318
  }
261
319
 
262
320
  async conclusion() {
263
321
  console.clear()
264
- console.log("🎓 CONCLUSIÓN: Diseño basado en Interfaces")
265
- console.log("=" .repeat(45))
322
+ console.log("🎓 CONCLUSIÓN: Diseño basado en Interfaces (Sistema Bancario)")
323
+ console.log("=" .repeat(55))
266
324
  console.log("🎯 Hemos demostrado que:")
267
325
  console.log("\n1. El ÉNFASIS debe estar en definir QUÉ solicitudes maneja cada objeto")
268
326
  console.log("2. Las INTERFACES especifican cómo hacer estas solicitudes")
269
327
  console.log("3. La IMPLEMENTACIÓN viene después, siguiendo el contrato")
270
328
  console.log("4. Los objetos COLABORAN a través de estas interfaces bien definidas")
271
329
 
330
+ console.log("\n🔑 Lecciones específicas del ejemplo bancario:")
331
+ console.log("• ENCAPSULACIÓN: getBalance() protege el acceso al estado interno")
332
+ console.log("• VALIDACIÓN: send() ejecuta lógica de negocio antes de operar")
333
+ console.log("• RESPONSABILIDAD: Cada objeto maneja su propia integridad")
334
+ console.log("• COLABORACIÓN: Bank, BankAccount y Transaction trabajan juntos")
335
+
272
336
  console.log("\n📚 Conexión con lecciones anteriores:")
273
337
  console.log("• Lección 2: Los objetos interactúan a través de solicitudes ✓")
274
338
  console.log("• Lección 3: Las solicitudes son la única forma de ejecutar operaciones ✓")
@@ -280,12 +344,17 @@ export class Lesson6InterfaceDesign {
280
344
  console.log("Practica este enfoque en tus propios proyectos:")
281
345
  console.log("1. Define primero QUÉ debe hacer cada objeto")
282
346
  console.log("2. Especifica las interfaces (firmas de operación)")
283
- console.log("3. Implementa respetando esos contratos")
284
- console.log("4. Haz que los objetos colaboren a través de solicitudes")
347
+ console.log("3. Incluye validaciones internas en tus métodos")
348
+ console.log("4. Encapsula el estado, expón solo lo necesario")
349
+ console.log("5. Haz que los objetos colaboren a través de solicitudes")
285
350
 
286
351
  console.log("\n💭 Reflexión final:")
287
- console.log("¿Cómo cambiaría tu forma de programar si siempre empezaras")
288
- console.log("definiendo las interfaces antes de la implementación?")
352
+ console.log("En el mundo real, ¿cómo te asegurarías de que tu cuenta bancaria")
353
+ console.log("esté protegida y que solo se ejecuten operaciones válidas?")
354
+ console.log("¡Exactamente como lo hicimos aquí: interfaces bien definidas!")
355
+
356
+ console.log("\n🏆 ¡Has completado el ejemplo más importante del diseño orientado a objetos!")
357
+ console.log("Ahora entiendes por qué el ÉNFASIS debe estar en las interfaces.")
289
358
 
290
359
  await this.waitForEnter("\n✨ ¡Lección 6 completada! Presiona Enter para salir...")
291
360
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icarusmx/creta",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Salgamos de este laberinto.",
5
5
  "type": "module",
6
6
  "bin": {