diff --git a/content/sesion09/Readme.md b/content/sesion09/Readme.md
index b73621e..20faaf7 100644
--- a/content/sesion09/Readme.md
+++ b/content/sesion09/Readme.md
@@ -1201,7 +1201,7 @@ git commit -m "Sesión 09"
git push origin main
```
----
+---
#### Resumen
@@ -1252,26 +1252,31 @@ psg-oop-2025/
---
-1. Crea el juego **"Piedra, Papel o Tijera"** utilizando el patrón **Singleton**
-para asegurar que solo exista **una única instancia del juego** en ejecución
-
-El juego permite que un jugador compita contra la computadora, la cual elige aleatoriamente entre las tres opciones:
+1. Crea el juego **Piedra, Papel o Tijera** implementando el patrón de diseño **Singleton**
+En esta versión, un jugador compite contra la computadora, la cual selecciona aleatoriamente entre las tres opciones disponibles:
`Piedra` , `Papel` , `Tijera`
-El objetivo es registrar las partidas ganadas por el jugador y por la computadora, manteniendo el control del puntaje dentro de la **única instancia** del juego
+Se debe registrar las partidas ganadas tanto por el jugador como por la computadora, manteniendo el control del puntaje dentro de la **única instancia** del juego.
-*(1/3)*
+*(1/4)*
---
-Crea la clase `PiedraPapelTijera` implementando el patrón **Singleton**
-Debe incluir:
+Implementa la clase **PiedraPapelTijera** aplicando el patrón de diseño **Singleton**, asegurando que exista **una única instancia del juego** durante toda la ejecución.
+
+La clase debe incluir los siguientes métodos:
+
+- `iniciarPartida()`: Permite jugar una ronda contra la computadora
+- `mostrarPuntaje()`: Muestra el puntaje acumulado del jugador y de la computadora.
+- `reiniciarJuego()`: Restablece los puntajes a cero.
+
+*(2/4)*
+
+---
+
+Además, se debe implementar un **menú interactivo en consola** con las siguientes opciones:
-- Un método para **iniciar una partida**
-- Un método para **mostrar el puntaje actual**
-- Un método para **reiniciar el juego** (puntajes en cero)
-- Un **menú interactivo en consola** que permita:
```markdown
1. Iniciar una nueva partida
2. Mostrar puntajes
@@ -1279,43 +1284,41 @@ Debe incluir:
4. Salir
```
-*(2/3)*
+*(3/4)*
---
-Realiza el **análisis** y el **diagrama de clases** para la clase `PiedraPapelTijera` en el archivo `ejercicio_04.md`
+- Elabora el **análisis** y el **diagrama de clases** de la clase `PiedraPapelTijera` en el archivo `ejercicio_01.md`.
+- Implementa el código en **Python** dentro del archivo `ejercicio_01.py`.
+- Prueba el juego en la consola para verificar su correcto funcionamiento antes de enviar tu solución
-Escribe el código Python en el archivo `ejercicio_04.py`
+*(4/4)*
-- Asegúrate de que las clases incluyan *atributos* y *métodos* coherentes con su propósito
-
-- Utiliza buenas prácticas de *nomenclatura*, *encapsulamiento* y *legibilidad*
+---
-- Prueba el juego en la consola para verificar su correcto funcionamiento
+2. Un DJ requiere una nueva consola de mezcla llamada **`BeatBox`**, capaz de mezclar pistas de audio en tiempo real.
+La consola debe permitir:
+- **Seleccionar una pista de audio**
+- **Ajustar el volumen** (subir o bajar)
+- **Mostrar el estado actual** de la consola (pista seleccionada y nivel de volumen)
+- **Aplicar un único efecto de sonido a la vez**, entre las opciones: `eco`, `reverb` o `distorsión`
-*(3/3)*
+*(1/4)*
---
-2. Un DJ necesita una nueva consola de mezcla `BeatBox` que permita mezclar pistas de audio en tiempo real
-Utiliza el patrón **Singleton** para asegurar que solo exista una única instancia de la consola de mezcla en ejecución
-La consola debe permitir:
-- Seleccionar pista de audio
-- Subir y bajar volumen
-- Mostrar el estado actual de la consola (pista seleccionada y volumen)
-- Puede aplicar un efecto de sonido a la vez (eco, reverb, distorsión)
+Crea la clase **`BeatBox`** implementando el patrón de diseño **Singleton**
+La clase debe incluir los siguientes métodos:
+- **`seleccionar_pista()`** → Permite elegir una pista de audio.
+- **`ajustar_volumen()`** → Permite subir o bajar el volumen.
+- **`aplicar_efecto()`** → Aplica un efecto de sonido (eco, reverb o distorsión).
+- **`mostrar_estado()`** → Muestra el estado actual de la consola (pista seleccionada y nivel de volumen).
-*(1/3)*
+*(24)*
---
-Crea la clase `BeatBox` implementando el patrón **Singleton**
-Debe incluir:
-- Un método para **seleccionar pista de audio**
-- Un método para **ajustar el volumen**
-- Un método para **aplicar un efecto de sonido**
-- Un método para **mostrar el estado actual** de la consola
-- Un **menú interactivo en consola** que permita:
+El programa debe ofrecer un menú con las siguientes opciones:
```markdown
1. Ingresar el nombre de la pista de audio
2. Ajustar volumen
@@ -1324,17 +1327,17 @@ Debe incluir:
5. Salir
```
-*(2/3)*
+*(3/4)*
---
-Realiza el **análisis** y el **diagrama de clases** para la clase `BeatBox` en el archivo `ejercicio_05.md`
-Escribe el código Python en el archivo `ejercicio_05.py`
-- Asegúrate de que las clases incluyan *atributos* y *métodos* coherentes con su propósito
+- Realiza el **análisis** y el **diagrama de clases** para la clase `BeatBox` en el archivo `ejercicio_02.md`
+- Implementa el código en **Python** dentro del archivo `ejercicio_02.py`
+- Asegúrate de que la clase incluya **atributos** y **métodos** coherentes con su propósito.
- Utiliza buenas prácticas de *nomenclatura*, *encapsulamiento* y *legibilidad*
-- Prueba la consola de mezcla en la consola para verificar su correcto funcionamiento
+- Prueba el juego en la consola para verificar su correcto funcionamiento antes de enviar tu solución
-*(3/3)*
+*(4/4)*
---
diff --git a/content/sesion10/Readme.md b/content/sesion10/Readme.md
new file mode 100644
index 0000000..c91514a
--- /dev/null
+++ b/content/sesion10/Readme.md
@@ -0,0 +1,1156 @@
+
+
+
+
+
+
+
+### Sesión 10
+#### Patrones de Diseño
+#### Factory
+
+---
+
+### Antes de empezar
+
+---
+
+#### Proyecto en VSCode
+
+Abre el proyecto en VSCode
+
+```bash
+code psg-oop-2025
+```
+
+Crea una carpeta con el nombre `sesion10`
+
+```bash
+mkdir sesion10
+cd sesion10
+```
+
+- Los archivos de esta sesión deben estar dentro de esta carpeta
+
+- Al finalizar la sesión, sube los cambios al repositorio en un commit
+
+---
+
+#### ¿Qué es un patrón de diseño?
+
+Un patrón de diseño es una **idea o solución** típica para resolver un problema
+
+que se repite **una y otra vez**
+
+---
+
+Los patrones varían en complejidad y propósito y pueden ser clasificados en tres categorías principales:
+
+- **Creacionales**: Se centran en la *creación* de objetos y clases
+- **Estructurales**: Se centran en la *composición* de clases y objetos
+- **De Comportamiento**: Se centran en la *interacción y responsabilidad* entre objetos
+
+---
+
+Nos enfocaremos en los **patrones creacionales**
+
+que son los que nos ayudan a crear objetos de manera eficiente y flexible
+
+---
+
+#### Patrones Creacionales
+
+Son aquellos que se encargan de la **creación** de objetos
+
+Ofrecen enfoques estructurados y reutilizables para crear **instancias** de clases
+
+los más comunes son:
+
+- **Singleton**
+- **Factory**
+
+---
+
+#### Factory
+
+Llamado
+
+**"metodo de fábrica"**
+
+Es como tener una **fábrica** que crea **objetos diferentes** según las **necesidades**
+
+---
+
+> "El patrón Factory es como pedir en una **cafetería**: tú solo dices qué quieres, y alguien más se encarga de prepararlo por ti."
+
+---
+
+
+
+---
+
+#### ¿Por qué usar Factory?
+
+- **Flexibilidad**: Permite crear diferentes tipos de objetos sin necesidad de conocer la clase exacta que se va a instanciar
+- **Desacoplamiento**: Separa la lógica de creación de objetos de su uso
+- **Mantenimiento**: Facilita la modificación y extensión del código sin afectar a las clases que lo utilizan
+
+---
+#### Ejemplos comunes
+
+*Aplicaciones web y escritorio*
+
+- **Creación de tipos de usuarios:** (administrador, cliente, etc.)
+- **Generación de reportes:** (PDF, Excel, etc.)
+- **Creación de notificaciones:** (correo electrónico, SMS, etc.)
+
+---
+#### Ejemplos comunes
+
+*Juegos y simulaciones*
+
+- **Creación de personajes:** (guerrero, mago, arquero, etc.)
+- **Generación de enemigos:** (zombies, dragones, robots, etc.)
+- **Creación de vehículos:** (coche, moto, avión, etc.)
+
+---
+
+#### Ejemplos comunes
+
+*Seguridad y autenticación*
+
+- **Generación de autenticación:** (Google, Facebook, GitHub, etc.)
+- **Generación de permisos:** (lectura, escritura, ejecución, etc.)
+- **Creación de autenticación biométrica:** (huella dactilar, reconocimiento facial, etc.)
+
+
+---
+#### Ejemplos comunes
+
+*IoT y dispositivos inteligentes*
+
+- **Estados de sensores:** (temperatura, humedad, luz, etc.)
+- **Generación de dispositivos:** (termómetro, cámara, bombilla, etc.)
+- **Generación de protocolos de comunicación:** (MQTT, HTTP, etc.)
+
+---
+
+#### Ventajas de usar Factory
+
+- **Reutilización de código**: Permite reutilizar el código de creación de objetos en diferentes partes
+- **Facilidad de mantenimiento**: Si se necesita cambiar la forma en que se crean los objetos, solo se modifica el Factory
+- **Escalabilidad**: Facilita la adición de nuevos tipos de objetos sin afectar al código existente
+
+---
+
+#### Desventajas de usar Factory
+
+- **Complejidad adicional**: Introduce una capa adicional de abstracción que puede hacer el código más complejo
+- **Sobrecarga de rendimiento**: En algunos casos, la creación de objetos a través de un Factory puede ser menos eficiente que la creación directa
+- **Granularidad**: Puede llevar a una sobreabundancia de clases y fábricas si no se gestiona adecuadamente
+
+---
+
+#### Diagrama de clases
+
+1. definir las clases que van a participar en el patrón Factory
+los productos que van a ser creados
+
+````
+```mermaid
+classDiagram
+ class Producto {
+ +usar()
+ }
+ class ProductoA {
+ +usar()
+ }
+ class ProductoB {
+ +usar()
+ }
+ Producto <|-- ProductoA
+ Producto <|-- ProductoB
+```
+````
+
+---
+#### Diagrama de clases
+
+Los productos A y B **heredan** de la clase Producto y serán creados por una fábrica
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+classDiagram
+direction LR
+ class Producto {
+ +usar()
+ }
+ class ProductoA {
+ +usar()
+ }
+ class ProductoB {
+ +usar()
+ }
+ Producto <|-- ProductoA
+ Producto <|-- ProductoB
+```
+
+---
+
+#### Diagrama de clases
+
+2. Ahora definimos la clase **Fábrica** y las fábricas concretas que crearán los productos A y B
+
+````
+```mermaid
+classDiagram
+ class Fabrica {
+ +crear()
+ }
+ class FabricaA {
+ +crear()
+ }
+ class FabricaB {
+ +crear()
+ }
+ Fabrica <|-- FabricaA
+ Fabrica <|-- FabricaB
+```
+````
+
+---
+#### Diagrama de clases
+
+La FabricaA y FabricaB heredan de la clase Fabrica ambos crearan productos de tipo Producto
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+classDiagram
+direction LR
+ class Fabrica {
+ +crear()
+ }
+ class FabricaA {
+ +crear()
+ }
+ class FabricaB {
+ +crear()
+ }
+ Fabrica <|-- FabricaA
+ Fabrica <|-- FabricaB
+```
+
+
+---
+#### Diagrama de clases
+
+3. Unimos las fábricas con los productos que crean se realiza mediante
+una **relación de asociación**, donde la fábrica crea productos de tipo Producto
+
+````
+```mermaid
+classDiagram
+ class Producto {
+ +usar()
+ }
+ class ProductoA {
+ +usar()
+ }
+ class ProductoB {
+ +usar()
+ }
+ class Fabrica {
+ +crear() Producto
+ }
+ class FabricaA {
+ +crear() Producto
+ }
+ class FabricaB {
+ +crear() Producto
+ }
+ Producto <|-- ProductoA
+ Producto <|-- ProductoB
+ Fabrica <|-- FabricaA
+ Fabrica <|-- FabricaB
+ Fabrica --> Producto
+```
+````
+---
+#### Diagrama de clases
+
+El diagrama final muestra la relación entre las fábricas y los productos que se va a crear
+
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+classDiagram
+direction LR
+ class Producto {
+ +usar()
+ }
+ class ProductoA {
+ +usar()
+ }
+ class ProductoB {
+ +usar()
+ }
+ class Fabrica {
+ +crear() Producto
+ }
+ class FabricaA {
+ +crear() Producto
+ }
+ class FabricaB {
+ +crear() Producto
+ }
+ Producto <|-- ProductoA
+ Producto <|-- ProductoB
+ Fabrica <|-- FabricaA
+ Fabrica <|-- FabricaB
+ Fabrica --> Producto
+```
+
+
+---
+
+#### Estructura de Factory en Python
+
+**Factory** implementa **Herencia** y **polimorfismo** pilares de la programación orientada a objetos
+
+La estructura de un Factory se puede ver mejor en un ejemplo **práctico**
+
+---
+
+#### Ejemplo 01
+
+Crear los archivos `cafeteria.md` y `cafeteria.py` en la carpeta `sesion10`
+```markdown
+Una cafetería prepara Expresos y Cappuccinos,
+el cliente (tú) solo debes pedir el tipo de café que desea
+el barista utilizará las máquinas adecuadas para preparar el café
+Todos los cafes se pueden tomar
+Las cafeteras preparan un tipo de café, después son consumidos
+por el cliente
+Al ingresar salir se termina la ejecución
+```
+
+
+Se implementará el patrón Factory
+
+---
+
+Análisis
+
+```markdown
+# Análisis
+Requisitos:
+- Se preparan dos tipos de café: Expreso y Cappuccino
+- El cliente solo debe pedir el tipo de café que desea
+- Se utiliza la máquina adecuada para cada café
+- Todos los cafés se pueden tomar
+- Las cafeteras preparan un tipo de café
+- Si se ingresa "salir" se termina la ejecución
+
+Objetos:
+- Café (Expreso, Cappuccino)
+- Cafetera (CafeteraExpreso, CafeteraCappuccino)
+- Barista
+
+Características:
+- Café: (sin características)
+- Expreso: (Café)
+- Cappuccino: (Café)
+- Cafetera: (sin características)
+- CafeteraExpreso: (Cafetera)
+- CafeteraCappuccino: (Cafetera)
+- Barista: (sin características)
+
+Acciones:
+- Café: tomar
+- Expreso: tomar
+- Cappuccino: tomar
+- Cafetera: preparar
+- CafeteraExpreso: preparar
+- CafeteraCappuccino: preparar
+- Barista: preparar cafe
+```
+
+---
+#### Diagrama de clases
+
+````
+```mermaid
+classDiagram
+ class Cafe {
+ +tomar()
+ }
+ class Expreso {
+ +tomar()
+ }
+ class Cappuccino {
+ +tomar()
+ }
+ class Cafetera {
+ +preparar()
+ }
+ class CafeteraExpreso {
+ +preparar()
+ }
+ class CafeteraCappuccino {
+ +preparar()
+ }
+ class Barista {
+ +preparar_cafe()
+ }
+ Cafe <|-- Expreso
+ Cafe <|-- Cappuccino
+ Cafetera <|-- CafeteraExpreso
+ Cafetera <|-- CafeteraCappuccino
+ Cafetera --> Cafe
+ Barista --> Cafetera
+```
+````
+
+---
+#### Diagrama de clases
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+classDiagram
+direction LR
+ class Cafe {
+ +tomar()
+ }
+ class Expreso {
+ +tomar()
+ }
+ class Cappuccino {
+ +tomar()
+ }
+ class Cafetera {
+ +preparar()
+ }
+ class CafeteraExpreso {
+ +preparar()
+ }
+ class CafeteraCappuccino {
+ +preparar()
+ }
+ class Barista {
+ +preparar_cafe()
+ }
+ Cafe <|-- Expreso
+ Cafe <|-- Cappuccino
+ Cafetera <|-- CafeteraExpreso
+ Cafetera <|-- CafeteraCappuccino
+ Cafetera --> Cafe
+ Barista --> Cafetera
+```
+
+
+---
+#### Implementación en Python
+
+Primero definimos la clase base `Cafe` y las clases derivadas `Expreso` y `Cappuccino`
+
+```python
+class Cafe:
+ def tomar(self):
+ print("Tomando un café")
+
+class Expreso(Cafe):
+ def tomar(self):
+ print("☕ Tomando un expreso")
+
+class Cappuccino(Cafe):
+ def tomar(self):
+ print("🥛 Tomando un cappuccino")
+```
+
+---
+Luego definimos la clase base `Cafetera` y las clases derivadas `CafeteraExpreso` y `CafeteraCappuccino`
+
+```python
+class Cafetera:
+ def preparar(self):
+ pass
+class CafeteraExpreso(Cafetera):
+ def preparar(self):
+ return Expreso()
+class CafeteraCappuccino(Cafetera):
+ def preparar(self):
+ return Cappuccino()
+```
+
+---
+Definimos las clases `Barista` que interactúa con las cafeteras
+
+```python
+class Barista:
+ def preparar_cafe(self, tipo):
+ if tipo == "expreso":
+ return CafeteraExpreso().preparar()
+ if tipo == "cappuccino":
+ return CafeteraCappuccino().preparar()
+ raise ValueError("❌ Café no disponible. Intente de nuevo")
+```
+
+---
+Finalmente creamos la lógica donde como clientes pedimos el **café** al barista
+
+```python
+while True:
+ tipo_cafe = input("💬 ¿Qué café desea? (expreso/cappuccino/salir): ")
+ tipo_cafe = tipo_cafe.lower().strip()
+ if tipo_cafe == "salir":
+ print("🚶♂️ Saliendo de la cafetería.")
+ break
+ try:
+ barista = Barista()
+ cafe = barista.preparar_cafe(tipo_cafe)
+ cafe.tomar()
+ except ValueError as e:
+ print(e)
+```
+
+
+---
+#### Código completo
+
+```python
+# Productos
+class Cafe:
+ def tomar(self):
+ print("Tomando un café")
+
+class Expreso(Cafe):
+ def tomar(self):
+ print("☕ Tomando un expreso")
+
+class Cappuccino(Cafe):
+ def tomar(self):
+ print("🥛 Tomando un cappuccino")
+
+# Fabricas
+class Cafetera:
+ def preparar(self):
+ pass
+class CafeteraExpreso(Cafetera):
+ def preparar(self):
+ return Expreso()
+class CafeteraCappuccino(Cafetera):
+ def preparar(self):
+ return Cappuccino()
+
+# Factory Central (decide qué fábrica usar)
+class Barista:
+ def preparar_cafe(self, tipo):
+ if tipo == "expreso":
+ return CafeteraExpreso().preparar()
+ if tipo == "cappuccino":
+ return CafeteraCappuccino().preparar()
+ raise ValueError("❌ Café no disponible. Intente de nuevo")
+
+# Cliente (No conoce las fábricas, solo pide el café)
+while True:
+ tipo_cafe = input("💬 ¿Qué café desea? (expreso/cappuccino/salir): ")
+ tipo_cafe = tipo_cafe.lower().strip()
+ if tipo_cafe == "salir":
+ print("🚶♂️ Saliendo de la cafetería.")
+ break
+ try:
+ barista = Barista()
+ cafe = barista.preparar_cafe(tipo_cafe)
+ cafe.tomar()
+ except ValueError as e:
+ print(e)
+```
+
+---
+
+#### Ejecución
+
+```bash
+python cafeteria.py
+```
+
+```text
+💬 ¿Qué café desea? (expreso/cappuccino/salir): expreso
+☕ Tomando un expreso
+💬 ¿Qué café desea? (expreso/cappuccino/salir): cappuccino
+🥛 Tomando un cappuccino
+💬 ¿Qué café desea? (expreso/cappuccino/salir): latte
+❌ Café no disponible. Intente de nuevo
+💬 ¿Qué café desea? (expreso/cappuccino/salir): salir
+🚶♂️ Saliendo de la cafetería.
+```
+
+---
+
+En el ejemplo anterior se muestra cómo el patrón Factory
+permite **crear diferentes** tipos de café sin que el cliente
+conozca los detalles de la creación
+
+---
+
+El único que conoce los detalles de la creación es el **Barista**
+que utiliza las **fábricas adecuadas** para preparar el café solicitado
+
+---
+
+#### Ejercicio para ti (02)
+
+Crea los archivos `juego.md` y `juego.py` en la carpeta `sesion10`
+
+Obtener el *Análisis*
+
+---
+
+
+```markdown
+En un juego de rol, los jugadores pueden elegir diferentes
+tipos de armas.
+Cuando un jugador solicita un arma, el armero utiliza
+la herramienta adecuada para crearla
+Cada herramienta crea un solo tipo de arma:
+- El martillo crea espadas
+- La Sierra crea arcos
+Todas las armas pueden usarse para atacar
+Si el jugador escribe "salir" en lugar de un tipo de arma,
+el programa finaliza.
+```
+
+3 minutos
+
+
+
+---
+
+#### Análisis
+
+```markdown
+Requisitos:
+- Hay dos tipos de armas: Espada y Arco.
+- El jugador solo debe pedir el tipo de arma que desea.
+- Se usa la herramienta adecuada para fabricar cada arma.
+- Todas las armas pueden usarse para atacar.
+- Cada herramienta fabrica un tipo de arma.
+- Si se ingresa "salir" se termina la ejecución.
+
+Objetos:
+- Arma (Espada, Arco)
+- Herramienta (Martillo, Sierra)
+- Armero
+
+Características:
+- Arma: (sin características extra)
+- Espada: (Arma)
+- Arco: (Arma)
+- Herramienta: (sin características)
+- Martillo: (Herramienta)
+- Sierra: (Herramienta)
+- Armero: (sin características)
+
+Acciones:
+- Arma: atacar()
+- Espada: atacar()
+- Arco: atacar()
+- Herramienta: fabricar()
+- Martillo: fabricar()
+- Sierra: fabricar()
+- Armero: (sin acciones)
+```
+
+---
+
+#### Ejercicio para ti (02)
+
+Ahora obtenemos el diseño del *diagrama de clase*
+
+2 minutos
+
+
+
+Modifica el archivo `juego.md`
+
+---
+
+#### Diagrama de clases
+
+````
+```mermaid
+classDiagram
+ class Arma {
+ +atacar()
+ }
+ class Espada {
+ +atacar()
+ }
+ class Arco {
+ +atacar()
+ }
+ class Herramienta {
+ +fabricar()
+ }
+ class Martillo {
+ +fabricar()
+ }
+ class Sierra {
+ +fabricar()
+ }
+ class Armero {
+ }
+ Arma <|-- Espada
+ Arma <|-- Arco
+ Herramienta <|-- Martillo
+ Herramienta <|-- Sierra
+ Herramienta --> Arma
+ Armero --> Herramienta
+```
+````
+
+
+---
+
+#### Diagrama de clases
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn"}}%%
+classDiagram
+direction LR
+ class Arma {
+ +atacar()
+ }
+ class Espada {
+ +atacar()
+ }
+ class Arco {
+ +atacar()
+ }
+ class Herramienta {
+ +fabricar()
+ }
+ class Martillo {
+ +fabricar()
+ }
+ class Sierra {
+ +fabricar()
+ }
+ class Armero {
+ +fabricar_arma()
+ }
+ Arma <|-- Espada
+ Arma <|-- Arco
+ Herramienta <|-- Martillo
+ Herramienta <|-- Sierra
+ Herramienta --> Arma
+ Armero --> Herramienta
+```
+
+
+
+---
+
+#### Ejercicio para ti (02)
+
+Ya tenemos el *análisis y diseño* de la clase
+
+Ahora podemos **programar**
+
+Modifica el archivo `juego.py`
+
+2 minutos
+
+
+
+Primero implementamos las armas
+
+---
+
+Clases de las armas
+
+```python
+class Arma:
+ def atacar(self):
+ print("Atacando con un arma")
+
+class Espada(Arma):
+ def atacar(self):
+ print("🗡️ Atacando con una espada")
+
+class Arco(Arma):
+ def atacar(self):
+ print("🏹 Atacando con un arco")
+```
+
+
+---
+
+Segundo implementamos las herramientas que fabrican las armas
+
+2 minutos
+
+
+
+Modifica el archivo `juego.py`
+
+---
+```python
+class Herramienta:
+ def fabricar(self):
+ pass
+
+class Martillo(Herramienta):
+ def fabricar(self):
+ return Espada()
+
+class Sierra(Herramienta):
+ def fabricar(self):
+ return Arco()
+```
+
+---
+
+#### Implementación del Armero
+
+Tercero implementar el Armero que utiliza las herramientas para fabricar las armas
+
+2 minutos
+
+
+
+Modifica el archivo `juego.py`
+
+---
+
+```python
+class Armero:
+ def fabricar_arma(self, tipo):
+ if tipo == "espada":
+ return Martillo().fabricar()
+ if tipo == "arco":
+ return Sierra().fabricar()
+ raise ValueError("❌ Arma no disponible. Intente de nuevo")
+```
+
+---
+
+#### Interacción del jugador
+
+Ahora que tenemos el Armero, implementar la lógica del juego donde el jugador solicita un arma
+al armero y si ingresa "salir" se termina la ejecución
+
+2 minutos
+
+
+
+---
+
+```python
+while True:
+ tipo_arma = input("💬 ¿Qué arma desea? (espada/arco/salir): ")
+ tipo_arma = tipo_arma.lower().strip()
+ if tipo_arma == "salir":
+ print("🚶♂️ Saliendo del juego.")
+ break
+ try:
+ armero = Armero()
+ arma = armero.fabricar_arma(tipo_arma)
+ arma.atacar()
+ except ValueError as e:
+ print(e)
+```
+
+---
+
+#### Código completo
+
+```python
+class Arma:
+ def atacar(self):
+ print("Atacando con un arma")
+
+class Espada(Arma):
+ def atacar(self):
+ print("🗡️ Atacando con una espada")
+
+class Arco(Arma):
+ def atacar(self):
+ print("🏹 Atacando con un arco")
+
+class Herramienta:
+ def fabricar(self):
+ pass
+
+class Martillo(Herramienta):
+ def fabricar(self):
+ return Espada()
+
+class Sierra(Herramienta):
+ def fabricar(self):
+ return Arco()
+
+class Armero:
+ def fabricar_arma(self, tipo):
+ if tipo == "espada":
+ return Martillo().fabricar()
+ if tipo == "arco":
+ return Sierra().fabricar()
+ raise ValueError("❌ Arma no disponible. Intente de nuevo")
+
+while True:
+ tipo_arma = input("💬 ¿Qué arma desea? (espada/arco/salir): ")
+ tipo_arma = tipo_arma.lower().strip()
+ if tipo_arma == "salir":
+ print("🚶♂️ Saliendo del juego.")
+ break
+ try:
+ armero = Armero()
+ arma = armero.fabricar_arma(tipo_arma)
+ arma.atacar()
+ except ValueError as e:
+ print(e)
+```
+
+---
+
+#### Ejecución
+
+```bash
+python juego.py
+```
+
+```text
+💬 ¿Qué arma desea? (espada/arco/salir): espada
+🗡️ Atacando con una espada
+💬 ¿Qué arma desea? (espada/arco/salir): arco
+🏹 Atacando con un arco
+💬 ¿Qué arma desea? (espada/arco/salir): hacha
+❌ Arma no disponible. Intente de nuevo
+💬 ¿Qué arma desea? (espada/arco/salir): salir
+🚶♂️ Saliendo del juego.
+```
+
+---
+
+Subimos los avances de la sesión al repositorio en **GitHub**
+
+```bash
+git add .
+git commit -m "Sesión 10"
+git push origin main
+```
+
+---
+
+#### Resumen
+
+- Un patrón de diseño es una solución típica para resolver problemas recurrentes en el desarrollo de software.
+- Los patrones de diseño se clasifican en creacionales, estructurales y de comportamiento según su propósito.
+- Los patrones creacionales ayudan a crear objetos de manera eficiente y flexible.
+
+---
+
+- El patrón Factory pertenece a los patrones creacionales y permite crear diferentes tipos de objetos según las necesidades.
+- El patrón Factory aporta flexibilidad, desacoplamiento y facilidad de mantenimiento al separar la lógica de creación de objetos de su uso.
+- El patrón Factory facilita la reutilización de código, la escalabilidad y el mantenimiento al permitir modificar la forma de crear objetos sin afectar el resto del código.
+
+---
+
+- El uso del patrón Factory puede agregar complejidad adicional y sobrecarga de rendimiento, y puede llevar a una sobreabundancia de clases si no se gestiona adecuadamente.
+- El patrón Factory se representa en diagramas de clases mediante la relación entre fábricas y productos, mostrando herencia y asociación.
+- La estructura del patrón Factory en Python utiliza herencia y polimorfismo para definir fábricas y productos.
+
+---
+
+- El análisis de requisitos, características y acciones es fundamental para diseñar correctamente las clases y sus relaciones al aplicar el patrón Factory.
+- Los diagramas de clases ayudan a visualizar la estructura y relaciones entre fábricas y productos en el patrón Factory.
+- El patrón Factory permite crear objetos sin que el cliente conozca los detalles de la creación, delegando esa responsabilidad a una fábrica especializada.
+
+
+---
+
+#### Retos
+
+Crear una carpeta con el nombre "retos_sesion_10" dentro del proyecto en la raíz, en la cual por cada ejercicio debes crear los siguientes archivos:
+
+```bash
+# Estructura de carpetas
+psg-oop-2025/
+ retos_sesion_10/
+ ejercicio_01.md
+ ejercicio_01.py
+ ejercicio_02.md
+ ejercicio_02.py
+```
+
+---
+
+1. Utilizando el patrón **Factory**
+
+En una heladería, los clientes pueden pedir helados de **vainilla** o **chocolate**.
+Además, pueden elegir si desean su helado en **cono** o en **vaso**.
+
+El encargado utiliza la **máquina adecuada** para preparar cada sabor:
+
+- La *máquina de vainilla* solo prepara helados de vainilla.
+- La *máquina de chocolate* solo prepara helados de chocolate.
+
+*(1/5)*
+
+---
+
+Todos los helados comparten características en común:
+- Se pueden **comer**.
+- Tienen un **envase** (cono o vaso).
+
+Debe permitir al usuario realizar pedidos de helado mediante un **Factory**, y escribir `"salir"` para terminar el proceso.
+
+*(2/5)*
+
+---
+
+- Los helados se representan como:
+
+`"[sabor] 🍦 en [envase]"`
+
+- El menú de opciones se representa como:
+
+```text
+🍨 Pedidos de Helado 🍨
+1. Vainilla en Cono
+2. Vainilla en Vaso
+3. Chocolate en Cono
+4. Chocolate en Vaso
+Escribe "salir" para terminar.
+```
+
+*(3/5)*
+
+---
+
+Debe permitir:
+
+Crear helados mediante una **fábrica**
+
+Representar cada helado en el formato especificado.
+
+Registrar varios pedidos hasta que el usuario escriba "salir".
+
+*(4/5)*
+
+---
+
+Instrucciones:
+
+- Realiza el **análisis** y **diagrama de clases** para las clases `Helado`, `HeladoVainilla`, `HeladoChocolate`, `Maquina`, `MaquinaVainilla`, `MaquinaChocolate` en `ejercicio_01.md`
+- Escribe el código en Python implementando el patrón Factory en `ejercicio_01.py`
+- Prueba varios pedidos de helado para verificar el correcto funcionamiento
+
+*(5/5)*
+
+---
+
+2. Utilizando el patrón **Factory**
+
+Simulador de Batalla de Monstruos
+
+Dos jugadores pueden elegir un **monstruo** para luchar.
+Cada jugador selecciona uno de los siguientes tipos:
+
+- **Dragón**
+- **Zombi**
+- **Vampiro**
+
+*(1/5)*
+
+---
+
+Cada monstruo tiene fortalezas y debilidades:
+
+```markdown
+- Dragón:
+ - Fuerte contra Zombi
+ - Débil contra Vampiro
+ - Igual contra Dragón
+- Zombi:
+ - Fuerte contra Vampiro
+ - Débil contra Dragón
+ - Igual contra Zombi
+- Vampiro:
+ - Fuerte contra Dragón
+ - Débil contra Zombi
+ - Igual contra Vampiro
+```
+
+El simulador debe determinar el **resultado de la batalla** entre los monstruos elegidos, si alguno de los jugadores escribe `"salir"`, el juego termina.
+
+*(2/5)*
+
+---
+
+
+Los monstruos se representan como:
+
+`"[monstruo] 🧟♂️ listo para luchar"`
+
+El menú de selección se representa como:
+
+```text
+🧩 Selección de Monstruos 🧩
+Jugador 1: Elige tu monstruo (dragón/zombi/vampiro):
+Jugador 2: Elige tu monstruo (dragón/zombi/vampiro):
+Escribe "salir" para terminar.
+```
+
+*(3/5)*
+
+---
+
+Debe permitir:
+
+- Crear monstruos mediante una **fábrica**
+- Representar cada monstruo en el formato especificado.
+- Determinar el resultado de la batalla según las fortalezas y debilidades.
+- Registrar varias batallas hasta que un jugador escriba "salir".
+
+*(4/5)*
+
+---
+
+Instrucciones:
+
+- Realiza el **análisis** y **diagrama de clases** para las clases `Monstruo`, `Dragon`, `Zombi`, `Vampiro`, `Spawner`, `SpawnerDragon`, `SpawnerZombi`, `SpawnerVampiro` en `ejercicio_02.md`
+- Escribe el código en Python implementando el patrón Factory en `ejercicio_02.py`
+- Prueba las batallas entre diferentes monstruos para verificar el correcto funcionamiento
+
+*(5/5)*
+
+---
+
+
+
+
+
+
+
+
+[ ](https://github.com/python-la-paz/python-study-group-oop/tree/main/content/sesion10)
+
+Repositorio de la Sesión
+
+---
+
+## Bibliografía y Referencias
+
+- [Factory Method](https://refactoring.guru/es/design-patterns/factory-method)
+- [Patrón Factory Method Introducción y ejemplos](https://revistacompleta.com/patron-factory-method-introduccion-y-ejemplos/)
+- [05-FactoryPattern](https://uetitc.github.io/ProgrammingII-2024-2/Lessons/05-FactoryPattern.html)
+- [Factory Pattern](https://www.chucksacademy.com/es/topic/javascript-design-patterns/factory-pattern)
+- [Object Oriented Analysis](https://www.gyata.ai/es/object-oriented-programming/object-oriented-analysis)
+- [Diagrama de clases](https://diagramasuml.com/diagrama-de-clases/)
+- [Guía PEP 8](https://peps.python.org/pep-0008/#class-names)
+- [Mermaid Charts](https://www.mermaidchart.com/play)
+- [Draw.io](https://app.diagrams.net/)
+- [Python 3 Object-oriented Programming, Second Edition, Dusty Phillips](https://github.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition)
+
diff --git a/content/sesion10/img/factory.gif b/content/sesion10/img/factory.gif
new file mode 100644
index 0000000..669256a
Binary files /dev/null and b/content/sesion10/img/factory.gif differ
diff --git a/content/sesion10/index.html b/content/sesion10/index.html
new file mode 100644
index 0000000..b5023ba
--- /dev/null
+++ b/content/sesion10/index.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ Sesión 10 | Python Study Group | OOP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/content/sesion11/Readme.md b/content/sesion11/Readme.md
new file mode 100644
index 0000000..d91f9c5
--- /dev/null
+++ b/content/sesion11/Readme.md
@@ -0,0 +1,1779 @@
+
+
+
+
+
+
+
+### Sesión 11
+#### Buenas prácticas
+#### Refactorización y organización
+
+---
+
+### Antes de empezar
+
+---
+
+#### Proyecto en VSCode
+
+Abre el proyecto en VSCode
+
+```bash
+code psg-oop-2025
+```
+
+Crea una carpeta con el nombre `sesion11`
+
+```bash
+mkdir sesion11
+cd sesion11
+```
+
+- Los archivos de esta sesión deben estar dentro de esta carpeta
+
+- Al finalizar la sesión, sube los cambios al repositorio en un commit
+
+---
+
+#### Introducción a la refactorización
+
+La refactorización es el proceso de mejorar el código existente sin cambiar su **funcionalidad** externa
+
+---
+
+#### ¿Por qué refactorizar?
+
+- Mejora la **legibilidad** del código
+- Facilita el **mantenimiento** y la evolución del software
+- Reduce la **complejidad** y el riesgo de errores
+
+---
+
+#### ¿Qué implica la refactorización?
+
+- *Reorganizar* el código en módulos y clases
+- *Eliminar* código duplicado
+- *Renombrar* variables y funciones para que sean más descriptivas
+- *Dividir* funciones grandes en funciones más pequeñas y manejables
+
+---
+
+#### Reorganización del código
+
+En python se puede organizar el código en **archivos y carpetas** para mejorar la estructura del proyecto
+
+
+
+---
+
+En lugar de tener un *archivo gigante* con todo el código, podemos dividirlo en **varios archivos** y carpetas
+
+---
+
+#### Buenas prácticas de organización
+
+- Utilizar **nombres descriptivos** para archivos y carpetas
+- **Agrupar** archivos relacionados en carpetas
+- **Mantener una estructura** coherente en todo el proyecto
+
+---
+
+En Python se recomienda seguir la convención [PEP 8](https://peps.python.org/pep-0008/)
+
+| Elemento | Convención | Ejemplo |
+| --------------------- | ---------- | ------------------------------ |
+| Clases | CamelCase | `MiClase` |
+| Funciones y variables | snake_case | `mi_funcion`, `mi_variable` |
+| Constantes | UPPER_CASE | `MI_CONSTANTE` |
+| Archivos y carpetas | snake_case | `mi_archivo.py`, `mi_carpeta/` |
+| Módulos y paquetes | snake_case | `mi_modulo` |
+
+---
+
+#### Ejemplo 01
+
+```markdown
+Juega "Piedra, Papel o Tijera" con un jugador y una computadora
+Ingresa el nombre del jugador y elige piedra, papel o tijera
+La computadora hará su elección al azar
+y se determinará el ganador según las reglas del juego
+Puede jugar varias rondas hasta que el jugador decida salir
+El juego esta compuesto por ambos jugadores
+Cuenta con un menú
+
+1. jugar
+2. salir
+```
+
+Crearemos un archivo llamado `juego.md` con el **análisis y diagrama**
+y el archivo `juego.py` con la **lógica** del juego
+
+---
+
+Análisis
+
+```markdown
+Requisitos:
+- El jugador debe ingresar su nombre
+- El jugador debe elegir entre piedra, papel o tijera
+- La computadora debe elegir al azar entre piedra, papel o tijera
+- El juego debe determinar el ganador según las reglas del juego
+- El juego debe mostrar el resultado de cada partida
+- El juego debe permitir al jugador jugar varias partidas
+- El juego debe tener un menú para jugar o salir
+- El juego esta compuesto por ambos jugadores (Jugador y Computadora)
+
+Objetos:
+- Jugador
+- Computadora
+- Juego
+
+Características:
+- Jugador:
+ - nombre
+- Computadora:
+ - nombre
+- Juego:
+ - jugador
+ - computadora
+
+Acciones:
+- Jugador:
+ - elegir
+- Computadora:
+ - elegir
+- Juego:
+ - jugar
+ - determinar ganador
+```
+
+---
+
+Diagrama de clases en Mermaid
+
+````
+```mermaid
+classDiagram
+ class Jugador {
+ +nombre: string
+ +elegir()
+ }
+ class Computadora {
+ +nombre: string
+ +elegir()
+ }
+ class Juego {
+ +jugador: Jugador
+ +computadora: Computadora
+ +determinar_ganador(eleccion_jugador, eleccion_cpu)
+ +jugar()
+ }
+ Juego *-- Jugador
+ Juego *-- Computadora
+```
+````
+
+---
+
+Diagrama de clases en Mermaid
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+classDiagram
+ class Jugador {
+ +nombre: string
+ +elegir()
+ }
+ class Computadora {
+ +nombre: string
+ +elegir()
+ }
+ class Juego {
+ +jugador: Jugador
+ +computadora: Computadora
+ +determinar_ganador(eleccion_jugador, eleccion_cpu)
+ +jugar()
+ }
+ Juego *-- Jugador
+ Juego *-- Computadora
+```
+
+
+---
+
+Código del juego de Piedra, Papel o Tijera
+
+```python [1|2-11|13-18|20-45|47-65]
+import random
+
+class Jugador:
+ def __init__(self, nombre):
+ self.nombre = nombre
+
+ def elegir(self):
+ eleccion = input(f"{self.nombre}, elige piedra, papel o tijera: ").lower()
+ while eleccion not in ["piedra", "papel", "tijera"]:
+ eleccion = input("Opción inválida. Intenta de nuevo: ").lower()
+ return eleccion
+
+class Computadora:
+ def __init__(self):
+ self.nombre = "Computadora"
+
+ def elegir(self):
+ return random.choice(["piedra", "papel", "tijera"])
+
+class Juego:
+ def __init__(self, jugador, computadora):
+ self.jugador = jugador
+ self.computadora = computadora
+
+ def determinar_ganador(self, eleccion_jugador, eleccion_computadora):
+ if eleccion_jugador == eleccion_computadora:
+ return "Empate"
+ elif (
+ (eleccion_jugador == "piedra" and eleccion_computadora == "tijera") or
+ (eleccion_jugador == "papel" and eleccion_computadora == "piedra") or
+ (eleccion_jugador == "tijera" and eleccion_computadora == "papel")
+ ):
+ return f"{self.jugador.nombre} gana!"
+ else:
+ return f"{self.computadora.nombre} gana!"
+
+ def jugar(self):
+ eleccion_jugador = self.jugador.elegir()
+ eleccion_computadora = self.computadora.elegir()
+
+ print(f"{self.jugador.nombre} eligió: {eleccion_jugador}")
+ print(f"{self.computadora.nombre} eligió: {eleccion_computadora}")
+
+ resultado = self.determinar_ganador(eleccion_jugador, eleccion_computadora)
+ print(resultado)
+
+print("🎮 Bienvenido al juego de Piedra, Papel o Tijera")
+nombre = input("Introduce tu nombre: ")
+jugador = Jugador(nombre)
+computadora = Computadora()
+juego = Juego(jugador, computadora)
+
+while True:
+ print("\n--- Menú ---")
+ print("1. Jugar")
+ print("2. Salir")
+ opcion = input("Elige una opción: ")
+
+ if opcion == "1":
+ juego.jugar()
+ elif opcion == "2":
+ print("¡Gracias por jugar! 👋")
+ break
+ else:
+ print("Opción inválida, intenta de nuevo.")
+```
+
+---
+
+Probamos el juego para asegurarnos que funciona **correctamente**
+
+```bash
+python juego.py
+```
+
+```text
+🎮 Bienvenido al juego de Piedra, Papel o Tijera
+Introduce tu nombre: Jhon
+--- Menú ---
+1. Jugar
+2. Salir
+Elige una opción: 1
+Jhon, elige piedra, papel o tijera: piedra
+Jhon eligió: piedra
+Computadora eligió: tijera
+Jhon gana!
+--- Menú ---
+1. Jugar
+2. Salir
+Elige una opción: 2
+¡Gracias por jugar! 👋
+```
+
+---
+
+**Reorganizaremos** el juego en archivos dentro de una carpeta llamada `juego`
+
+Donde primero separaremos las partes del juego en diferentes archivos
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+graph TD;
+ A["📄 juego.py"]
+ B["📕 juego.md"]
+```
+
+
+⬇
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+graph TD;
+ A["📁 juego"]
+ B["📄 jugador.py"]
+ C["📄 computadora.py"]
+ D["📄 juego.py"]
+ E["📄 main.py"]
+ F["📕 juego.md"]
+ A --> B
+ A --> C
+ A --> D
+ A --> E
+ A --> F
+```
+
+
+---
+
+#### 1. Crear la carpeta y los archivos
+
+**Crear** la carpeta `juego` y los archivos vacíos dentro
+
+```markdown
+Con `mkdir` creamos la carpeta
+Con `cd` entramos a la carpeta
+Con `touch` creamos los archivos vacíos
+Con `tree` vemos la estructura de carpetas y archivos
+```
+
+```bash
+mkdir juego
+cd juego
+touch jugador.py computadora.py juego.py main.py
+tree
+```
+
+```bash
+.
+├── computadora.py
+├── juego.py
+├── jugador.py
+└── main.py
+
+1 directory, 4 files
+```
+
+---
+
+**Mover** el archivo `juego.md` a la carpeta `juego` para mantener todo organizado
+
+```markdown
+Con `cd ..` volvemos a la carpeta anterior
+Con `mv` movemos el archivo `juego.md` a la carpeta `juego`
+Con `cd juego` entramos a la carpeta `juego`
+Con `tree` vemos la estructura de carpetas y archivos
+```
+
+```bash
+cd ..
+mv juego.md juego
+cd juego
+tree
+```
+
+```bash
+.
+├── computadora.py
+├── juego.md
+├── juego.py
+├── jugador.py
+└── main.py
+
+1 directory, 5 files
+```
+
+
+---
+
+#### 2. Llenar los archivos con el código
+
+Ya con la estructura creada, **llenar** cada archivo con el código correspondiente
+
+El archivo `jugador.py` contendrá la clase `Jugador`
+
+```python
+# jugador.py
+class Jugador:
+ def __init__(self, nombre):
+ self.nombre = nombre
+ def elegir(self):
+ eleccion = input(f"{self.nombre}, elige piedra, papel o tijera: ").lower()
+ while eleccion not in ["piedra", "papel", "tijera"]:
+ eleccion = input("Opción inválida. Intenta de nuevo: ").lower()
+ return eleccion
+```
+
+---
+
+El archivo `computadora.py` contendrá la clase `Computadora`
+
+
+**Importamos** el módulo `random` porque el método `elegir` de la clase `Computadora` utiliza la función `choice` de este módulo para seleccionar una opción al azar
+
+```python
+# computadora.py
+import random
+class Computadora:
+ def __init__(self):
+ self.nombre = "Computadora"
+
+ def elegir(self):
+ return random.choice(["piedra", "papel", "tijera"])
+```
+
+
+---
+
+En el archivo `juego.py` **copiar** la clase `Juego`
+
+```python
+# juego.py
+class Juego:
+ def __init__(self, jugador, computadora):
+ self.jugador = jugador
+ self.computadora = computadora
+
+ def determinar_ganador(self, eleccion_jugador, eleccion_computadora):
+ if eleccion_jugador == eleccion_computadora:
+ return "Empate"
+ elif (
+ (eleccion_jugador == "piedra" and eleccion_computadora == "tijera") or
+ (eleccion_jugador == "papel" and eleccion_computadora == "piedra") or
+ (eleccion_jugador == "tijera" and eleccion_computadora == "papel")
+ ):
+ return f"{self.jugador.nombre} gana!"
+ else:
+ return f"{self.computadora.nombre} gana!"
+
+ def jugar(self):
+ eleccion_jugador = self.jugador.elegir()
+ eleccion_computadora = self.computadora.elegir()
+
+ print(f"{self.jugador.nombre} eligió: {eleccion_jugador}")
+ print(f"{self.computadora.nombre} eligió: {eleccion_computadora}")
+
+ resultado = self.determinar_ganador(eleccion_jugador, eleccion_computadora)
+ print(resultado)
+```
+
+---
+
+Por último, el archivo `main.py` contendrá la lógica para iniciar e interactuar con el juego
+
+Pero primero el juego necesita tener acceso a las clases `Jugador`, `Computadora` y `Juego` que están definidas en **otros archivos**
+
+Por lo que debemos **importar** los archivos correspondientes
+
+---
+#### Importación de módulos
+
+Python permite **importar** módulos para reutilizar código
+
+Podemos importar un **módulo completo** o solo **partes** específicas de él
+
+Para importar un módulo completo utilizamos la palabra clave `import` seguida del nombre del módulo
+
+```python
+import random
+```
+
+---
+
+Para importar **una parte** específica de un módulo como una función o clase
+
+Utilizamos la palabra clave `from` seguida del **módulo**
+
+Luego la palabra `import` seguido del nombre de la **función o clase**
+
+```python
+from random import choice
+```
+
+---
+
+Es **recomendable** importar solo lo que se necesita para evitar *conflictos de nombres* y mejorar la **legibilidad** del código
+
+> No es necesario importar todo el módulo,
+>
+> si solo se utiliza una parte de él
+
+---
+
+Cada archivo que hemos creado **puede ser importado** en Python
+
+Para importar las clases desde los archivos que hemos creado, utilizamos la sintaxis
+
+`from nombre_archivo import NombreClase`.
+
+---
+
+En el archivo `main.py`, importamos las clases de la siguiente manera
+
+```python
+# main.py
+from jugador import Jugador
+from computadora import Computadora
+from juego import Juego
+```
+
+Esto nos permite utilizar las clases `Jugador`, `Computadora` y `Juego` en el archivo `main.py`
+
+---
+
+El archivo `main.py` quedaría así
+
+```python
+# main.py
+from jugador import Jugador
+from computadora import Computadora
+from juego import Juego
+
+
+print("🎮 Bienvenido al juego de Piedra, Papel o Tijera")
+nombre = input("Introduce tu nombre: ")
+jugador = Jugador(nombre)
+computadora = Computadora()
+juego = Juego(jugador, computadora)
+
+while True:
+ print("\n--- Menú ---")
+ print("1. Jugar")
+ print("2. Salir")
+ opcion = input("Elige una opción: ")
+
+ if opcion == "1":
+ juego.jugar()
+ elif opcion == "2":
+ print("¡Gracias por jugar! 👋")
+ break
+ else:
+ print("Opción inválida, intenta de nuevo.")
+```
+
+---
+
+#### 3. Ejecutar el juego
+
+Después de organizar el código en **archivos separados**, probamos que todo funcione correctamente
+
+donde el archivo `main.py` es el **punto de entrada** del juego
+
+Para ejecutar, debemos estar en el directorio `juego` y ejecutar el archivo `main.py`
+
+
+---
+
+Ejecutamos el juego
+
+```bash
+cd juego # Si no estamos ya en la carpeta juego
+python main.py
+```
+
+```text
+🎮 Bienvenido al juego de Piedra, Papel o Tijera
+Introduce tu nombre: Jhon
+
+--- Menú ---
+1. Jugar
+2. Salir
+Elige una opción: 1
+Jhon, elige piedra, papel o tijera: tijera
+Jhon eligió: tijera
+Computadora eligió: piedra
+Computadora gana!
+
+--- Menú ---
+1. Jugar
+2. Salir
+Elige una opción: 1
+Jhon eligió: papel
+Computadora eligió: papel
+Empate
+
+--- Menú ---
+1. Jugar
+2. Salir
+Elige una opción: 2
+¡Gracias por jugar! 👋
+```
+
+---
+
+Refactorizamos el código del juego de Piedra, Papel o Tijera
+y lo **organizamos** en una estructura de archivos pero de *un sólo nivel*
+
+Podemos seguir mejorando la organización del código creando una **estructura de carpetas**
+para ordenar los archivos según su **funcionalidad**
+
+---
+#### Estructura de carpetas
+
+Organizaremos en carpetas para **agrupar** clases y lógica del juego
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+graph LR;
+ A["📁 juego"]
+ B["📁 clases"]
+ C["📁 logica"]
+ D["📄 jugador.py"]
+ E["📄 computadora.py"]
+ F["📄 juego.py"]
+ G["📄 main.py"]
+ H["📕 juego.md"]
+ A --> B
+ A --> C
+ B --> D
+ B --> E
+ C --> F
+ A --> G
+ A --> H
+```
+
+
+---
+
+#### 1. Crear las carpetas y los archivos
+
+**Crear** las carpetas `clases` y `logica` dentro de la carpeta `juego`
+
+```markdown
+Con `cd` entramos a la carpeta `juego`
+Con `mkdir` creamos las carpetas `clases` y `logica`
+```
+
+```bash
+cd juego
+mkdir clases logica
+```
+
+---
+
+**Mover** los archivos correspondientes a cada carpeta
+
+```markdown
+Con `mv` movemos los archivos `jugador.py` y `computadora.py`
+a la carpeta `clases`
+Con `mv` movemos el archivo `juego.py` a la carpeta `logica`
+```
+
+```bash
+mv jugador.py clases
+mv computadora.py clases
+mv juego.py logica
+```
+
+---
+
+**Quedará** la siguiente estructura de carpetas y archivos:
+
+```markdown
+Con `tree` vemos la estructura de carpetas y archivos
+```
+
+```bash
+tree
+```
+
+```bash
+.
+├── clases
+│ ├── computadora.py
+│ └── jugador.py
+├── logica
+│ └── juego.py
+├── juego.md
+└── main.py
+
+
+3 directories, 5 files
+```
+
+---
+
+#### 2. Actualizar las importaciones
+
+Hay que **actualizar** las importaciones en los archivos para reflejar la nueva estructura de carpetas
+
+En `main.py` **importar** las clases desde las nuevas ubicaciones
+
+Como se movió los archivos para importar ahora se utiliza la sintaxis
+
+`from carpeta.archivo import Clase`
+
+```python
+# main.py
+from clases.jugador import Jugador
+from clases.computadora import Computadora
+from logica.juego import Juego
+```
+
+---
+
+#### 3. Ejecutar el juego
+
+Actualizado las importaciones para reflejar la nueva estructura de carpetas
+
+**Ejecutamos** el juego de la misma manera que antes:
+
+```bash
+python main.py
+```
+
+---
+
+Podemos mejorar más la organización del código
+
+creando un archivo `__init__.py` en cada carpeta para convertir las *carpetas* en **paquetes** de Python
+
+---
+
+#### `__init__.py`
+
+El archivo `__init__.py` es un **archivo especial** en Python que indica que una carpeta debe ser tratada como un **paquete**
+
+Esto permite importar módulos desde esa carpeta de manera más sencilla
+
+Son **opcionales** pero es recomendable incluirlos para indicar que la carpeta es un paquete de Python
+
+---
+
+#### 1. Crear los archivos `__init__.py`
+
+**Crear** un archivo `__init__.py` vacío en cada carpeta para indicar que son paquetes
+
+```markdown
+Con `cd` entramos a la carpeta `juego`
+Con `touch` creamos los archivos `__init__.py`
+en las carpetas `clases` y `logica`
+```
+
+```bash
+cd juego
+touch clases/__init__.py logica/__init__.py
+```
+
+---
+
+La **estructura** de carpetas y archivos quedará así:
+
+```bash
+tree
+```
+
+```bash
+.
+├── clases
+│ ├── __init__.py
+│ ├── computadora.py
+│ └── jugador.py
+├── logica
+│ ├── __init__.py
+│ └── juego.py
+├── juego.md
+└── main.py
+
+3 directories, 7 files
+```
+
+---
+
+#### 2. Importar desde los paquetes
+
+En el archivo `__init__.py` añadimos los archivos que podremos importar directamente desde el paquete
+
+En `clases/__init__.py` añadimos:
+
+```python
+# clases/__init__.py
+from .jugador import Jugador
+from .computadora import Computadora
+```
+
+Al utilizar el punto `.` antes del **nombre del archivo**, indicando que importamos el archivo desde la **misma carpeta**
+
+---
+
+En `logica/__init__.py` añadimos:
+
+```python
+# logica/__init__.py
+from .juego import Juego
+```
+
+Asi cada paquete podrá **importar** sus clases correspondientes sin necesidad de especificar el archivo
+
+---
+
+#### 2. Importar desde los paquetes
+
+Para importar desde los paquetes en `main.py`, uilizamos el **nombre del paquete** seguido de la clase
+
+```python
+# main.py
+from clases import Jugador, Computadora
+from logica import Juego
+```
+
+Esto permite **importar directamente** desde los paquetes sin especificar el archivo donde se encuentra
+
+---
+
+#### 3. Ejecutar el juego
+
+**Organizamos** el código en **paquetes** y podemos ejecutar el juego de la misma manera que antes:
+
+```bash
+python main.py
+```
+
+---
+
+Después de **organizar el código**, podemos continuar **refactorizando**
+
+Ahora *eliminaremos* el código duplicado y dividir en funciones más pequeñas
+
+---
+
+#### Eliminación de código duplicado
+
+El código duplicado es un *problema* común en la programación que hace que el código sea más difícil de *mantener y entender*
+
+La eliminación de código duplicado implica **identificar y eliminar** partes del **código que se repiten** innecesariamente
+
+y reemplazarlas por funciones o métodos reutilizables
+
+---
+
+#### ¿Por qué eliminar el código duplicado?
+
+- **Mejora** la legibilidad del código
+- **Facilita** el mantenimiento y la evolución del software
+- **Reduce** el riesgo de errores al modificar el código
+
+---
+
+#### Refactorización del código duplicado
+
+En el Jugador podemos notar que al elegir piedra, papel o tijera,
+**se repite el código** para validar la elección del jugador
+
+```python [4-8]
+# original
+class Jugador:
+ def __init__(self, nombre):
+ self.nombre = nombre
+ def elegir(self):
+ eleccion = input(f"{self.nombre}, elige piedra, papel o tijera: ").lower()
+ while eleccion not in ["piedra", "papel", "tijera"]:
+ eleccion = input("Opción inválida. Intenta de nuevo: ").lower()
+ return eleccion
+```
+
+---
+
+**Refactorizamos** separando las opciones válidas en una constante y
+la validación de la elección en un método separado
+
+```python [1|6-9|11-14]
+OPCIONES = ["piedra", "papel", "tijera"]
+class Jugador:
+ def __init__(self, nombre):
+ self.nombre = nombre
+
+ def validar_eleccion(self, eleccion):
+ while eleccion not in OPCIONES:
+ eleccion = input("Opción inválida. Intenta de nuevo: ").lower()
+ return eleccion
+
+ def elegir(self):
+ opciones = ", ".join(OPCIONES)
+ eleccion = input(f"{self.nombre}, elige {opciones}: ").lower()
+ return self.validar_eleccion(eleccion)
+```
+
+---
+
+**Refactorizar** el `input` del jugador para que sea más claro y reutilizable.
+
+**Definir** un `__str__` para la clase Jugador
+
+```python [6-7|9-10|14,19]
+OPCIONES = ["piedra", "papel", "tijera"]
+class Jugador:
+ def __init__(self, nombre):
+ self.nombre = nombre
+
+ def __str__(self):
+ return self.nombre
+
+ def ingresar(self, mensaje):
+ return input(f"{self}, {mensaje}: ").lower()
+
+ def validar_eleccion(self, eleccion):
+ while eleccion not in OPCIONES:
+ eleccion = self.ingresar("opción inválida. Intenta de nuevo")
+ return eleccion
+
+ def elegir(self):
+ opciones = ", ".join(OPCIONES)
+ eleccion = self.ingresar(f"elige {opciones}")
+ return self.validar_eleccion(eleccion)
+```
+
+---
+
+En la clase Computadora, se puede hacer algo similar
+
+**Importar** sólo lo necesario del módulo `random` y no todo el módulo
+
+**Definir** un `__str__` para la clase Computadora
+
+```python [2,8]
+# original
+import random
+class Computadora:
+ def __init__(self):
+ self.nombre = "Computadora"
+
+ def elegir(self):
+ return random.choice(["piedra", "papel", "tijera"])
+```
+
+---
+
+**Importando** solo lo necesario del módulo `random`
+
+**Refactorizar** la clase Computadora, separando las opciones válidas en una constante y definiendo el método `__str__`
+
+
+```python [1|2,11| 7-8]
+from random import choice
+OPCIONES = ["piedra", "papel", "tijera"]
+class Computadora:
+ def __init__(self):
+ self.nombre = "Computadora"
+
+ def __str__(self):
+ return self.nombre
+
+ def elegir(self):
+ return choice(OPCIONES)
+```
+
+---
+
+**Identificar** el código duplicado en la clase `Juego` como la impresión de las elecciones de los jugadores y sus nombres
+
+
+```python [14-16,22-23]
+class Juego:
+ def __init__(self, jugador, computadora):
+ self.jugador = jugador
+ self.computadora = computadora
+
+ def determinar_ganador(self, eleccion_jugador, eleccion_computadora):
+ if eleccion_jugador == eleccion_computadora:
+ return "Empate"
+ elif (
+ (eleccion_jugador == "piedra" and eleccion_computadora == "tijera") or
+ (eleccion_jugador == "papel" and eleccion_computadora == "piedra") or
+ (eleccion_jugador == "tijera" and eleccion_computadora == "papel")
+ ):
+ return f"{self.jugador.nombre} gana!"
+ else:
+ return f"{self.computadora.nombre} gana!"
+
+ def jugar(self):
+ eleccion_jugador = self.jugador.elegir()
+ eleccion_computadora = self.computadora.elegir()
+
+ print(f"{self.jugador.nombre} eligió: {eleccion_jugador}")
+ print(f"{self.computadora.nombre} eligió: {eleccion_computadora}")
+
+ resultado = self.determinar_ganador(eleccion_jugador, eleccion_computadora)
+ print(resultado)
+```
+
+---
+
+**Refactorizar** el archivo `juego.py` usando el método `__str__` de las clases `Jugador` y `Computadora`
+
+**Adicionar** un método para mostrar las elecciones
+
+```python [14,15,17,18,23,24]
+class Juego:
+ def __init__(self, jugador, computadora):
+ self.jugador = jugador
+ self.computadora = computadora
+
+ def determinar_ganador(self, eleccion_jugador, eleccion_computadora):
+ if eleccion_jugador == eleccion_computadora:
+ return "Empate"
+ if (
+ (eleccion_jugador == "piedra" and eleccion_computadora == "tijera") or
+ (eleccion_jugador == "papel" and eleccion_computadora == "piedra") or
+ (eleccion_jugador == "tijera" and eleccion_computadora == "papel")
+ ):
+ return f"{self.jugador} gana!"
+ return f"{self.computadora} gana!"
+
+ def mostrar_elecciones(self, jugador, eleccion):
+ print(f"{jugador} eligió: {eleccion}")
+
+ def jugar(self):
+ eleccion_jugador = self.jugador.elegir()
+ eleccion_computadora = self.computadora.elegir()
+ self.mostrar_elecciones(self.jugador, eleccion_jugador)
+ self.mostrar_elecciones(self.computadora, eleccion_computadora)
+ resultado = self.determinar_ganador(eleccion_jugador, eleccion_computadora)
+ print(resultado)
+```
+
+---
+
+Se **eliminó** algunos duplicados y mejorado la legibilidad del código
+
+**Podemos** seguir mejorando y refactorizando el código y dividiendo
+en funciones más pequeñas y manejables
+
+---
+
+El objetivo es tener un **código limpio y fácil de mantener** y también es importante
+saber cuando detenerse, ya que la *refactorización excesiva* puede llevar a un código innecesariamente complejo
+
+---
+
+Al final **actualizar** el análisis y el diagrama de clases para reflejar los cambios realizados
+
+---
+
+#### Análisis actualizado
+
+```markdown
+Requisitos:
+- El jugador debe ingresar su nombre
+- El jugador debe elegir entre piedra, papel o tijera
+- La computadora debe elegir al azar entre piedra, papel o tijera
+- El juego debe determinar el ganador según las reglas del juego
+- El juego debe mostrar el resultado de cada partida
+- El juego debe permitir al jugador jugar varias partidas
+- El juego debe tener un menú para jugar o salir
+- El juego esta compuesto por ambos jugadores (Jugador y Computadora)
+
+Objetos:
+- Jugador
+- Computadora
+- Juego
+
+Características:
+- Jugador:
+ - nombre
+- Computadora:
+ - nombre
+- Juego:
+ - jugador
+ - computadora
+
+Acciones:
+- Jugador:
+ - elegir
+ - validar eleccion
+ - ingresar
+- Computadora:
+ - elegir
+- Juego:
+ - jugar
+ - determinar ganador
+ - mostrar elecciones
+```
+
+---
+
+#### Diagrama de clases actualizado
+
+````
+```mermaid
+classDiagram
+ class Jugador {
+ +nombre: string
+ +ingresar(mensaje)
+ +validar_eleccion(eleccion)
+ +elegir()
+ }
+ class Computadora {
+ +nombre: string
+ +elegir()
+ }
+ class Juego {
+ +jugador: Jugador
+ +computadora: Computadora
+ +determinar_ganador(eleccion_jugador, eleccion_cpu)
+ +mostrar_elecciones(jugador, eleccion)
+ +jugar()
+ }
+ Juego *-- Jugador
+ Juego *-- Computadora
+```
+````
+
+---
+
+#### Diagrama de clases actualizado
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+classDiagram
+ class Jugador {
+ +nombre: string
+ +ingresar(mensaje)
+ +validar_eleccion(eleccion)
+ +elegir()
+ }
+ class Computadora {
+ +nombre: string
+ +elegir()
+ }
+ class Juego {
+ +jugador: Jugador
+ +computadora: Computadora
+ +determinar_ganador(eleccion_jugador, eleccion_cpu)
+ +mostrar_elecciones(jugador, eleccion)
+ +jugar()
+ }
+ Juego *-- Jugador
+ Juego *-- Computadora
+```
+
+
+---
+
+Cambiar el nombre de `juego.md` a `README.md`
+será reconocido como la documentación principal
+
+```markdown
+Con `mv` cambiamos el nombre del archivo `juego.md` a `README.md`
+Con `tree` vemos la estructura de carpetas y archivos
+```
+
+```bash
+mv juego.md README.md
+tree
+```
+```bash
+.
+├── clases
+│ ├── __init__.py
+│ ├── computadora.py
+│ └── jugador.py
+├── logica
+│ ├── __init__.py
+│ └── juego.py
+├── README.md
+└── main.py
+3 directories, 7 files
+```
+
+---
+
+#### Dependencia circular
+
+
+
+Ocurre cuando dos o más módulos *dependen entre sí* directa o indirectamente
+
+---
+
+La dependencia circular es *mala*
+
+Esto puede llevar a problemas como:
+
+- *Dificultad* para entender y mantener el código
+- *Problemas de rendimiento* debido a la carga repetida de módulos
+- *Errores en tiempo de ejecución* si los módulos no se cargan en el orden correcto
+
+---
+
+Para evitar dependencias circulares, es recomendable:
+
+- **Reorganizar el código** para reducir las dependencias
+- **Dividir módulos grandes** en módulos más pequeños y manejables
+
+---
+
+Para definir el **punto de entrada** de un programa, utilizamos la variable especial `__name__` en Python
+
+---
+
+#### La variable `__name__`
+
+En Python, la variable especial `__name__` se utiliza para determinar si un módulo
+se está ejecutando como **programa principal** o si se está **importando** desde otro módulo
+
+---
+
+Cuando un módulo se **ejecuta directamente**, su variable `__name__` se establece en `"__main__"`
+
+Cuando *se importa* desde otro módulo, su variable `__name__` se establece en el nombre del módulo
+
+---
+
+Esto permite que el código dentro de un bloque
+
+`if __name__ == "__main__":`
+
+se ejecute cuando el módulo se **ejecuta directamente**, y NO cuando se *importa* desde otro módulo
+
+Es una buena práctica utilizar este bloque para definir el **punto de entrada** del programa
+
+---
+
+**Encapsular** la lógica principal del juego dentro de una función `main`
+
+**Agregar** al final del archivo `main.py` el bloque `if` para llamar a la función `main`
+
+```python [3|24-25]
+from clases import Jugador, Computadora
+from logica import Juego
+def main():
+ print("🎮 Bienvenido al juego de Piedra, Papel o Tijera")
+ nombre = input("Introduce tu nombre: ")
+ jugador = Jugador(nombre)
+ computadora = Computadora()
+ juego = Juego(jugador, computadora)
+
+ while True:
+ print("\n--- Menú ---")
+ print("1. Jugar")
+ print("2. Salir")
+ opcion = input("Elige una opción: ")
+
+ if opcion == "1":
+ juego.jugar()
+ elif opcion == "2":
+ print("¡Gracias por jugar! 👋")
+ break
+ else:
+ print("Opción inválida, intenta de nuevo.")
+
+if __name__ == "__main__":
+ main()
+```
+
+---
+
+Con esto se completó la **refactorización** y se puede los cambios al repositorio de GitHub
+
+```bash
+git add .
+git commit -m "Refactorización y organización del código"
+git push
+```
+
+---
+
+#### Ejemplo 02
+
+```text
+Al ejemplo anterior aún faltan algunas mejoras que hacer
+
+Elimina la duplicidad del código en OPCIONES almacenando
+en paquete que se llame `constantes` y crea un
+archivo `opciones.py` donde almacenes las opciones
+válidas del juego y reglas
+
+El juego debe funcionar con emojis: 🧱, 📄 y ✂️
+cambiando en las constantes y la lógica del juego
+
+Crea las reglas del juego en un diccionario
+para determinar el ganador
+```
+
+Realizar los cambios 4 minutos
+
+
+
+---
+
+#### 1. Estructura de carpetas y archivos
+
+```bash
+cd juego
+mkdir constantes
+touch constantes/__init__.py constantes/opciones.py
+tree
+```
+
+```bash
+.
+├── clases
+│ ├── __init__.py
+│ ├── computadora.py
+│ └── jugador.py
+├── constantes
+│ ├── __init__.py
+│ └── opciones.py
+├── logica
+│ ├── __init__.py
+│ └── juego.py
+├── README.md
+└── main.py
+4 directories, 8 files
+```
+
+---
+
+#### 2. Opciones válidas
+
+En `constantes/opciones.py` definir las opciones válidas del juego y las reglas
+
+```python
+# constantes/opciones.py
+PIEDRA = "piedra"
+PAPEL = "papel"
+TIJERA = "tijera"
+OPCIONES = [PIEDRA, PAPEL, TIJERA]
+REGLAS = {
+ PIEDRA: TIJERA,
+ PAPEL: PIEDRA,
+ TIJERA: PAPEL
+}
+```
+
+---
+
+En `constantes/__init__.py` importamos las opciones para que estén disponibles al importar el paquete `constantes`.
+
+```python
+# constantes/__init__.py
+from .opciones import OPCIONES, REGLAS
+```
+
+---
+
+#### 3. Actualizar el código
+
+Actualizar el código en los archivos `jugador.py`, `computadora.py` utilizando las constantes definidas
+
+```python
+# clases/jugador.py
+from constantes import OPCIONES
+...
+```
+
+```python
+# clases/computadora.py
+from constantes import OPCIONES
+from random import choice
+...
+```
+
+---
+
+En `juego.py` actualizar las importaciones y el método `determinar_ganador` para utilizar las constantes y reglas definidas
+
+```python [2|4-9]
+# logica/juego.py
+from constantes import REGLAS
+...
+ def determinar_ganador(self, eleccion_jugador, eleccion_computadora):
+ if eleccion_jugador == eleccion_computadora:
+ return "Empate"
+ if REGLAS[eleccion_jugador] == eleccion_computadora:
+ return f"{self.jugador} gana!"
+ return f"{self.computadora} gana!"
+```
+
+---
+
+Actualizar `main.py` para que muestre las opciones válidas al jugador
+
+```python [2,6]
+# main.py
+from constantes import OPCIONES
+from clases import Jugador, Computadora
+from logica import Juego
+def main():
+ print(f"🎮 Bienvenido al juego de {', '.join(OPCIONES)}")
+ nombre = input("Introduce tu nombre: ")
+ jugador = Jugador(nombre)
+ computadora = Computadora()
+ juego = Juego(jugador, computadora)
+...
+```
+
+---
+Reafactorizado el código para utilizar constantes y mejorar la flexibilidad del juego
+
+Ejecutar el juego para verificar que todo funciona correctamente:
+
+```bash
+python main.py
+```
+
+---
+
+Al final *modificar* las constantes para utilizar emojis en lugar de texto
+
+```python
+# constantes/opciones.py
+PIEDRA = "🧱"
+PAPEL = "📄"
+TIJERA = "✂️"
+```
+
+---
+
+Ejecutamos el juego para verificar que todo funciona correctamente:
+
+```bash
+python main.py
+```
+
+```text
+🎮 Bienvenido al juego de 🧱, 📄, ✂️
+Introduce tu nombre: jose
+
+--- Menú ---
+1. Jugar
+2. Salir
+Elige una opción: 1
+jose, elige 🧱, 📄, ✂️: 🧱
+jose eligió: 🧱
+Computadora eligió: 🧱
+Empate
+```
+
+---
+
+Subimos los avances de la sesión al repositorio en **GitHub**
+
+```bash
+git add .
+git commit -m "Sesión 11"
+git push origin main
+```
+
+---
+
+#### Resumen
+
+- La refactorización es el proceso de mejorar el código existente sin cambiar su funcionalidad externa.
+- Refactorizar mejora la legibilidad, facilita el mantenimiento y reduce la complejidad y el riesgo de errores.
+- La refactorización implica reorganizar el código en módulos y clases, eliminar código duplicado, renombrar variables y funciones, y dividir funciones grandes en funciones más pequeñas.
+
+---
+
+- En Python se recomienda organizar el código en archivos y carpetas para mejorar la estructura del proyecto.
+- Es buena práctica utilizar nombres descriptivos para archivos y carpetas, agrupar archivos relacionados y mantener una estructura coherente.
+- En Python se recomienda seguir la convención de nombres PEP 8 para clases, funciones, variables, constantes, archivos y módulos.
+
+---
+
+- La importación de módulos permite reutilizar código y que es recomendable importar solo lo necesario para evitar conflictos y mejorar la legibilidad.
+- Crear archivos `__init__.py` en las carpetas permite que sean tratados como paquetes de Python y facilita la importación de módulos.
+- Eliminar código duplicado mejora la legibilidad y facilita el mantenimiento del software.
+
+---
+
+- La dependencia circular entre módulos es mala porque dificulta el mantenimiento y puede causar errores de ejecución.
+- La variable especial `__name__` en Python permite determinar si un módulo se ejecuta directamente o se importa desde otro módulo.
+- Es buena práctica encapsular la lógica principal en una función y usar el bloque `if __name__ == "__main__":` para definir el punto de entrada del programa.
+
+---
+
+- El uso de constantes y la organización en paquetes mejora la flexibilidad y mantenibilidad del código.
+- La documentación y la estructura clara de carpetas y archivos son esenciales para proyectos bien organizados.
+
+---
+
+#### Retos
+
+Crear una carpeta con el nombre "retos_sesion_11" dentro del proyecto en la raíz, en la cual por cada ejercicio debes crear las siguientes carpetas y archivos:
+
+```bash
+# Estructura de carpetas
+psg-oop-2025/
+ retos_sesion_11/
+ ejercicio_01/
+ README.md
+
+ .py
+ main.py
+ ejercicio_02/
+ README.md
+
+ .py
+ main.py
+```
+
+---
+
+1. **Refactoriza** el siguiente ejemplo en una estructura de carpetas y archivos
+
+Un programa para gestionar una lista de tareas pendientes:
+
+- Permite agregar nuevas tareas
+- Eliminar tareas
+- Marcar tareas como completadas
+- Eliminar solo tareas completadas
+- Eliminar todas las tareas
+
+*(1/6)*
+
+---
+
+El programa debe ser **refactorizado**:
+
+- **Utilizar** convenciones de estilo PEP 8
+- **Mejorar** nombres en clases, variables y métodos
+- **Crear** una estructura ordenada de carpetas y módulos de Python
+- **Incluir** la documentación del análisis y diagrama en un archivo README.md
+
+*(2/6)*
+
+---
+
+
+Separar las clases en **archivos individuales** dentro de la carpeta `ejercicio_01/models`
+
+Implementar una organización de carpetas y archivos como la siguiente:
+
+```bash
+ejercicio_01/
+ ├── models/
+ │ ├── __init__.py
+ │ ├── tarea.py
+ │ └── lista_tareas.py
+ ├── main.py
+ └── README.md
+```
+
+Mantener la misma funcionalidad del programa original
+
+*(3/6)*
+
+---
+
+Diagrama de clases original
+
+```mermaid
+%%{init: {"theme": "dark", "look": "handDrawn" }}%%
+classDiagram
+direction LR
+ class Tarea {
+ +des
+ +comp
+ completada()
+ }
+ class ListaTareas {
+ +t
+ agregar(tarea)
+ ver()
+ completar(tarea)
+ eliminar(tarea)
+ eliminar_comp()
+ del_all()
+ }
+ Tarea --* ListaTareas
+```
+
+
+Debe incluirse el análisis y diagramas en `README.md`
+
+*(4/6)*
+
+---
+
+Código original
+
+```python
+class Tarea:
+ def __init__(self, d):
+ self.des = d
+ self.comp = 0
+ def completada(self):
+ self.comp = 1
+
+class ListaTareas:
+ def __init__(self):
+ self.t = []
+ def agregar(self, t):
+ self.t.append(Tarea(t))
+ def ver(self):
+ if not self.t:
+ print("No hay tareas pendientes.")
+ else:
+ for i, tarea in enumerate(self.t, 1):
+ estado = "✔️" if tarea.comp else " "
+ print(f"{i}. [{estado}] {tarea.des} ")
+ def completar(self, i):
+ if 0 < i <= len(self.t):
+ self.t[i-1].comp = 1
+ print(f"Tarea '{self.t[i-1].des}' marcada como completada.")
+ else:
+ print("Índice de tarea inválido.")
+ def eliminar(self, i):
+ if 0 < i <= len(self.t):
+ tarea_eliminada = self.t.pop(i-1)
+ print(f"Tarea '{tarea_eliminada.des}' eliminada.")
+ else:
+ print("Índice de tarea inválido.")
+ def eliminar_comp(self):
+ self.t = [tarea for tarea in self.t if not tarea.comp]
+ print("Tareas completadas eliminadas.")
+ def del_all(self):
+ self.t.clear()
+ print("Todas las tareas eliminadas.")
+lista = ListaTareas()
+while True:
+ print("\n--- Menú ---")
+ print("1. Agregar tarea")
+ print("2. Ver tareas")
+ print("3. Completar tarea")
+ print("4. Eliminar tarea")
+ print("5. Eliminar tareas completadas")
+ print("6. Eliminar todas las tareas")
+ print("7. Salir")
+ opcion = input("Elige una opción: ")
+
+ if opcion == "1":
+ tarea = input("Ingrese la descripción de la tarea: ")
+ lista.agregar(tarea)
+ elif opcion == "2":
+ lista.ver()
+ elif opcion == "3":
+ lista.ver()
+ i = int(input("Ingrese el índice de la tarea a completar: "))
+ lista.completar(i)
+ elif opcion == "4":
+ lista.ver()
+ i = int(input("Ingrese el índice de la tarea a eliminar: "))
+ lista.eliminar(i)
+ elif opcion == "5":
+ lista.eliminar_comp()
+ elif opcion == "6":
+ lista.del_all()
+ elif opcion == "7":
+ print("¡Gracias por usar la aplicación! 👋")
+ break
+ else:
+ print("Opción inválida, intenta de nuevo.")
+```
+
+Debe refactorizarse y organizarse en archivos y carpetas en la carpeta `ejercicio_01`
+
+*(5/6)*
+
+---
+
+- Realiza el **análisis** y **diagrama de clases** para el programa en `README.md` dentro de la carpeta `ejercicio_01`
+- Refactoriza el código manteniendo la misma funcionalidad
+- Escribe el código en archivos separados y organiza en carpetas en `ejercicio_01`
+- Utiliza **nombres descriptivos** y sigue las buenas prácticas de **PEP 8**
+- Prueba que el programa funcione correctamente
+
+*(6/6)*
+
+---
+
+2. La Biblioteca Municipal iniciará la digitalización de su registro de préstamos
+Actualmente, todos los datos se anotan manualmente en un cuaderno:
+
+Información de **libros**: título, autor e ISBN
+Información de **usuarios**: nombre
+
+El objetivo es digitalizar el flujo de préstamos de libros
+
+*(1/5)*
+
+---
+
+El flujo de préstamos es el **siguiente**:
+
+1. Un usuario ingresa su nombre
+2. Se lista los libros
+3. El usuario elige un libro para prestar
+4. Se registra la lista de prestados con el nombre del usuario y el libro prestado
+5. Puede prestarse más de un libro al mismo usuario
+6. Deben devolver todos los libros a la vez
+7. Se puede ver la lista de libros prestados y los usuarios que los tienen
+8. Para salir se debe ingresar "salir"
+
+*(2/5)*
+
+---
+
+- **Crear** clases para representar `Libro`, `Usuario`
+- **Organizar** el código en una estructura ordenada de carpetas y módulos de Python
+- **Incluir** la documentación del análisis y diagrama en un archivo `README.md`
+- **Mantener** la funcionalidad descrita en el flujo de préstamos
+- **Utilizar** nombres descriptivos para clases, variables y métodos
+
+*(3/5)*
+
+---
+
+La estructura de carpetas y archivos debe ser similar a la siguiente:
+
+```bash
+ejercicio_02/
+ ├── modelos/
+ │ ├── __init__.py
+ │ ├── libro.py
+ │ └── usuario.py
+ ├── logica/
+ │ │── __init__.py
+ │ └── biblioteca.py
+ ├── main.py
+ └── README.md
+```
+
+*(4/5)*
+
+---
+
+- Realizar el análisis y diagrama de clases para el programa en `README.md` dentro de la carpeta `ejercicio_02`
+- Refactorizar el código manteniendo la misma funcionalidad
+- Escribir el código en archivos separados y organizar en carpetas en `ejercicio_02`
+- Utilizar nombres descriptivos y seguir las buenas prácticas de PEP 8
+- Probar que el programa funcione correctamente
+
+*(5/5)*
+
+---
+
+
+
+
+
+
+
+
+
+[ ](https://github.com/python-la-paz/python-study-group-oop/tree/main/content/sesion11)
+
+Repositorio de la Sesión
+
+---
+
+## Bibliografía y Referencias
+
+- [Best Practices for Refactoring Code](https://www.freecodecamp.org/news/best-practices-for-refactoring-code/)
+- [Python Refactoring](https://realpython.com/python-refactoring/)
+- [Guía PEP 8](https://peps.python.org/pep-0008/#class-names)
+- [Mermaid Charts](https://www.mermaidchart.com/play)
+- [Draw.io](https://app.diagrams.net/)
+- [Python 3 Object-oriented Programming, Second Edition, Dusty PhillipsDusty Phillips](https://github.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition)
+- [Objetos en programación](https://ebac.mx/blog/objeto-en-programacion)
+- [Enfoque orientado a objetos](https://1library.co/article/enfoque-orientado-a-objetos-base-te%C3%B3rica.qvld461y)
+- [OOAD](https://www.tutorialspoint.com/object_oriented_analysis_design/ooad_object_oriented_analysis.htm)
diff --git a/content/sesion11/img/dependencia_circular.jpeg b/content/sesion11/img/dependencia_circular.jpeg
new file mode 100644
index 0000000..4f3d683
Binary files /dev/null and b/content/sesion11/img/dependencia_circular.jpeg differ
diff --git a/content/sesion11/img/folders.png b/content/sesion11/img/folders.png
new file mode 100644
index 0000000..d4a16a2
Binary files /dev/null and b/content/sesion11/img/folders.png differ
diff --git a/content/sesion11/index.html b/content/sesion11/index.html
new file mode 100644
index 0000000..92fbc15
--- /dev/null
+++ b/content/sesion11/index.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ Sesión 11 | Python Study Group | OOP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/content/sesion12/Readme.md b/content/sesion12/Readme.md
new file mode 100644
index 0000000..af674b9
--- /dev/null
+++ b/content/sesion12/Readme.md
@@ -0,0 +1,1565 @@
+
+
+
+
+
+
+
+### Sesión 12
+#### Buenas prácticas
+#### Anotaciones, Documentación y Principios
+
+---
+
+### Antes de empezar
+
+---
+
+#### Proyecto en VSCode
+
+Abre el proyecto en VSCode
+
+```bash
+code psg-oop-2025
+```
+
+Crea una carpeta con el nombre `sesion12`
+
+```bash
+mkdir sesion12
+cd sesion12
+```
+
+- Los archivos de esta sesión deben estar dentro de esta carpeta
+
+- Al finalizar la sesión, sube los cambios al repositorio en un commit
+
+---
+
+**Python** es uno de los lenguajes más populares y entre sus características
+se encuentra la **facilidad** para escribir código
+
+El **tipado dinámico** de Python permite a los desarrolladores escribir código
+rápido y flexible
+
+---
+
+Pero esto también puede llevar a *errores* difíciles de detectar si no se tiene *cuidado*
+
+---
+
+Una **variable** puede **cambiar de tipo** en cualquier momento, lo que puede causar *errores* en tiempo de ejecución
+
+Desde la terminal interactiva de Python
+
+```python
+def sumar(a, b):
+ return a + b
+x = 5 # x es un entero
+y = 10 # y es un entero
+print(sumar(x, y)) # Funciona correctamente
+x = "5" # x ahora es una cadena
+print(sumar(x, y)) # Error en tiempo de ejecución: TypeError
+```
+
+```text
+15
+ERROR!
+Traceback (most recent call last):
+ File "", line 4, in
+ File "", line 2, in sumar
+TypeError: unsupported operand type(s) for +: 'int' and 'str'
+```
+
+---
+
+Este tipo de *errores* puede ser difícil de *detectar y corregir*, especialmente en proyectos grandes
+
+Para lo cual, en **Python** se ha introducido el concepto de **anotaciones de tipo**
+
+---
+
+#### Anotaciones
+
+Python desde su versión **3.6** introdujo el concepto de **anotaciones de tipo**
+que permiten a los desarrolladores especificar el tipo de datos esperado
+para
+
+- Variables
+- Parámetros de funciones
+- Valores de retorno de funciones
+- Atributos de clases
+
+---
+
+Esto **ayuda** a mejorar la **legibilidad** del código y a **detectar errores**
+antes de que se ejecuten
+
+Además, los **editores de código** y herramientas de análisis estático
+pueden utilizar estas anotaciones para proporcionar **sugerencias** y *advertencias*
+
+
+
+---
+
+Crearemos un archivo llamado `anotaciones.py` en la carpeta `sesion12`
+
+```bash
+touch anotaciones.py
+```
+
+---
+
+#### Anotaciones en variables
+
+Las anotaciones de tipo en **variables** se utilizan para indicar el tipo de dato
+que se espera que contenga una variable
+
+```python
+edad: int = 30
+nombre: str = "Jhon"
+altura: float = 1.75
+activo: bool = True
+print(type(edad)) #
+print(type(nombre)) #
+print(type(altura)) #
+print(type(activo)) #
+```
+
+Estas **anotaciones** no afectan el comportamiento del código,
+pero proporcionan **información adicional** para los desarrolladores
+
+---
+
+De esta manera, podemos ver que
+
+- `edad` es un **entero**
+- `nombre` es una **cadena**
+- `altura` es un **flotante**
+- `activo` es un **booleano**
+
+Esto ayuda a los desarrolladores a entender mejor el **propósito** de cada variable
+
+---
+
+También se pueden utilizar anotaciones de tipo para **estructuras** de datos donde
+se incluyen los **tipos de los elementos** que contienen
+
+En el caso de listas se utiliza la sintaxis
+
+`list[tipo]`
+
+- `list` es la estructura de datos
+- `tipo` es el tipo de los elementos que contiene la lista
+
+---
+
+En el caso de diccionarios se utiliza la sintaxis
+
+`dict[clave_tipo, valor_tipo]`
+
+- `dict` es la estructura de datos
+- `clave_tipo` es el tipo de las claves
+- `valor_tipo` es el tipo de los valores
+
+---
+
+En el caso de tuplas se utiliza la sintaxis
+
+`tuple[tipo1, tipo2, ...]`
+
+- `tuple` es la estructura de datos
+- `tipo1`, `tipo2`, `...` Son los tipos de los elementos que contiene la tupla
+
+
+---
+
+Ejemplos de anotaciones en estructuras de datos
+
+```python
+numeros: list[int] = []
+nombres: dict[str, str] = {}
+tuplas: tuple[int, str] = ()
+print(type(numeros)) #
+print(type(nombres)) #
+print(type(tuplas)) #
+```
+
+- `numeros`: lista de enteros
+- `nombres`: diccionario con claves y valores de tipo cadena
+- `tuplas` : tuplas de enteros y cadenas
+
+---
+
+*Sin* anotaciones
+
+
+
+**Con** anotaciones
+
+
+
+---
+
+#### Anotaciones en funciones
+
+Se utilizan para indicar los tipos de datos que se espera que contengan los **parámetros**
+y el tipo de dato que se espera que **retorne**
+
+Los parámetros de una función se anotan utilizando la sintaxis
+
+`parametro: tipo`
+
+y el tipo de retorno se anota utilizando la sintaxis
+
+`-> tipo`
+
+El tipo de retorno se coloca antes de los dos puntos `:`
+
+---
+
+Por ejemplo, si tenemos una función que suma **dos números enteros** y retorna un **entero**
+
+```python
+def sumar(a: int, b: int) -> int:
+ return a + b
+```
+
+- La función `sumar` espera dos parámetros
+- `a` y `b` de tipo entero
+- Retorna un valor de tipo entero
+
+---
+
+*Sin* anotaciones
+
+
+
+**Con** anotaciones
+
+
+
+---
+
+Se pueden utilizar anotaciones de tipo para funciones que aceptan
+parámetros de **estructuras** de datos
+
+Por ejemplo, si tenemos una función que recibe una **lista de enteros** y
+retorna una **lista de enteros** que son los cuadrados de los números
+
+```python
+def cuadrados(numeros: list[int]) -> list[int]:
+ return [n ** 2 for n in numeros]
+numeros = cuadrados([2,4,6])
+print (numeros)
+```
+
+- La función `cuadrados` espera un parámetro
+- `numeros` que es una lista de enteros
+- Retorna una lista de enteros
+
+---
+
+Las anotaciones nos ayudan a saber que la variable `numeros` que almacena el resultado de la función
+es una **lista de enteros**
+
+
+
+---
+
+#### Anotaciones en clases
+
+Las anotaciones de tipo también se pueden utilizar en clases para indicar
+los tipos de datos de los **atributos** de una clase
+
+Por ejemplo, si tenemos una clase `Persona` con atributos de clase `especie`
+y atributos de instancia `nombre` y `edad`
+
+---
+
+Ejemplo de anotaciones en clases
+
+```python
+class Persona:
+ especie: str = "Homo sapiens"
+ def __init__(self, nombre: str, edad: int):
+ self.nombre: str = nombre
+ self.edad: int = edad
+```
+
+- Clase `Persona` tiene un atributos
+- Atributo `especie` es de tipo cadena con valor por defecto `"Homo sapiens"`
+- Atributo `nombre` de tipo cadena
+- Atributo `edad` de tipo entero
+
+---
+
+**Con** anotaciones en atributos y en el constructor al **instanciar** la clase
+muestra el editor los tipos de datos que necesita la clase
+
+
+
+---
+
+Existe otro estilo de anotaciones usado mayormente en versiones anteriores a *Python 3.8*
+
+importando el módulo `typing` y las clases `List`, `Dict`, `Tuple`, etc.
+
+---
+
+Si tenemos una función que recibe una **lista de enteros** y retorna una **lista de enteros**
+
+```python
+from typing import List
+def cuadrados(numeros: List[int]) -> List[int]:
+ return [n ** 2 for n in numeros]
+numeros = cuadrados([2, 4, 6])
+print(numeros)
+```
+
+Actualmente, este estilo es menos común y se recomienda utilizar las anotaciones
+directamente sin importar el módulo `typing`
+
+---
+
+Es importante recordar que las anotaciones de tipo en Python **son opcionales**
+y **NO afectan el comportamiento** del código en tiempo de ejecución
+
+---
+
+Pero no es suficiente para escribir código limpio y mantenible
+
+También es importante **documentar el código**
+para que se pueda entender fácilmente su propósito y funcionamiento
+
+---
+
+#### Documentación
+
+La documentación es una **parte esencial** del desarrollo de software
+ya que ayuda a los desarrolladores a entender el **propósito y funcionamiento** del código
+
+En las **clases** podemos utilizar cadenas de documentación *docstrings*
+para documentar el propósito de la clase, sus atributos y métodos.
+
+---
+
+Se coloca entre comillas triples `"""` justo después de la definición de la clase
+o del método
+
+Los **editores de código** y herramientas de análisis estático utilizan
+estas cadenas de documentación para mostrar **sugerencias** y *advertencias* sobre el uso del código
+
+---
+Crearemos un archivo llamado `documentacion.py` en la carpeta `sesion12`
+
+```bash
+touch documentacion.py
+```
+
+---
+
+#### Documentación en clases
+
+Por ejemplo, Una clase `Persona` con un atributo `nombre` y un método `saludar`
+
+*Sin* documentación
+
+```python
+class Persona:
+ def __init__(self, nombre: str):
+ self.nombre: str = nombre
+ def saludar(self):
+ print(f"Hola, mi nombre es {self.nombre}")
+jhon = Persona("Jhon")
+jhon.saludar()
+```
+
+
+
+
+
+---
+
+**Con** documentación
+
+```python
+class Persona:
+ """Clase que representa a una persona."""
+ def __init__(self, nombre: str):
+ """Inicializa una nueva instancia de la clase Persona.
+
+ Args:
+ nombre (str): El nombre de la persona.
+ """
+ self.nombre: str = nombre
+
+ def saludar(self):
+ """Imprime un saludo con el nombre de la persona."""
+ print(f"Hola, mi nombre es {self.nombre}")
+jhon = Persona("Jhon")
+jhon.saludar()
+```
+
+---
+
+
+
+
+
+---
+
+Existen diferentes convenciones para escribir **documentación en Python**
+la [PEP 257](https://peps.python.org/pep-0257/) establece la base para las cadenas de documentación
+
+Existen diferentes estilos de documentación
+
+- Google
+- NumPy
+- reStructuredText (reST)
+
+Cada uno tiene su propia **sintaxis y convenciones**
+
+---
+
+Usos **típicos** de cada estilo
+
+| Estilo | Uso típico |
+| ----------------------- | -------------------------------------------- |
+| Google | Documentación de proyectos y bibliotecas |
+| NumPy | Documentación científica y técnica |
+| reStructuredText (reST) | Documentación profesional, grandes proyectos |
+
+
+---
+
+**Comparación** entre estilos
+
+| Estilo | Ventajas | Desventajas |
+| ------ | --------------------------- | ------------------------------------------------ |
+| Google | Fácil de leer y escribir | Menos detallado para proyectos complejos |
+| NumPy | Estructurado y detallado | Puede ser más difícil de leer para principiantes |
+| reST | Muy detallado y profesional | Más complejo de escribir menos amigable |
+
+---
+
+**Legibilidad** de cada estilo
+
+| Estilo | Legibilidad |
+| ------ | --------------------------------------------------------------------------------- |
+| Google | Claro y conciso, fácil para humanos |
+| NumPy | Muy estructurado, detallado, generado por herramientas |
+| reST | Muy detallado, profesional, orientado a herramientas de documentación como Sphinx |
+
+
+---
+
+#### Estilo Google
+
+Se utiliza la siguiente sintaxis para documentar
+
+```python [2-17]
+def funcion(parametro1: tipo, parametro2: tipo) -> tipo:
+ """Descripción breve de la función
+
+ Args:
+ parametro1 (tipo): Descripción del primer parámetro
+ parametro2 (tipo): Descripción del segundo parámetro
+
+ Returns:
+ tipo: Descripción del valor de retorno
+
+ Raises:
+ TipoDeError: Condiciones que causan este error
+
+ Examples:
+ >>> funcion(valor1, valor2)
+ resultado_esperado
+ """
+ pass
+```
+
+
+---
+
+Crea el archivo `estilo_google.py` en la carpeta `sesion12`
+
+Documenta la siguiente función que devuelve la secuencia de Fibonacci hasta el n-ésimo número
+usando el estilo Google
+
+```python
+def fibonacci(n: int) -> str:
+ if n < 0:
+ raise ValueError("El índice no puede ser negativo")
+ fib = [0, 1]
+ for i in range(2, n):
+ fib.append(fib[i - 1] + fib[i - 2])
+ return ', '.join(str(x) for x in fib[:n])
+print (fibonacci(5))
+```
+
+---
+
+Documentación estilo Google
+
+```python [2|4-5|7-8|10-11|13-16]
+def fibonacci(n: int) -> str:
+ """Devuelve la secuencia de Fibonacci hasta el n-ésimo número.
+
+ Args:
+ n (int): El índice del número de Fibonacci a calcular.
+
+ Returns:
+ str: La secuencia de Fibonacci hasta el n-ésimo número.
+
+ Raises:
+ ValueError: Si n es negativo.
+
+ Examples:
+ >>> fibonacci(5)
+ '0, 1, 1, 2, 3'
+ """
+ if n < 0:
+ raise ValueError("El índice no puede ser negativo")
+ fib = [0, 1]
+ for i in range(2, n):
+ fib.append(fib[i - 1] + fib[i - 2])
+ return ', '.join(str(x) for x in fib[:n])
+print (fibonacci(5))
+```
+
+---
+El editor de código **interpreta la documentación** y proporcionar sugerencias de autocompletado
+
+
+
+
+---
+
+#### Estilo NumPy
+
+Se utiliza la siguiente sintaxis para documentar
+
+```python
+def funcion(parametro1: tipo, parametro2: tipo) -> tipo:
+ """
+ Descripción breve de la función.
+
+ Parameters
+ ----------
+ parametro1 : tipo
+ Descripción del primer parámetro.
+ parametro2 : tipo
+ Descripción del segundo parámetro.
+
+ Returns
+ -------
+ tipo
+ Descripción del valor de retorno.
+
+ Raises
+ ------
+ TipoDeError
+ Condiciones que causan este error.
+
+ Examples
+ --------
+ >>> funcion(valor1, valor2)
+ resultado_esperado
+ """
+ pass
+```
+
+---
+
+Crea el archivo `estilo_numpy.py` en la carpeta `sesion12`
+
+Documenta la siguiente función que devuelve la secuencia de Fibonacci hasta el n-ésimo número
+
+```python
+def fibonacci(n: int) -> str:
+ if n < 0:
+ raise ValueError("El índice no puede ser negativo")
+ fib = [0, 1]
+ for i in range(2, n):
+ fib.append(fib[i - 1] + fib[i - 2])
+ return ', '.join(str(x) for x in fib[:n])
+print (fibonacci(5))
+```
+
+---
+
+Documentación estilo NumPy
+
+```python [3|5-8|10-13|15-18|20-23]
+def fibonacci(n: int) -> str:
+ """
+ Devuelve la secuencia de Fibonacci hasta el n-ésimo número.
+
+ Parameters
+ ----------
+ n : int
+ El índice del número de Fibonacci a calcular.
+
+ Returns
+ -------
+ str
+ La secuencia de Fibonacci hasta el n-ésimo número.
+
+ Raises
+ ------
+ ValueError
+ Si n es negativo.
+
+ Examples
+ --------
+ >>> fibonacci(5)
+ '0, 1, 1, 2, 3'
+ """
+ if n < 0:
+ raise ValueError("El índice no puede ser negativo")
+ fib = [0, 1]
+ for i in range(2, n):
+ fib.append(fib[i - 1] + fib[i - 2])
+ return ', '.join(str(x) for x in fib[:n])
+
+print(fibonacci(5))
+```
+
+---
+El editor de código **interpretar la documentación** y proporcionar sugerencias de autocompletado
+
+
+
+---
+
+#### Estilo reStructuredText (reST)
+
+Se utiliza la siguiente sintaxis para documentar
+
+```python
+def funcion(parametro1: tipo, parametro2: tipo) -> tipo:
+ """
+ Descripción breve de la función.
+
+ :param parametro1: Descripción del primer parámetro.
+ :type parametro1: tipo
+ :param parametro2: Descripción del segundo parámetro.
+ :type parametro2: tipo
+ :return: Descripción del valor de retorno.
+ :rtype: tipo
+ :raises TipoDeError: Condiciones que causan este error.
+ :example:
+ >>> funcion(valor1, valor2)
+ resultado_esperado
+ """
+ pass
+```
+
+---
+
+Crea el archivo `estilo_rest.py` en la carpeta `sesion12`
+
+Documenta la siguiente función que devuelve la secuencia de Fibonacci hasta el n-ésimo número
+
+```python
+def fibonacci(n: int) -> str:
+ if n < 0:
+ raise ValueError("El índice no puede ser negativo")
+ fib = [0, 1]
+ for i in range(2, n):
+ fib.append(fib[i - 1] + fib[i - 2])
+ return ', '.join(str(x) for x in fib[:n])
+print (fibonacci(5))
+```
+
+---
+
+Documentación estilo reST
+
+```python [3|5-6|7-8|9|10-12]
+def fibonacci(n: int) -> str:
+ """
+ Devuelve la secuencia de Fibonacci hasta el n-ésimo número.
+
+ :param n: El índice del número de Fibonacci a calcular.
+ :type n: int
+ :return: La secuencia de Fibonacci hasta el n-ésimo número.
+ :rtype: str
+ :raises ValueError: Si n es negativo.
+ :example:
+ >>> fibonacci(5)
+ '0, 1, 1, 2, 3'
+ """
+ if n < 0:
+ raise ValueError("El índice no puede ser negativo")
+ fib = [0, 1]
+ for i in range(2, n):
+ fib.append(fib[i - 1] + fib[i - 2])
+ return ', '.join(str(x) for x in fib[:n])
+
+print(fibonacci(5))
+```
+
+---
+
+El editor de código **interpretar la documentación** y proporcionar sugerencias de autocompletado
+
+
+
+---
+
+Todos los estilos son **válidos** y tienen sus propias ventajas y desventajas
+
+
+
+
+
+---
+
+**Elige** un estilo de documentación y sé **consistente** en su uso a lo largo de tu código
+
+Esto **facilitará** la lectura y comprensión del código para ti y para otros desarrolladores
+
+---
+
+Añadir anotaciones de tipo y documentación a tu código es una **buena práctica**
+
+Existen otras buenas prácticas como los **principios de desarrollo**
+
+que ayudan a escribir código elegante y mantenible
+
+---
+
+#### Principios de desarrollo
+
+Los principios de desarrollo son un conjunto de **buenas prácticas**
+que ayudan a los desarrolladores a escribir código limpio, mantenible y escalable
+
+Al igual que los *patrones de diseño*, los principios de desarrollo
+proporcionan soluciones a problemas comunes en el desarrollo de software
+
+---
+
+Algunos de los principios más importantes son:
+
+- **KISS** (Keep It Simple, Stpid)
+- **DRY** (Don't Repeat Yourself)
+- **YAGNI** (You Aren't Gonna Need It)
+
+---
+
+#### KISS (Keep It Simple, St*pid)
+
+El principio KISS enfatiza la importancia de mantener **el código simple** y fácil de entender
+
+Un código simple es **más fácil** de leer, mantener y depurar
+
+---
+
+Se resume en
+
+- **Simplicidad**: Las soluciones deben ser lo más simples posible evitando características innecesarias
+- **Claridad**: El código debe ser claro y fácil de entender
+
+---
+
+- **Minimalismo**: Evitar la complejidad innecesaria y centrarse en lo esencial
+- **Eficiencia**: Las soluciones simples son más eficientes
+- *Evite la sobreingeniería*: No agregue características o funcionalidades innecesarias
+
+
+---
+
+Una forma de aplicarlo es
+
+- **Dividir** problemas complejos en partes más pequeñas y manejables
+- *Evitar* la sobreingeniería y **centrarse** en lo esencial
+- **Utilizar** nombres claros y descriptivos para variables, funciones y clases
+
+---
+
+#### Ejemplo KISS
+
+Crear el archivo `kiss.py` en la carpeta `sesion12`
+
+```text
+La primera calculadora de un estudiante sólo sabe
+sumar dos números enteros, tiene una clase SumaDeNumeros
+con el método operar que realiza la suma.
+Sin embargo, no es muy claro, ni simple.
+Simplifica el código aplicando el principio KISS
+```
+
+```python [1-11|13-18]
+class SumaDeNumeros:
+ def __init__(self, a, b):
+ self.a = a
+ self.b = b
+
+ def operar(self):
+ return self.a + self.b
+
+suma = SumaDeNumeros(5, 7)
+print(suma.operar())
+```
+
+---
+
+Solución simple
+
+```python
+class Calculadora:
+ def sumar(self, a, b):
+ return a + b
+calc = Calculadora()
+print(calc.sumar(5, 7))
+```
+
+---
+
+Solución con anotaciones y documentación
+
+```python
+class Calculadora:
+ """Clase que representa una calculadora simple."""
+
+ def sumar(self, a: int, b: int) -> int:
+ """Suma dos números enteros.
+
+ Args:
+ a (int): El primer número.
+ b (int): El segundo número.
+
+ Returns:
+ int: La suma de los dos números.
+ """
+ return a + b
+calc = Calculadora()
+print(calc.sumar(5, 7))
+```
+
+---
+
+#### Ejercicio para ti (01)
+
+```text
+Una calculadora de areas que pueda calcular
+el área de rectángulos y cuadrados.
+Donde el código tiene mucha complejidad
+Simplifica el código aplicando el principio KISS
+Después de simplificar, agrega anotaciones y documentación
+```
+
+Crea el archivo `areas.py` en la carpeta `sesion12`
+
+---
+
+Realizar los cambios 4 minutos
+
+```python
+class Area:
+ def __init__(self, tipo, lado1, lado2=None):
+ self.tipo = tipo
+ self.lado1 = lado1
+ self.lado2 = lado2
+ def calcular(self):
+ if self.tipo == "rectangulo":
+ return self.lado1 * self.lado2
+ elif self.tipo == "cuadrado":
+ return self.lado1 ** 2
+area_rectangulo = Area("rectangulo", 5, 10)
+print(area_rectangulo.calcular())
+area_cuadrado = Area("cuadrado", 5)
+print(area_cuadrado.calcular())
+```
+
+
+
+---
+
+Solución simple
+
+```python
+class Calculadora:
+ def area(self, lado1, lado2):
+ return lado1 * lado2
+calculadora = Calculadora()
+print(calculadora.area(5, 10)) # Área del rectángulo
+print(calculadora.area(5, 5)) # Área del cuadrado
+```
+
+```bash
+python areas.py
+```
+
+```text
+50
+25
+```
+
+---
+
+Solución con anotaciones y documentación
+
+```python
+class Calculadora:
+ """Clase que representa una calculadora simple."""
+
+ def area(self, lado1: int, lado2: int) -> int:
+ """Calcula el área de un rectángulo o cuadrado.
+
+ Args:
+ lado1 (int): El primer lado.
+ lado2 (int): El segundo lado.
+
+ Returns:
+ int: El área calculada.
+ """
+ return lado1 * lado2
+calculadora = Calculadora()
+print(calculadora.area(5, 10)) # Área del rectángulo
+print(calculadora.area(5, 5)) # Área del cuadrado
+```
+
+
+---
+
+#### DRY (Don't Repeat Yourself)
+
+El principio DRY enfatiza la importancia de **evitar la duplicación** de código
+
+El código *duplicado* puede llevar a errores y dificultades en el mantenimiento
+
+---
+
+Se resume en
+
+- **Unicidad**: Cada pieza de conocimiento o lógica debe tener una única representación en el código
+- **Mantenimiento**: El código duplicado es más difícil de mantener y actualizar
+- **Reutilización**: Fomenta la reutilización de código a través de funciones, clases y módulos
+
+---
+
+- **Reducción de errores**: Menos duplicación significa menos oportunidades para errores
+- **Eficiencia**: Un código sin duplicación es más eficiente
+- **Claridad**: Un código sin duplicación es más claro y fácil de entender
+
+
+---
+
+Una forma de aplicarlo es
+
+- **Identificar** y eliminar código duplicado
+- **Utilizar** funciones, clases y módulos para reutilizar código
+- **Mantener** el código DRY durante el desarrollo y la refactorización
+
+---
+
+#### Ejemplo DRY
+
+Crear el archivo `dry.py` en la carpeta `sesion12`
+
+```text
+Calcular el área de un rectángulo y un cuadrado,
+pero el código tiene duplicación
+Utiliza el principio DRY para eliminar la duplicación
+Unifica el código para que el cálculo del área
+sea realizado por una sola función
+```
+
+
+```python [1-7|9-13]
+# Código duplicado
+def area_rectangulo(lado1, lado2):
+ return lado1 * lado2
+def area_cuadrado(lado):
+ return lado * lado
+print(area_rectangulo(5, 10))
+print(area_cuadrado(5))
+```
+
+---
+
+Código sin duplicación
+
+```python
+
+# Código sin duplicación
+def area(lado1, lado2):
+ return lado1 * lado2
+print(area(5, 10)) # Área del rectángulo
+print(area(5, 5)) # Área del cuadrado
+```
+
+---
+
+Código sin duplicación con anotaciones y documentación
+
+```python
+def area(lado1: int, lado2: int) -> int:
+ """Calcula el área de un rectángulo o cuadrado.
+
+ Args:
+ lado1 (int): El primer lado.
+ lado2 (int): El segundo lado.
+
+ Returns:
+ int: El área calculada.
+ """
+ return lado1 * lado2
+print(area(5, 10)) # Área del rectángulo
+print(area(5, 5)) # Área del cuadrado
+```
+
+---
+
+#### Ejercicio para ti (02)
+
+```text
+Una tienda en línea permite pagar con diferentes métodos de pago:
+- Tarjeta
+- Transferencia
+- QR
+Y por cada canal aplica un impuesto diferente
+- Tarjeta 5%
+- Transferencia 2%
+- QR 1%
+Simplifica el código aplicando el principio DRY
+```
+
+Crea archivo `tienda.py` en la carpeta `sesion12`
+
+---
+
+Realizar los cambios 4 minutos
+
+```python
+class Tienda:
+ def impuesto_tarjeta(self, monto):
+ return monto * 0.05
+ def impuesto_transferencia(self, monto):
+ return monto * 0.02
+ def impuesto_qr(self, monto):
+ return monto * 0.01
+ def pagar_tarjeta(self, monto):
+ impuesto = self.impuesto_tarjeta(monto)
+ total = monto + impuesto
+ print(f"Pago con tarjeta: Monto={monto}, Impuesto={impuesto}, Total={total}")
+ def pagar_transferencia(self, monto):
+ impuesto = self.impuesto_transferencia(monto)
+ total = monto + impuesto
+ print(f"Pago con transferencia: Monto={monto}, Impuesto={impuesto}, Total={total}")
+ def pagar_qr(self, monto):
+ impuesto = self.impuesto_qr(monto)
+ total = monto + impuesto
+ print(f"Pago con QR: Monto={monto}, Impuesto={impuesto}, Total={total}")
+tienda = Tienda()
+tienda.pagar_tarjeta(100)
+tienda.pagar_transferencia(100)
+tienda.pagar_qr(100)
+```
+
+
+
+---
+
+Solución simple
+
+```python
+class Tienda:
+ def calcular_impuesto(self, monto, tasa):
+ return monto * tasa
+ def pagar(self, monto, metodo):
+ tasas = {
+ "tarjeta": 0.05,
+ "transferencia": 0.02,
+ "qr": 0.01
+ }
+ tasa = tasas.get(metodo, 0)
+ impuesto = self.calcular_impuesto(monto, tasa)
+ total = monto + impuesto
+ print(f"Pago con {metodo}: Monto={monto}, Impuesto={impuesto}, Total={total}")
+tienda = Tienda()
+tienda.pagar(100, "tarjeta")
+tienda.pagar(100, "transferencia")
+tienda.pagar(100, "qr")
+```
+
+```bash
+python tienda.py
+```
+
+```text
+Pago con tarjeta: Monto=100, Impuesto=5.0, Total=105.0
+Pago con transferencia: Monto=100, Impuesto=2.0, Total=102.0
+Pago con qr: Monto=100, Impuesto=1.0, Total=101.0
+```
+
+---
+
+Solución con anotaciones y documentación
+
+```python
+class Tienda:
+ """Clase que representa una tienda en línea."""
+
+ def calcular_impuesto(self, monto: float, tasa: float) -> float:
+ """Calcula el impuesto basado en el monto y la tasa.
+
+ Args:
+ monto (float): El monto sobre el cual se calcula el impuesto.
+ tasa (float): La tasa de impuesto.
+
+ Returns:
+ float: El monto del impuesto calculado.
+ """
+ return monto * tasa
+ def pagar(self, monto: float, metodo: str) -> None:
+ """Procesa el pago utilizando el método especificado.
+ Args:
+ monto (float): El monto a pagar.
+ metodo (str): El método de pago (tarjeta, transferencia, qr).
+ """
+ tasas = {
+ "tarjeta": 0.05,
+ "transferencia": 0.02,
+ "qr": 0.01
+ }
+tienda = Tienda()
+tienda.pagar(100, "tarjeta")
+tienda.pagar(100, "transferencia")
+tienda.pagar(100, "qr")
+```
+
+---
+
+#### YAGNI (You Aren't Gonna Need It)
+
+El principio YAGNI enfatiza la importancia de *NO agregar* funcionalidades
+o características que *no son necesarias* en el momento
+
+Ayuda a evitar la *sobreingeniería* y a mantener el código simple y enfocado
+
+---
+
+Se resume en
+
+- **Necesidad**: No agregue funcionalidades o características que no son necesarias
+- **Simplicidad**: Mantenga el código simple y enfocado en el propósito actual
+- **Eficiencia**: Evite la complejidad innecesaria que puede afectar el rendimiento
+
+---
+
+- **Mantenimiento**: Un código sin características innecesarias es más fácil de mantener
+- **Claridad**: Un código sin características innecesarias es más fácil de entender
+- **Flexibilidad**: Un código sin características innecesarias es más flexible y adaptable a cambios futuros
+
+---
+
+Una forma de aplicarlo es
+
+- **Enfocarse** en los **requisitos actuales** y NO en posibles requisitos futuros
+- *Evitar* la sobreingeniería y centrarse en lo esencial
+- Revisar y *eliminar características innecesarias* durante la refactorización
+
+---
+
+- **Priorizar** la simplicidad y claridad del código
+- **Mantener** el código enfocado en el propósito actual
+- **Evitar** la tentación de agregar características
+
+*"por si acaso"*
+
+---
+#### Ejemplo YAGNI
+
+Crear el archivo `yagni.py` en la carpeta `sesion12`
+
+```text
+Un estudiante esta aprendiendo a sumar dos números, y le dieron
+una calculadora para para que pueda practicar la suma, pero
+tiene muchas funciones que no va a usar aún
+Simplifica el código aplicando el principio YAGNI
+Deja solo la funcionalidad necesaria para sumar dos números
+agrega anotaciones y documentación
+```
+
+```python [1-14]
+class Calculadora:
+ def sumar(self, a, b):
+ return a + b
+ def restar(self, a, b):
+ return a - b
+ def multiplicar(self, a, b):
+ return a * b
+ def dividir(self, a, b):
+ return a / b
+calc = Calculadora()
+print(calc.sumar(5, 7))
+```
+
+---
+
+Solución simple
+
+```python
+class Calculadora:
+ def sumar(self, a, b):
+ return a + b
+calc = Calculadora()
+print(calc.sumar(5, 7))
+```
+
+---
+Solución con anotaciones y documentación
+
+```python
+class Calculadora:
+ """Clase que representa una calculadora simple."""
+ def sumar(self, a: int, b: int) -> int:
+ """Suma dos números enteros.
+
+ Args:
+ a (int): El primer número.
+ b (int): El segundo número.
+
+ Returns:
+ int: La suma de los dos números.
+ """
+ return a + b
+calc = Calculadora()
+print(calc.sumar(5, 7))
+```
+
+---
+#### Ejercicio para ti (03)
+
+```text
+Un profesor necesita una forma sencilla de llevar
+el registro de sus estudiantes, con sus nombres y
+las notas de sus exámenes. Quiere poder calcular
+fácilmente el promedio de cada estudiante y saber
+de si ha aprobado o no el curso,
+con nota mayor a 51, además de tener
+un resumen claro de sus resultados para mostrárselos
+Simplifica el código aplicando el principio YAGNI
+Agrega anotaciones y documentación
+```
+
+Crea archivo `estudiantes.py` en la carpeta `sesion12`
+
+---
+Realizar los cambios 4 minutos
+
+```python
+class Estudiante:
+ def __init__(self, nombre, notas):
+ self.nombre = nombre
+ self.notas = notas
+
+ self.promedio_cache = None
+
+ def calcular_promedio(self):
+ total = 0
+ for n in self.notas:
+ total += n
+ return total / len(self.notas)
+
+ def mostrar_promedio(self):
+ total = 0
+ for n in self.notas:
+ total += n
+ return f"Promedio de {self.nombre}: {total / len(self.notas)}"
+
+ def aprobo(self):
+ promedio = self.calcular_promedio()
+ return promedio > 50 or promedio == 51
+
+ def mejor_nota(self):
+ return max(self.notas)
+
+ def peor_nota(self):
+ return min(self.notas)
+ def resumen(self):
+ return f"Estudiante: {self.nombre}, Notas: {self.notas}, Promedio: {self.calcular_promedio()}, Aprobado: {self.aprobo()}"
+
+estudiante1 = Estudiante("Ana", [85, 90, 78])
+print(estudiante1.mostrar_promedio())
+print(f"Aprobó: {estudiante1.aprobo()}")
+print(estudiante1.resumen())
+```
+
+
+
+---
+Solución simple
+
+```python
+class Estudiante:
+ def __init__(self, nombre, notas):
+ self.nombre = nombre
+ self.notas = notas
+ def calcular_promedio(self):
+ return sum(self.notas) / len(self.notas)
+ def aprobo(self):
+ return self.calcular_promedio() >= 51
+ def resumen(self):
+ mensaje = f"Estudiante: {self.nombre}"
+ mensaje += f", Notas: {self.notas}"
+ mensaje += f", Promedio: {self.calcular_promedio()}"
+ mensaje += f", Aprobado: {self.aprobo()}"
+ return mensaje
+estudiante1 = Estudiante("Ana", [85, 90, 78])
+print(estudiante1.resumen())
+```
+
+```bash
+python estudiantes.py
+```
+```text
+Estudiante: Ana, Notas: [85, 90, 78], Promedio: 84.33333333333333, Aprobado: True
+```
+
+---
+
+Solución con anotaciones y documentación
+
+```python
+class Estudiante:
+ """Clase que representa a un estudiante."""
+ def __init__(self, nombre: str, notas: list[int]) -> None:
+ """Inicializa una nueva instancia de la clase Estudiante.
+
+ Args:
+ nombre (str): El nombre del estudiante.
+ notas (list[int]): Las notas del estudiante.
+ """
+ self.nombre = nombre
+ self.notas = notas
+ def calcular_promedio(self) -> float:
+ """Calcula el promedio de las notas del estudiante.
+ Returns:
+ float: El promedio de las notas.
+ """
+ return sum(self.notas) / len(self.notas)
+ def aprobo(self) -> bool:
+ """Determina si el estudiante aprobó el curso.
+ Returns:
+ bool: True si aprobó, False en caso contrario.
+ """
+ return self.calcular_promedio() >= 51
+ def resumen(self) -> str:
+ """Genera un resumen del estudiante.
+ Returns:
+ str: Un resumen con el nombre, notas, promedio y estado de aprobación.
+ """
+ mensaje = f"Estudiante: {self.nombre}"
+ mensaje += f", Notas: {self.notas}"
+ mensaje += f", Promedio: {self.calcular_promedio()}"
+ mensaje += f", Aprobado: {self.aprobo()}"
+ return mensaje
+estudiante1 = Estudiante("Ana", [85, 90, 78])
+print(estudiante1.resumen())
+```
+
+---
+
+#### Resumen
+
+- Python es un lenguaje popular por su facilidad para escribir código y su tipado dinámico, lo que permite flexibilidad pero puede causar errores difíciles de detectar.
+- Las anotaciones de tipo permiten especificar el tipo de datos esperado para variables, parámetros de funciones, valores de retorno y atributos de clases, mejorando la legibilidad y ayudando a detectar errores antes de la ejecución.
+
+---
+
+- Las anotaciones de tipo en Python son opcionales y no afectan el comportamiento del código en tiempo de ejecución, pero son útiles para desarrolladores y herramientas de análisis estático.
+- Existen diferentes estilos para escribir anotaciones, como el uso directo de tipos o el módulo typing, aunque se recomienda el uso directo en versiones recientes.
+
+---
+
+- La documentación es esencial para entender el propósito y funcionamiento del código, y que se puede realizar mediante cadenas de documentación (docstrings) en clases, funciones y métodos.
+- Existen diferentes estilos de documentación en Python, como Google, NumPy y reStructuredText (reST), cada uno con sus ventajas y desventajas, y que es importante elegir uno y ser consistente.
+
+---
+
+- Los principios de desarrollo ayudan a escribir código limpio, mantenible y escalable, proporcionando soluciones a problemas comunes en el desarrollo de software.
+- El principio KISS enfatiza la simplicidad, claridad y eficiencia, evitando la sobreingeniería y manteniendo el código enfocado en lo esencial.
+
+---
+
+- El principio DRY promueve evitar la duplicación de código, fomentando la reutilización y facilitando el mantenimiento.
+- El principio YAGNI recomienda no agregar funcionalidades innecesarias, manteniendo el código simple y enfocado en los requisitos actuales.
+
+---
+
+#### Retos
+
+Crear una carpeta con el nombre "retos_sesion_12" dentro del proyecto en la raíz, en la cual por cada ejercicio debes crear los siguientes archivos:
+
+```bash
+# Estructura de carpetas
+psg-oop-2025/
+ retos_sesion_12/
+ ejercicio_01.md
+ ejercicio_01.py
+ ejercicio_02.md
+ ejercicio_02.py
+```
+
+---
+
+1. Una empresa de juegos moviles crea un nuevo juego **Dados de la Suerte**, las reglas son sencillas:
+
+- El jugador lanza **dos** dados
+- Si la suma es **7 o 11 gana**
+- Si es **2, 3 o 12 pierde**
+- Con cualquier otro valor puede volver a lanzar los dados.
+
+En el futuro tiene planificado registrar el nombre del jugador y su puntuación más alta
+pero para el lanzamiento inicial no es necesario
+
+*(1/5)*
+
+---
+
+
+Cuenta con un flujo básico:
+
+- Jugador inicia el juego
+- Juego lanza los dados automáticamente
+- Juego muestra el resultado y si ganó, perdió o si puede volver a lanzar
+- Jugador responde SI o NO para volver a lanzar los dados
+- El juego termina cuando el jugador decide NO lanzar más o gana o pierde
+- El juego muestra un mensaje final con el resultado
+
+*(2/5)*
+
+
+---
+
+- Crea la clase `DadosDeLaSuerte` donde se implemente el flujo básico del juego
+- Identifica atributos y métodos relevantes para la clase
+- Implementa la **lógica del juego** según las reglas descritas
+- Documenta el código y usa **anotaciones de tipo**
+- Utiliza los principios KISS, DRY y YAGNI
+
+*(3/5)*
+
+---
+
+Realiza el **análisis** y **diagrama de clases** en el archivo `ejercicio_01.md`
+
+Escribe el código en Python para la clase de tu diagrama de clases en el archivo `ejercicio_01.py`
+
+*(4/5)*
+
+---
+
+- Asegúrate de que la clase incluyan *atributos* y *métodos* coherentes con su propósito
+- Utiliza buenas prácticas de *nomenclatura*, *legibilidad* y *PEP 8*
+- Juega varias veces para verificar que la lógica del juego funciona correctamente
+
+*(5/5)*
+
+---
+
+2. Una empresa de citas necesita un programa para gestionar tareas.
+
+El programa puede:
+
+- Agregar nuevas tareas
+- Eliminar tareas existentes
+- Marcar tareas como completadas
+
+En el futuro se planea agregar fechas de vencimiento y prioridades a las tareas
+
+*(1/5)*
+
+---
+
+Cada tarea tiene un *título*, una *descripción* y un *estado* (completada o no completada).
+
+Cuenta con un menú básico:
+
+1. **Agregar** una nueva tarea proporcionando un título y una descripción
+2. **Eliminar** una tarea por su título
+3. **Marcar** una tarea como completada por su título
+4. **Listar** todas las tareas con su estado actual
+
+*(2/5)*
+
+---
+
+- Crea la clase `Tarea` para representar una tarea individual con atributos y métodos relevantes
+- Crea la clase `GestorDeTareas` para gestionar **una colección** de tareas, incluyendo métodos para agregar, eliminar y marcar tareas como completadas
+- Documenta el código y usa **anotaciones de tipo**
+- Utiliza los principios KISS, DRY y YAGNI
+
+*(3/5)*
+
+---
+
+- Realiza el **análisis** y **diagrama de clases** en el archivo `ejercicio_02.md`
+- Escribe el código en Python para las clases de tu diagrama de clases en el archivo `ejercicio_02.py`
+- Implementa un menú interactivo para que el usuario pueda gestionar las tareas
+- Documenta el código y usa **anotaciones de tipo**
+- Utiliza los principios KISS, DRY y YAGNI
+
+*(4/5)*
+
+---
+
+- Asegúrate de que las clases incluyan *atributos* y *métodos* coherentes con su propósito
+- Utiliza buenas prácticas de *nomenclatura*, *legibilidad* y *PEP 8*
+- Prueba las clases creando instancias y realizando operaciones de agregar, eliminar y marcar tareas como completadas
+
+*(5/5)*
+
+---
+
+Gracias por Participar del
+
+🐍 Python Study Group - OOP 2025 🐍
+
+> Un objeto puede ser pequeño, pero bien diseñado puede sostener un sistema entero
+
+---
+
+
+
+
+
+
+
+
+[ ](https://github.com/python-la-paz/python-study-group-oop/tree/main/content/sesion12)
+
+Repositorio de la Sesión
+
+---
+
+## Bibliografía y Referencias
+
+- [Function Annotations](https://ellibrodepython.com/function-annotations)
+- [Annotations HowTo](https://docs.python.org/es/3/howto/annotations.html)
+- [Python Annotations](https://pywombat.com/articles/python-annotation)
+- [PEP 484](https://peps.python.org/pep-0484/)
+- [PEP 585](https://peps.python.org/pep-0585/#parameters-to-generics-are-available-at-runtime)
+- [KISS Principle](https://fourweekmba.com/kiss-principle/)
+- [Principles of Clean Code: DRY, KISS, and YAGNI](https://medium.com/@curiousraj/the-principles-of-clean-code-dry-kiss-and-yagni-f973aa95fc4d)
+- [DRY Principle](https://www.techtarget.com/whatis/definition/DRY-principle)
+- [The DRY Principle](https://dev.to/ahadalireach/the-dry-principle-50hh)
+- [KISS, DRY, SOLID, YAGNI: A Simple Guide to Some Principles of Software Engineering and Clean Code](https://medium.com/@hlfdev/kiss-dry-solid-yagni-a-simple-guide-to-some-principles-of-software-engineering-and-clean-code-05e60233c79f)
+- [Object Oriented Analysis](https://www.gyata.ai/es/object-oriented-programming/object-oriented-analysis)
+- [DDOO Unidad 1](https://dmd.unadmexico.mx/contenidos/DCEIT/BLOQUE1/DS/02/DDOO/U1/descargables/DDOO_Unidad_1.pdf)
+- [Programación procedural VS orientada a objetos](https://programacionpro.com/programacion-procedural-vs-orientada-a-objetos-diferencias-y-similitudes/)
+- [Python OOP](https://www.learnpython.org/en/Classes_and_Objects)
+- [Atributos de clase](https://oregoom.com/python/atributos-clase/)
+- [Diagrama de clases](https://diagramasuml.com/diagrama-de-clases/)
+- [Guía PEP 8](https://peps.python.org/pep-0008/#class-names)
+- [Mermaid Charts](https://www.mermaidchart.com/play)
+- [Draw.io](https://app.diagrams.net/)
+- [Python 3 Object-oriented Programming, Second Edition, Dusty PhillipsDusty Phillips](https://github.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition)
+- [Objetos en programación](https://ebac.mx/blog/objeto-en-programacion)
+- [Enfoque orientado a objetos](https://1library.co/article/enfoque-orientado-a-objetos-base-te%C3%B3rica.qvld461y)
+- [OOAD](https://www.tutorialspoint.com/object_oriented_analysis_design/ooad_object_oriented_analysis.htm)
diff --git a/content/sesion12/img/annotation_01.png b/content/sesion12/img/annotation_01.png
new file mode 100644
index 0000000..e74d4b9
Binary files /dev/null and b/content/sesion12/img/annotation_01.png differ
diff --git a/content/sesion12/img/annotation_02.png b/content/sesion12/img/annotation_02.png
new file mode 100644
index 0000000..a0ded45
Binary files /dev/null and b/content/sesion12/img/annotation_02.png differ
diff --git a/content/sesion12/img/annotation_03.png b/content/sesion12/img/annotation_03.png
new file mode 100644
index 0000000..fe086b0
Binary files /dev/null and b/content/sesion12/img/annotation_03.png differ
diff --git a/content/sesion12/img/annotation_04.png b/content/sesion12/img/annotation_04.png
new file mode 100644
index 0000000..3dccd26
Binary files /dev/null and b/content/sesion12/img/annotation_04.png differ
diff --git a/content/sesion12/img/annotation_05.png b/content/sesion12/img/annotation_05.png
new file mode 100644
index 0000000..750588a
Binary files /dev/null and b/content/sesion12/img/annotation_05.png differ
diff --git a/content/sesion12/img/annotation_06.png b/content/sesion12/img/annotation_06.png
new file mode 100644
index 0000000..cc84150
Binary files /dev/null and b/content/sesion12/img/annotation_06.png differ
diff --git a/content/sesion12/img/annotation_07.png b/content/sesion12/img/annotation_07.png
new file mode 100644
index 0000000..da916b5
Binary files /dev/null and b/content/sesion12/img/annotation_07.png differ
diff --git a/content/sesion12/img/documentation_01.png b/content/sesion12/img/documentation_01.png
new file mode 100644
index 0000000..6952192
Binary files /dev/null and b/content/sesion12/img/documentation_01.png differ
diff --git a/content/sesion12/img/documentation_02.png b/content/sesion12/img/documentation_02.png
new file mode 100644
index 0000000..7899223
Binary files /dev/null and b/content/sesion12/img/documentation_02.png differ
diff --git a/content/sesion12/img/documentation_03.png b/content/sesion12/img/documentation_03.png
new file mode 100644
index 0000000..dd21ab7
Binary files /dev/null and b/content/sesion12/img/documentation_03.png differ
diff --git a/content/sesion12/img/documentation_04.png b/content/sesion12/img/documentation_04.png
new file mode 100644
index 0000000..2722380
Binary files /dev/null and b/content/sesion12/img/documentation_04.png differ
diff --git a/content/sesion12/img/documentation_05.png b/content/sesion12/img/documentation_05.png
new file mode 100644
index 0000000..52b348a
Binary files /dev/null and b/content/sesion12/img/documentation_05.png differ
diff --git a/content/sesion12/img/documentation_06.png b/content/sesion12/img/documentation_06.png
new file mode 100644
index 0000000..4b0d424
Binary files /dev/null and b/content/sesion12/img/documentation_06.png differ
diff --git a/content/sesion12/img/documentation_07.png b/content/sesion12/img/documentation_07.png
new file mode 100644
index 0000000..eb79eba
Binary files /dev/null and b/content/sesion12/img/documentation_07.png differ
diff --git a/content/sesion12/index.html b/content/sesion12/index.html
new file mode 100644
index 0000000..56e5d31
--- /dev/null
+++ b/content/sesion12/index.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ Sesión 12 | Python Study Group | OOP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.html b/index.html
index febbb0e..fcb4aa8 100644
--- a/index.html
+++ b/index.html
@@ -138,8 +138,9 @@