Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
716 changes: 567 additions & 149 deletions prisma/content/csharp/unidad-01-modelar.ts

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions prisma/content/csharp/unidad-02-encapsular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ export const unidad02: UnitDefinition = {
type: "theory",
markdown: `# Encapsular es controlar el acceso

- \`public\`: cualquier código con acceso al objeto puede usar el miembro. En UML: \`+\`.
- \`private\`: sólo la propia clase puede usarlo. En UML: \`-\`.
- \`protected\`: la clase y sus clases derivadas pueden usarlo. En UML: \`#\`.
- \`public\`: cualquier código con acceso al objeto puede usar el miembro.
- \`private\`: sólo la propia clase puede usarlo.

El estado que sostiene una regla debe ser \`private\`. Si \`saldo\` es público, cualquier línea puede volverlo negativo sin pasar por \`Retirar\`. Haz públicas las operaciones que el resto del programa necesita, no todos los datos.

\`protected\` se reconoce ahora, pero se usará con sentido al estudiar herencia. No lo elijas como un \`private\` “menos estricto”.`,
Existe una tercera opción, \`protected\`, que sólo tiene sentido cuando hay herencia de por medio — la verás con propósito en U5. Por ahora, si dudas entre \`private\` y \`protected\`, usa \`private\`.`,
},
{
type: "code_example",
Expand Down
60 changes: 50 additions & 10 deletions prisma/content/csharp/unidad-03-uml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,10 +413,39 @@ Puerta
correctIndex: 2,
explanation: "El diagrama de clase muestra estructura, no el algoritmo interno completo.",
},
{
// Antes este paso era "usa Semaforo desde Main": no evaluaba UML,
// sólo llamar métodos. La unidad promete C#→UML, así que el reto
// debe pedir el diagrama, no una llamada más.
type: "fill_blank",
prompt: "A partir de esta clase, completa su diagrama UML.",
template: `class Semaforo
{
private string color;
public Semaforo(string inicial) { color = inicial; }
public void Cambiar(string nuevo) { color = nuevo; }
public string ColorActual() { return color; }
}

// Diagrama:
// Semaforo
// -----------------
// {{0}}: string
// -----------------
// +Semaforo(inicial: string)
// +Cambiar(nuevo: string): {{1}}
// +ColorActual(): {{2}}`,
blanks: [
{ answer: "-color", hint: "El campo es privado: usa el signo `-` seguido del nombre exacto del campo." },
{ answer: "void", hint: "Cambiar no devuelve nada." },
{ answer: "string", hint: "ColorActual devuelve el color." },
],
explanation: "El diagrama registra visibilidad, atributos y firmas — nunca el cuerpo de los métodos. `Cambiar` no retorna nada (`void`); `ColorActual` retorna `string`.",
},
{
type: "code_challenge",
exercise: {
prompt: "El programa contiene una clase Semaforo. Completa sólo Main para demostrar su contrato: lee color inicial, crea el objeto, llama Cambiar una vez con el segundo color e imprime ColorActual(). No modifiques la clase.",
prompt: "Ahora al revés: usa el contrato de Semaforo (ya escrito, no lo modifiques) desde Main. Lee color inicial, crea el objeto, llama Cambiar una vez con el segundo color e imprime ColorActual().",
starterCode: `using System;

class Semaforo
Expand Down Expand Up @@ -463,7 +492,7 @@ class Program
"No agregues acceso directo al campo.",
],
difficulty: "easy",
xpReward: 24,
xpReward: 20,
structure: {
classes: [
{
Expand Down Expand Up @@ -530,21 +559,32 @@ No crees \`Papeleria\`, \`Sistema\`, \`Usuario\`, \`Pantalla\` y \`BaseDeDatos\`
explanation: "Separar dominio de interfaz desde el modelo evita una clase que haga todo.",
},
{
// Antes de programar, decisión explícita sobre ESTE requerimiento:
// el conjunto exacto de miembros que Articulo va a tener, contra
// tres alternativas plausibles pero equivocadas (dato inventado,
// dato faltante que la regla necesita, responsabilidad de GUI).
// El challenge que sigue implementa justo lo que aquí se decide.
type: "quiz",
question: "¿Qué decisión demuestra mejor abstracción para este requerimiento?",
question: "Según el requerimiento de la papelería, ¿qué conjunto de miembros debe tener Articulo?",
options: [
"Agregar 20 datos “por si acaso”",
"Modelar sólo datos y operaciones usados por venta",
"Poner todo en Main",
"Hacer públicos todos los campos",
"Codigo, Precio, Existencias, Vender(cantidad), ConsultarExistencias()",
"Codigo, Precio, Existencias, NombreProveedor, Vender(cantidad)",
"Codigo, Precio, Vender(cantidad), ConsultarExistencias() (sin Existencias)",
"Codigo, Precio, Existencias, Vender(cantidad), MostrarBotonVender()",
],
feedbackPerOption: [
"",
"NombreProveedor no aparece en el requerimiento ni lo necesita ninguna regla: es un dato inventado.",
"Sin Existencias no hay con qué comparar la cantidad ni qué reducir: la regla central queda sin dónde vivir.",
"Mostrar un botón es responsabilidad de la interfaz, no del artículo del dominio.",
],
correctIndex: 1,
explanation: "El modelo mínimo cubre las reglas observables sin inventar alcance.",
correctIndex: 0,
explanation: "Cada miembro de este conjunto responde a algo que el requerimiento pide o que la regla de venta necesita — ni un dato de más, ni uno de menos.",
},
{
type: "code_challenge",
exercise: {
prompt: "Implementa Articulo con Codigo de sólo lectura externa, Precio y Existencias privados, constructor, Vender(int):bool y ConsultarExistencias():int. El constructor convierte precio/existencias negativos a 0. Lee artículo y dos ventas; por cada venta imprime OK/NO; al final Stock: N.",
prompt: "Implementa el modelo que acabas de decidir: Articulo con Codigo de sólo lectura externa, Precio y Existencias privados, constructor, Vender(int):bool y ConsultarExistencias():int. El constructor convierte precio/existencias negativos a 0. Lee artículo y dos ventas; por cada venta imprime OK/NO; al final Stock: N.",
starterCode: `using System;

class Articulo
Expand Down
2 changes: 1 addition & 1 deletion prisma/content/csharp/unidad-04-relaciones.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ Las relaciones no son etiquetas decorativas. Deben coincidir con campos, paráme
{
type: "code_challenge",
exercise: {
prompt: "Modela una OrdenServicio que se asocia con un Cliente existente y compone un Diagnostico creado internamente desde texto. Cliente tiene Nombre; Diagnostico tiene Detalle; OrdenServicio recibe folio, Cliente y detalle. Mostrar imprime Folio | Cliente | Detalle. Lee un cliente y dos órdenes; ambas comparten el mismo Cliente, cada orden crea su Diagnostico.",
prompt: "El taller registra órdenes de servicio. El Cliente existe por su cuenta y puede tener varias órdenes abiertas al mismo tiempo. Cada orden, en cambio, redacta y posee su propio diagnóstico: nadie más lo usa y no tiene sentido fuera de esa orden. Decide qué relación le corresponde a cada parte y modélala así: Cliente tiene Nombre; Diagnostico tiene Detalle; OrdenServicio recibe folio, el Cliente y el texto del diagnóstico. Mostrar imprime Folio | Cliente | Detalle. Lee un cliente y dos órdenes para ese mismo cliente.",
starterCode: `using System;

class Cliente { /* ... */ }
Expand Down
116 changes: 87 additions & 29 deletions prisma/content/csharp/unidad-05-herencia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,36 @@ export const unidad05: UnitDefinition = {
Prueba de sustitución: si una operación espera \`Empleado\`, ¿aceptar un \`Becario\` conserva el sentido? Si sí, la generalización puede ser adecuada.`,
},
{
// Constructores vacíos a propósito: la relación es-un y "qué se
// hereda" es lo que se evalúa aquí. Encadenar constructores con
// base(...) llega en la lección siguiente, por su cuenta.
type: "code_example",
code: `using System;

class Empleado
{
public string Nombre { get; private set; }
public Empleado(string nombre) { Nombre = nombre; }
public string Nombre;
public void Identificarse() { Console.WriteLine("Empleado: " + Nombre); }
}

class Becario : Empleado
{
public string Escuela { get; private set; }
public Becario(string nombre, string escuela) : base(nombre) { Escuela = escuela; }
public string Escuela;
}

class Program
{
static void Main()
{
Becario b = new Becario("Ana", "CETI");
Becario b = new Becario();
b.Nombre = "Ana";
b.Escuela = "CETI";

b.Identificarse();
Console.WriteLine(b.Escuela);
}
}`,
explanation: "Becario hereda Identificarse y satisface el contrato de Empleado; Escuela es su especialización.",
explanation: "Becario no declara Nombre ni Identificarse: los hereda de Empleado. Escuela es su especialización, lo que lo hace un Becario y no cualquier Empleado.",
runnable: true,
expectedOutput: `Empleado: Ana
CETI`,
Expand All @@ -74,52 +78,51 @@ CETI`,
{
type: "code_challenge",
exercise: {
prompt: "Crea Persona con Nombre y método Presentar(). Crea Alumno : Persona con Registro. Lee nombre y registro; imprime \"Persona: N\" y \"Registro: R\".",
prompt: "Crea Persona con campo público Nombre y método Presentar() que imprima \"Persona: N\". Crea Alumno : Persona con campo público Registro (sin constructores todavía). En Main lee nombre y registro, crea UN Alumno, asígnale ambos campos, llama Presentar() (heredado) e imprime \"Registro: R\".",
starterCode: `using System;
class Persona { /* completa */ }
class Alumno : Persona { /* completa */ }
class Program { static void Main() { /* lee, crea y muestra */ } }`,
solutionCode: `using System;
class Persona
{
public string Nombre { get; private set; }
public Persona(string nombre) { Nombre = nombre; }
public string Nombre;
public void Presentar() { Console.WriteLine("Persona: " + Nombre); }
}
class Alumno : Persona
{
public string Registro { get; private set; }
public Alumno(string nombre, string registro) : base(nombre) { Registro = registro; }
public string Registro;
}
class Program
{
static void Main()
{
Alumno a = new Alumno(Console.ReadLine(), Console.ReadLine());
Alumno a = new Alumno();
a.Nombre = Console.ReadLine();
a.Registro = Console.ReadLine();

a.Presentar();
Console.WriteLine("Registro: " + a.Registro);
}
}`,
hints: [
"Usa : Persona.",
"Invoca base(nombre) en el constructor.",
"El método heredado se llama sobre Alumno.",
"Alumno no declara Nombre ni Presentar: los hereda.",
"Todavía sin constructores: asigna los campos después de new.",
],
difficulty: "medium",
xpReward: 28,
structure: {
classes: [
{
name: "Persona",
properties: [{ name: "Nombre", visibility: "public", type: "string" }],
constructors: [{ paramCount: 1 }],
fields: [{ name: "Nombre", visibility: "public", type: "string" }],
methods: [{ name: "Presentar", visibility: "public" }],
},
{
name: "Alumno",
extends: "Persona",
properties: [{ name: "Registro", visibility: "public", type: "string" }],
constructors: [{ paramCount: 2, callsBase: true }],
fields: [{ name: "Registro", visibility: "public", type: "string" }],
},
],
},
Expand Down Expand Up @@ -341,9 +344,54 @@ class Program
steps: [
{
type: "theory",
markdown: `# Un mensaje, varias respuestas
markdown: `# Un mensaje, ¿varias respuestas?

Piensa en este caso antes de ver ninguna palabra nueva:

La base declara un punto de extensión \`virtual\`; la subclase lo redefine con \`override\`. Una variable de tipo base puede referirse a cualquier subtipo y C# elige la implementación según el objeto real. En POO I se practica con un arreglo de tamaño fijo; las colecciones genéricas corresponden a POO II.`,
\`\`\`csharp
class Empleado
{
public decimal Pago() { return 1000m; }
}
class Vendedor : Empleado
{
public decimal Pago() { return 1300m; }
}
...
Empleado e = new Vendedor();
Console.WriteLine(e.Pago());
\`\`\`

\`e\` está **declarada** como \`Empleado\`, pero el objeto que guarda es, en realidad, un \`Vendedor\`. ¿Qué \`Pago()\` corre: el de \`Empleado\` o el de \`Vendedor\`?`,
},
{
type: "quiz",
question: "Con el código de arriba (sin ninguna palabra clave nueva todavía), ¿qué imprime `e.Pago()`?",
options: [
"1300.00, porque el objeto real es un Vendedor",
"1000.00, porque C# usa el tipo con el que se declaró e, no el tipo del objeto",
"Un error de compilación",
"1000.00 y 1300.00, una por línea",
],
feedbackPerOption: [
"Es la respuesta intuitiva, pero no es lo que hace C# sin más: sigue leyendo.",
"",
"El código compila: Vendedor sí es un Empleado.",
"Sólo hay una llamada a Pago(); sólo puede imprimir un valor.",
],
correctIndex: 1,
explanation: "Sin marcar nada especial, C# resuelve Pago() por el TIPO DECLARADO de la variable (Empleado), no por el objeto real. Para que corra la versión de Vendedor hace falta decírselo explícitamente: eso es lo que sigue.",
},
{
type: "theory",
markdown: `# virtual + override: el contrato explícito

Para que \`e.Pago()\` corra la versión de \`Vendedor\` —lo que probablemente esperabas— hay que decirle a C# dos cosas:

- en la base, que el método **puede redefinirse**: \`public virtual decimal Pago()\`
- en la derivada, que **lo redefine**: \`public override decimal Pago()\`

Con las dos palabras presentes, C# elige la implementación según el **objeto real**, sin importar el tipo con el que declaraste la variable. Eso es polimorfismo.`,
},
{
type: "code_example",
Expand All @@ -360,30 +408,40 @@ class Program
{
static void Main()
{
Empleado[] equipo = new Empleado[2];
equipo[0] = new Empleado();
equipo[1] = new Vendedor();
for (int i = 0; i < equipo.Length; i++)
Console.WriteLine(equipo[i].Pago().ToString("0.00"));
Empleado e = new Vendedor();
Console.WriteLine(e.Pago().ToString("0.00"));
}
}`,
explanation: "La segunda referencia tiene tipo declarado Empleado, pero el objeto Vendedor decide la respuesta.",
explanation: "Misma variable declarada Empleado, mismo objeto Vendedor que antes — pero ahora Pago es virtual/override y sí corre la versión redefinida: 1300.00.",
runnable: true,
expectedOutput: `1000.00
1300.00`,
expectedOutput: "1300.00",
},
{
type: "quiz",
question: "¿Qué habilita el despacho polimórfico del ejemplo?",
options: [
"Que Pago sea static.",
"La combinación virtual en la base y override en la derivada.",
"Que el arreglo tenga dos posiciones.",
"Que la variable se llame e.",
"El método Main.",
],
correctIndex: 1,
explanation: "virtual/override forman el contrato de redefinición.",
},
{
// Segunda dimensión, por separado: ahora un ARREGLO de la base
// con varios objetos reales. Todavía un solo par virtual/override.
type: "code_completion",
prompt: "Ordena el ciclo que recorre un arreglo de Empleado (con un Empleado y un Vendedor adentro) e imprime el Pago de cada uno con el método ya definido arriba.",
lines: [
"Empleado[] equipo = new Empleado[2];",
"equipo[0] = new Empleado();",
"equipo[1] = new Vendedor();",
"for (int i = 0; i < equipo.Length; i++)",
" Console.WriteLine(equipo[i].Pago().ToString(\"0.00\"));",
],
explanation: "El arreglo se declara del tipo base; cada posición puede guardar cualquier subtipo, y el ciclo no necesita saber cuál es cuál: cada Pago() se resuelve solo.",
},
{
type: "code_challenge",
exercise: {
Expand Down
Loading
Loading