@icarusmx/creta 0.8.2 → 0.9.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.
- package/bin/creta.js +50 -13
- package/lessons/lesson6-interface-design.js +306 -0
- package/package.json +1 -1
package/bin/creta.js
CHANGED
|
@@ -6,6 +6,7 @@ import fs from 'fs'
|
|
|
6
6
|
import path from 'path'
|
|
7
7
|
import { fileURLToPath } from 'url'
|
|
8
8
|
import { CretaCodeSession } from '../lib/session.js'
|
|
9
|
+
import { Lesson6InterfaceDesign } from '../lessons/lesson6-interface-design.js'
|
|
9
10
|
|
|
10
11
|
const ENUNCIADOS = [
|
|
11
12
|
{
|
|
@@ -786,7 +787,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
786
787
|
return new Promise((resolve) => {
|
|
787
788
|
renderOptions()
|
|
788
789
|
|
|
789
|
-
const onKeyPress = (key) => {
|
|
790
|
+
const onKeyPress = async (key) => {
|
|
790
791
|
if (key === 'q' || key === '\x03') { // q or Ctrl+C
|
|
791
792
|
process.stdin.setRawMode(false)
|
|
792
793
|
process.stdin.removeListener('data', onKeyPress)
|
|
@@ -810,13 +811,30 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
810
811
|
console.log(`\n💡 Enfoque: ${enunciadoSeleccionado.enfoque}`)
|
|
811
812
|
console.log(`📚 Nivel: ${enunciadoSeleccionado.nivel}`)
|
|
812
813
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
814
|
+
// Check if we have a lesson implementation for this enunciado
|
|
815
|
+
if (enunciadoSeleccionado.id === 6) {
|
|
816
|
+
console.log("\n🚀 ¡Lección disponible! Iniciando sesión de estudio dirigida...")
|
|
817
|
+
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
818
|
+
|
|
819
|
+
try {
|
|
820
|
+
const lesson6 = new Lesson6InterfaceDesign()
|
|
821
|
+
await lesson6.start()
|
|
822
|
+
} catch (error) {
|
|
823
|
+
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
824
|
+
console.log("\n🚀 Próximamente:")
|
|
825
|
+
console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
|
|
826
|
+
console.log("- Ejercicios prácticos que ilustren el concepto")
|
|
827
|
+
console.log("- Proyectos específicos para internalizar la idea")
|
|
828
|
+
}
|
|
829
|
+
} else {
|
|
830
|
+
console.log("\n🚀 Próximamente:")
|
|
831
|
+
console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
|
|
832
|
+
console.log("- Ejercicios prácticos que ilustren el concepto")
|
|
833
|
+
console.log("- Proyectos específicos para internalizar la idea")
|
|
817
834
|
|
|
818
|
-
|
|
819
|
-
|
|
835
|
+
console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
|
|
836
|
+
console.log(" te genera más curiosidad o confusión?")
|
|
837
|
+
}
|
|
820
838
|
|
|
821
839
|
resolve()
|
|
822
840
|
return
|
|
@@ -876,13 +894,32 @@ async function startEnunciadosSelectorFallback() {
|
|
|
876
894
|
console.log(`\n💡 Enfoque: ${enunciadoSeleccionado.enfoque}`)
|
|
877
895
|
console.log(`📚 Nivel: ${enunciadoSeleccionado.nivel}`)
|
|
878
896
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
897
|
+
// Check if we have a lesson implementation for this enunciado
|
|
898
|
+
if (enunciadoSeleccionado.id === 6) {
|
|
899
|
+
console.log("\n🚀 ¡Lección disponible! Iniciando sesión de estudio dirigida...")
|
|
900
|
+
rl.close()
|
|
901
|
+
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
902
|
+
|
|
903
|
+
try {
|
|
904
|
+
const lesson6 = new Lesson6InterfaceDesign()
|
|
905
|
+
await lesson6.start()
|
|
906
|
+
} catch (error) {
|
|
907
|
+
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
908
|
+
console.log("\n🚀 Próximamente:")
|
|
909
|
+
console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
|
|
910
|
+
console.log("- Ejercicios prácticos que ilustren el concepto")
|
|
911
|
+
console.log("- Proyectos específicos para internalizar la idea")
|
|
912
|
+
}
|
|
913
|
+
return
|
|
914
|
+
} else {
|
|
915
|
+
console.log("\n🚀 Próximamente:")
|
|
916
|
+
console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
|
|
917
|
+
console.log("- Ejercicios prácticos que ilustren el concepto")
|
|
918
|
+
console.log("- Proyectos específicos para internalizar la idea")
|
|
883
919
|
|
|
884
|
-
|
|
885
|
-
|
|
920
|
+
console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
|
|
921
|
+
console.log(" te genera más curiosidad o confusión?")
|
|
922
|
+
}
|
|
886
923
|
|
|
887
924
|
} else {
|
|
888
925
|
console.log("❌ Opción no válida. Elige un número entre 1 y 6.")
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
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
|
|
5
|
+
|
|
6
|
+
import { createInterface } from 'readline'
|
|
7
|
+
|
|
8
|
+
export class Lesson6InterfaceDesign {
|
|
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 6: Diseño basado en Interfaces")
|
|
18
|
+
console.log("=" .repeat(50))
|
|
19
|
+
console.log("📚 Concepto: El énfasis al diseñar los objetos debe estar en la definición de sus solicitudes e interfaces.")
|
|
20
|
+
console.log("\n💡 ¿Por qué es importante?")
|
|
21
|
+
console.log("- Las interfaces definen QUÉ hace un objeto, no CÓMO lo hace")
|
|
22
|
+
console.log("- Facilita la colaboración entre objetos")
|
|
23
|
+
console.log("- Permite cambiar implementaciones sin afectar otros objetos")
|
|
24
|
+
console.log("- Hace el código más mantenible y extensible")
|
|
25
|
+
|
|
26
|
+
await this.waitForEnter("\nPresiona Enter para comenzar el ejercicio práctico...")
|
|
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: Sistema de Gestión de Tareas")
|
|
37
|
+
console.log("=" .repeat(50))
|
|
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")
|
|
43
|
+
|
|
44
|
+
await this.waitForEnter("\nPresiona Enter para ver cómo diseñar las interfaces...")
|
|
45
|
+
|
|
46
|
+
await this.step1_DefineRequests()
|
|
47
|
+
await this.step2_DefineInterfaces()
|
|
48
|
+
await this.step3_ImplementObjects()
|
|
49
|
+
await this.step4_TestInteraction()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async step1_DefineRequests() {
|
|
53
|
+
console.clear()
|
|
54
|
+
console.log("📋 PASO 1: Definir las Solicitudes")
|
|
55
|
+
console.log("=" .repeat(40))
|
|
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")
|
|
72
|
+
|
|
73
|
+
await this.waitForEnter("\n✅ ¡Excelente! Ya definimos QUÉ debe hacer cada objeto. Presiona Enter para definir las interfaces...")
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async step2_DefineInterfaces() {
|
|
77
|
+
console.clear()
|
|
78
|
+
console.log("🔌 PASO 2: Definir las Interfaces (Firmas de Operación)")
|
|
79
|
+
console.log("=" .repeat(50))
|
|
80
|
+
console.log("Ahora especifiquemos CÓMO se solicitan estas operaciones:")
|
|
81
|
+
|
|
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")
|
|
88
|
+
console.log("}")
|
|
89
|
+
|
|
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")
|
|
96
|
+
console.log("}")
|
|
97
|
+
|
|
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")
|
|
103
|
+
console.log("}")
|
|
104
|
+
|
|
105
|
+
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.)")
|
|
109
|
+
|
|
110
|
+
await this.waitForEnter("\n✅ Interfaces definidas. Presiona Enter para implementar...")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async step3_ImplementObjects() {
|
|
114
|
+
console.clear()
|
|
115
|
+
console.log("⚙️ PASO 3: Implementar los Objetos")
|
|
116
|
+
console.log("=" .repeat(40))
|
|
117
|
+
console.log("Ahora que tenemos las interfaces claras, implementemos:")
|
|
118
|
+
|
|
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
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getInfo() {
|
|
129
|
+
return {
|
|
130
|
+
id: this.id,
|
|
131
|
+
title: this.title,
|
|
132
|
+
description: this.description,
|
|
133
|
+
completed: this.completed
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
markCompleted() {
|
|
138
|
+
this.completed = true
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isCompleted() {
|
|
142
|
+
return this.completed
|
|
143
|
+
}
|
|
144
|
+
}`)
|
|
145
|
+
|
|
146
|
+
await this.waitForEnter("\nPresiona Enter para ver TaskStorage...")
|
|
147
|
+
|
|
148
|
+
console.log("\n// Implementación de TaskStorage")
|
|
149
|
+
console.log(`class TaskStorage {
|
|
150
|
+
constructor() {
|
|
151
|
+
this.tasks = []
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
save(task) {
|
|
155
|
+
this.tasks.push(task)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
findAll() {
|
|
159
|
+
return [...this.tasks]
|
|
160
|
+
}
|
|
161
|
+
|
|
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
|
|
167
|
+
}
|
|
168
|
+
return false
|
|
169
|
+
}
|
|
170
|
+
}`)
|
|
171
|
+
|
|
172
|
+
await this.waitForEnter("\nPresiona Enter para ver TaskManager...")
|
|
173
|
+
|
|
174
|
+
console.log("\n// Implementación de TaskManager")
|
|
175
|
+
console.log(`class TaskManager {
|
|
176
|
+
constructor(storage) {
|
|
177
|
+
this.storage = storage
|
|
178
|
+
this.nextId = 1
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
createTask(title, description) {
|
|
182
|
+
const task = new Task(this.nextId++, title, description)
|
|
183
|
+
this.storage.save(task)
|
|
184
|
+
return task
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getAllTasks() {
|
|
188
|
+
return this.storage.findAll()
|
|
189
|
+
}
|
|
190
|
+
|
|
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
|
|
197
|
+
}
|
|
198
|
+
return false
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
deleteTask(taskId) {
|
|
202
|
+
return this.storage.remove(taskId)
|
|
203
|
+
}
|
|
204
|
+
}`)
|
|
205
|
+
|
|
206
|
+
await this.waitForEnter("\n✅ Implementación completa. Presiona Enter para probar la interacción...")
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async step4_TestInteraction() {
|
|
210
|
+
console.clear()
|
|
211
|
+
console.log("🧪 PASO 4: Probar la Interacción entre Objetos")
|
|
212
|
+
console.log("=" .repeat(45))
|
|
213
|
+
console.log("Veamos cómo los objetos colaboran usando sus interfaces:")
|
|
214
|
+
|
|
215
|
+
console.log("\n// Crear el sistema")
|
|
216
|
+
console.log("const storage = new TaskStorage()")
|
|
217
|
+
console.log("const manager = new TaskManager(storage)")
|
|
218
|
+
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')")
|
|
222
|
+
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()")
|
|
226
|
+
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("})")
|
|
231
|
+
|
|
232
|
+
console.log("\n🎯 Resultado esperado:")
|
|
233
|
+
console.log("Estudiar OOP: ✅")
|
|
234
|
+
console.log("Practicar código: ❌")
|
|
235
|
+
|
|
236
|
+
await this.waitForEnter("\nPresiona Enter para ver los beneficios de este enfoque...")
|
|
237
|
+
|
|
238
|
+
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")
|
|
246
|
+
|
|
247
|
+
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 */ }")
|
|
253
|
+
console.log("}")
|
|
254
|
+
console.log("")
|
|
255
|
+
console.log("// TaskManager sigue funcionando igual!")
|
|
256
|
+
console.log("const dbStorage = new DatabaseStorage()")
|
|
257
|
+
console.log("const manager = new TaskManager(dbStorage)")
|
|
258
|
+
|
|
259
|
+
await this.waitForEnter("\nPresiona Enter para continuar con la conclusión...")
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async conclusion() {
|
|
263
|
+
console.clear()
|
|
264
|
+
console.log("🎓 CONCLUSIÓN: Diseño basado en Interfaces")
|
|
265
|
+
console.log("=" .repeat(45))
|
|
266
|
+
console.log("🎯 Hemos demostrado que:")
|
|
267
|
+
console.log("\n1. El ÉNFASIS debe estar en definir QUÉ solicitudes maneja cada objeto")
|
|
268
|
+
console.log("2. Las INTERFACES especifican cómo hacer estas solicitudes")
|
|
269
|
+
console.log("3. La IMPLEMENTACIÓN viene después, siguiendo el contrato")
|
|
270
|
+
console.log("4. Los objetos COLABORAN a través de estas interfaces bien definidas")
|
|
271
|
+
|
|
272
|
+
console.log("\n📚 Conexión con lecciones anteriores:")
|
|
273
|
+
console.log("• Lección 2: Los objetos interactúan a través de solicitudes ✓")
|
|
274
|
+
console.log("• Lección 3: Las solicitudes son la única forma de ejecutar operaciones ✓")
|
|
275
|
+
console.log("• Lección 4: Cada operación tiene nombre, insumos y valor de retorno ✓")
|
|
276
|
+
console.log("• Lección 5: La interfaz es el conjunto de todas las firmas ✓")
|
|
277
|
+
console.log("• Lección 6: El énfasis está en definir solicitudes e interfaces ✓")
|
|
278
|
+
|
|
279
|
+
console.log("\n🚀 Próximo paso:")
|
|
280
|
+
console.log("Practica este enfoque en tus propios proyectos:")
|
|
281
|
+
console.log("1. Define primero QUÉ debe hacer cada objeto")
|
|
282
|
+
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")
|
|
285
|
+
|
|
286
|
+
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?")
|
|
289
|
+
|
|
290
|
+
await this.waitForEnter("\n✨ ¡Lección 6 completada! Presiona Enter para salir...")
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async waitForEnter(message) {
|
|
294
|
+
return new Promise((resolve) => {
|
|
295
|
+
this.rl.question(message, () => {
|
|
296
|
+
resolve()
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Para usar la lección independientemente
|
|
303
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
304
|
+
const lesson = new Lesson6InterfaceDesign()
|
|
305
|
+
await lesson.start()
|
|
306
|
+
}
|