Paradigmas de programación


2026 semestre 2

Cronograma de actividades

  • (5%) Quiz #1 [Semana 3 Julio 30 | 31]
  • (15%) Evaluación #1 [Semana 5 Agosto 11 | 12]
  • (20%) Taller #1 [Semana 9 Septiembre 8 | 9]
  • (15%) Taller #2 [Semana 12 Septiembre 29 | 30]
  • (20%) Evaluación #2 [Semana 15 Octubre 22 | 23]
  • (25%) Proyecto Final [Semana 17 Noviembre 3 | 4]

Taller 1

En parejas o equipos de tres personas

Se les entrega un juego de Godot en C# que ya funciona, pero está mal organizado.

El taller no es hacer el juego. Es reorganizarlo aplicando correctamente los conceptos de POO, y extenderlo sin romper lo que ya anda.

⬇ Descargar el proyecto base

El juego base

Un vendedor de madera (Pawn) recorre el mapa. Hoy:

  • Se mueve, se anima y tiene contador de madera y monedas
  • El Lancer le compra: descuenta 1 madera, suma 1 moneda
  • El Goblin está en el mapa y se anima, pero no tiene implementada la interacción de robo
  • La tecla Z existe, dice "Z para resumir" y solo imprime un texto

El objetivo

Diagrama de clases objetivo del juego SellerGame

⬇ Descargar el diagrama (Draw.io) — es el mínimo. Lo que haga falta para limpiar el código va por encima.

Parte 1 — Limpiar la base

Hoy Lancer.cs, Goblin.cs y Monk.cs tienen el mismo bloque de código copiado tres veces: el campo _animator, el _Ready() y un _Process() vacío.

  • Extraer la clase base NPC con lo común
  • Lancer, Goblin y Monk deben derivar de NPC
  • Que no quede ni una línea duplicada entre los tres

Parte 2 — Las interfaces

Este es el corazón del taller. Hoy el vendedor pregunta por el tipo concreto:


// ANTES  (Seller.cs)
if (_npc is Lancer lancer)
    lancer.Buy(this);

// DESPUES
if (_npc is IBuyer buyer) buyer.Buy(this);
if (_npc is IThief thief) thief.Steal(this);
          

Crear IBuyer (Price, Buy) e IThief (Steal), y que el Seller deje de conocer clases concretas.

Parte 3 — El Monk

El proyecto ya trae el sprite, la escena monk.tscn y un Monk.cs vacío.

  • Convertirlo en un segundo comprador: Monk : NPC, IBuyer
  • Debe pagar un precio distinto al del Lancer
  • Agregarlo a world.tscn, porque todavía no está en el mapa

Prueba de fuego: si el refactor está bien hecho, el Seller no se toca para que esto funcione.

Parte 4 — El Goblin roba

  • Implementar Goblin : NPC, IThief con su método Steal
  • Al intentar venderle, el vendedor pierde producto
  • El robo se refleja en el HUD

Regla concreta: si el vendedor no tiene madera, el Goblin no puede robar y no se descuenta nada.

Parte 5 — Las transacciones

  • Crear Transaction con NPC y TotalCounts como mínimo, y sus dos especializaciones: Sale y Theft
  • El Seller compone la lista: las crea, las posee y mueren con él
  • Cada venta y cada robo quedan registrados

Una transacción por NPC: venderle dos veces al mismo Lancer no crea dos instancias — incrementa el TotalCounts de la que ya existe.

Parte 5 — El historial

La tecla Z debe imprimir el historial de transacciones: qué NPC, qué tipo de transacción y cuánto acumula cada uno.

Ustedes deciden el formato y qué más guarda cada transacción. Justifíquenlo en la sustentación.

Reglas del refactor

  • El juego debe seguir funcionando igual que antes, más lo nuevo
  • Si rompieron el juego, el refactor falló — por más lindo que quedó el diagrama

Debe seguir andando: moverse, animarse, el contador de madera, el contador de monedas, el indicador de "X para vender" y la venta al Lancer.

Prohibido

La regla debe salir del diseño, no de un condicional:

  • if (npc is Lancer) o cualquier chequeo de tipo concreto
  • Comparar Name, strings o tags para decidir si vende o le roban
  • Banderas tipo esComprador o esLadron
  • Repetir el bloque _animator + _Ready() en cada NPC

Calidad del código

Esto se evalúa aparte:

  • _Process() no lleva lógica de negocio: orquesta y delega a métodos con nombre
  • Cero números mágicos: velocidad, madera inicial, precios y cantidad robada son constantes o [Export]
  • Una clase por archivo, nombres significativos
  • Encapsulamiento real: nadie modifica los contadores desde afuera a la fuerza

El diagrama final

El diagrama que entregan no es el que les di.

  • Debe reflejar el refactor completo, no solo el mínimo
  • Toda clase, atributo, método y relación que exista en el código debe estar en el diagrama
  • Y al revés: nada en el diagrama que no exista en el código

Espejo exacto. Si crearon una clase extra para limpiar algo, va al diagrama.

Entregables

  • El proyecto de Godot con el refactor completo
  • El diagrama de clases final en Draw.io, espejo exacto del código
  • Demostración en clase: vender al Lancer, vender al Monk a otro precio, que el Goblin robe, y Z mostrando el historial
  • Sustentación individual

Criterios de evaluación

  • (20%) Jerarquía NPC y eliminación de la duplicación
  • (25%) Interfaces IBuyer / IThief y eliminación del chequeo de tipo concreto
  • (10%) Monk como segundo comprador con precio propio
  • (10%) Goblin.Steal funcionando
  • (15%) Transacciones (Sale / Theft) e historial con Z
  • (10%) Calidad del código
  • (10%) Diagrama final completo y espejo del código

⚠️ Importante


El taller no será evaluado si:


  • El proyecto no compila o no corre
  • El diagrama final no corresponde con el código entregado
  • El integrante no sustenta la entrega

No hay excepciones.

Taller 2

En parejas o equipos de tres personas

Construir desde cero una aplicación de escritorio en .NET C# con Windows Forms que modele una tienda híbrida: vende productos físicos y productos digitales.

El tema lo eligen ustedes — videojuegos, películas, música, libros, lo que les apasione. El dominio es suyo; los requisitos no.

¿Por qué híbrida?

Porque los dos productos se comportan distinto, y ahí está el taller:

  • El físico tiene stock finito y cuesta enviarlo
  • El digital no se agota y no se envía: se descarga
  • Los dos se cobran, se listan y se venden por la misma caja financiera

Si la venta tiene que preguntar de qué tipo es cada producto para cobrarlo, el diseño falló.

El sistema, en tres CRUDs

  1. Productos — el catálogo: físicos y digitales
  2. Clientes — a quién le vendemos
  3. Ventas — qué le vendimos, cuándo y a qué precio

Constrúyanlos en ese orden. Cada uno se apoya en el anterior, y la venta es la que pone a prueba todo lo demás.

CRUD 1 — Productos

Lo primero es decidir qué datos guarda cada uno:

  • Común: código, nombre, descripción, precio, categoría y peso — en el físico es cuánto pesa, en el digital es cuánto ocupa la descarga
  • Físico: stock disponible y costo de envío
  • Digital: formato y URL de descarga

La regla: lo común sube a la clase base. Lo propio se queda abajo. Si un atributo no aplica a los dos, no va en la base.

CRUD 1 — Los dos formularios

Crear un producto físico no es lo mismo que crear uno digital, y el formulario lo tiene que mostrar.

  • Un formulario para producto físico — con stock y costo de envío
  • Otro para producto digital — con su formato y su URL de descarga
  • Un único catálogo que los lista a los dos en la misma grilla

Más el resto de operaciones CRUD

La pregunta incómoda


¿Qué pasa si eliminan un producto que ya fue vendido?


Guarden la respuesta. La necesitan en el CRUD 3.

CRUD 2 — Clientes

El más simple de los tres. Aprovéchenlo para dejar el patrón limpio.

  • Datos mínimos: documento, nombre, correo, teléfono
  • El documento no se repite: valídenlo al crear y al actualizar

Otra decisión suya: ¿se puede eliminar un cliente que ya compró? Si dicen que no, expliquen por qué. Si dicen que sí, expliquen qué pasa con sus ventas.

CRUD 3 — Ventas

Acá se junta todo. Una venta es:

  • Un número y una fecha
  • Un cliente
  • Un conjunto de detalles — lo que se llevó y cuánto
  • Un total que la venta calcula, no la pantalla

El formulario: elegir cliente, ir agregando productos con cantidad, ver el total en vivo y guardar.

El patrón SaleDetail

La venta no guarda productos. Guarda SaleDetail, y cada detalle copia lo que se vendió:

  • Descripción del producto tal como estaba ese día
  • Precio unitario del momento de la venta
  • Cantidad y subtotal

¿Y por qué tanto lío?

Porque si la venta apunta directo al producto, pasa esto:

  • Mañana suben el precio — y la venta de ayer cambia sola
  • Pasado eliminan el producto — y la venta queda rota
  • Le cambian el nombre — y el histórico miente

Una venta es un registro histórico. Una vez registrada, nada de afuera la puede modificar. Eso es desacoplar.

Las reglas de la venta

  • Al vender un producto físico se descuenta el stock; si no alcanza, el detalle no se agrega
  • El digital nunca se queda sin stock
  • El total sale de sumar los detalles, cada uno con su precio y su envío
  • Una venta sin detalles no se guarda

Persistencia con CsvHelper

Los tres CRUDs guardan en archivos .csv con CsvHelper.

  • Un archivo por entidad: productos, clientes, ventas y los detalles de venta
  • Al abrir la aplicación se carga todo; al crear, actualizar o eliminar se guarda
  • Nada de leer ni escribir el CSV a mano: eso lo hace CsvHelper

La prueba: cerrar la aplicación, volver a abrirla y que esté todo, tal como lo dejaron.

Y acá viene lo bueno

Un CSV es plano. Su modelo no lo es.

  • ¿Cómo guardan productos físicos y digitales y los vuelven a leer sabiendo cuál es cuál?
  • ¿Cómo reconstruyen una venta con sus detalles si el CSV solo guarda filas sueltas?

No hay una única respuesta correcta. Hay respuestas justificadas. La suya la explican en la sustentación.

Entregables


  1. Comprimido del código — el proyecto completo, con los archivos .csv incluidos
  2. Diagrama de clases UML en archivo Draw.io

Criterios de evaluación

  • (25%) CRUD de productos: separación común / físico / digital y los dos formularios
  • (15%) CRUD de clientes: validaciones y su relación con las ventas
  • (35%) CRUD de ventas: SaleDetail, reglas de stock y cálculo del total
  • (25%) Persistencia con CsvHelper: guardar y recuperar sin perder datos

⚠️ La sustentación


La sustentación es individual y no tiene porcentaje. No suma: habilita.

El integrante que no sustente correctamente su entrega no tiene el taller evaluado — aunque el equipo haya entregado todo completo y funcionando.


No hay excepciones.

Proyecto Final

En parejas o equipos de tres personas

Construir una API REST con ASP.NET Core Web API en C# que exponga tres módulos: Products, Receipt y DetailReceipt.

En el Taller 2 la tienda tenía ventanas. Ahora no hay pantalla: cualquiera le habla por HTTP, y la API tiene que defenderse sola.

Los tres módulos

  1. Products — el catálogo: qué se vende, a qué precio y cuánto queda
  2. Receipt — la factura: número, fecha, cliente y total
  3. DetailReceipt — las líneas de la factura: qué se llevó y cuánto

Constrúyanlos en ese orden. El detalle es el que pone a prueba todo lo demás.

La arquitectura

Cada módulo se organiza en capas, y cada capa tiene un solo trabajo:

  • Controllers — reciben la petición HTTP y devuelven la respuesta. Nada más
  • Services — las reglas de negocio: stock, totales, validaciones
  • Repositories — hablan con la base de datos
  • DTOs — lo que entra y lo que sale de la API

Las dependencias se inyectan por constructor y se registran en Program.cs.

Módulo 1 — Products

  • GET /api/products — listar
  • GET /api/products/{id} — consultar uno

Datos mínimos: código, nombre, descripción, precio y stock.

El catálogo viene precargado en su archivo CSV. La API solo lo consulta; el stock cambia únicamente cuando se vende.

Módulo 2 — Receipt

  • GET /api/receipts — listar facturas
  • GET /api/receipts/{id} — la factura con sus detalles
  • POST /api/receipts — abrir una factura
  • POST /api/receipts/{id}/details — agregar un producto a la factura
  • POST /api/receipts/{id}/close — cerrarla

Una factura tiene número, fecha, documento y nombre del cliente, un estado (abierta / cerrada) y un total que calcula la factura, no quien llama a la API.

Módulo 3 — DetailReceipt

Cada producto agregado a la factura se convierte en un DetailReceipt. Igual que SaleDetail en el Taller 2, el detalle copia lo que se vendió:

  • Descripción del producto tal como estaba ese día
  • Precio unitario del momento de la venta
  • Cantidad y subtotal

No tiene endpoints propios: se crea desde la factura y se consulta con ella.

Las reglas de la factura

  • Al agregar un detalle se descuenta el stock; si no alcanza, el detalle no se agrega
  • El total sale de sumar los subtotales de los detalles
  • Una factura sin detalles no se puede cerrar
  • Una factura cerrada no se modifica: ni detalles, ni datos, ni se elimina

Y la pregunta de siempre: ¿qué pasa con una factura cerrada si mañana alguien cambia el precio del producto en el CSV? Respóndanlo con el diseño.

Entidades vs. DTOs

La API nunca expone las entidades. Entra un DTO, sale un DTO:


public record CreateDetailReceiptDto(int ProductId, int Quantity);

public record DetailReceiptDto(
    int Id,
    string Description,
    decimal UnitPrice,
    int Quantity,
    decimal Subtotal);
          

Fíjense: para crear un detalle no se envía el precio ni el subtotal. Eso lo decide el servidor.

Persistencia con CsvHelper

Sin base de datos. Todo se guarda en archivos .csv con CsvHelper.

  • Un archivo por entidad: productos, facturas y detalles de factura
  • Al levantar la API se carga todo; cada cambio se guarda
  • Nada de leer ni escribir el CSV a mano: eso lo hace CsvHelper

La prueba: detener la API, volver a levantarla y que esté todo, tal como lo dejaron.

Prohibido

  • Lógica de negocio en los controllers
  • Devolver o recibir entidades directamente en los endpoints
  • Aceptar el precio, subtotal o total desde la petición
  • Usar una base de datos en lugar de los archivos CSV

Entregables


  1. Comprimido del código — la solución completa, con los archivos .csv incluidos
  2. Swagger funcionando para probar todos los endpoints
  3. Diagrama de clases UML en archivo Draw.io

Criterios de evaluación

  • (10%) Módulo Products: consultas del catálogo
  • (25%) Módulo Receipt: agregar productos, cierre y cálculo del total
  • (25%) Módulo DetailReceipt: copia del precio y reglas de stock
  • (15%) Arquitectura en capas y DTOs
  • (25%) Persistencia con CsvHelper: guardar y recuperar sin perder datos

⚠️ La sustentación


La sustentación es individual y no tiene porcentaje. No suma: habilita.

El integrante que no sustente correctamente su entrega no tiene el proyecto evaluado — aunque el equipo haya entregado todo completo y funcionando.


No hay excepciones.