<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="https://joseluisgs.dev/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <atom:link href="https://joseluisgs.dev/rss.xml" rel="self" type="application/rss+xml"/>
    <title>José Luis González</title>
    <link>https://joseluisgs.dev/</link>
    <description>Web personal de José Luis González Sánchez - Profesor FP de DAW, DAM y formador certificado</description>
    <language>es-ES</language>
    <pubDate>Mon, 20 Apr 2026 22:01:32 GMT</pubDate>
    <lastBuildDate>Mon, 20 Apr 2026 22:01:32 GMT</lastBuildDate>
    <generator>@vuepress/plugin-feed</generator>
    <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
    <category>Blog</category>
    <category>Proyectos</category>
    <category>Docencia</category>
    <category>Personal</category>
    <category>Investigación</category>
    <item>
      <title>Nueva version de mi web</title>
      <link>https://joseluisgs.dev/posts/2026/2026-04-20-nueva_version_web.html</link>
      <guid>https://joseluisgs.dev/posts/2026/2026-04-20-nueva_version_web.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Nueva version de mi web</source>
      <description>Avanzando hacia una nueva etapa con una versión renovada tecnológica y visualmente.</description>
      <category>Blog</category>
      <category>Proyectos</category>
      <pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>A lo largo de los años, mi sitio web ha sido un espacio de aprendizaje, experimentación y compartición de conocimientos. Sin embargo, con el paso del tiempo, la tecnología que sustentaba mi web se volvió obsoleta, lo que me llevó a tomar la decisión de realizar una migración completa a una nueva versión. Esta nueva versión no solo representa una actualización tecnológica, sino también una renovación visual y funcional que busca ofrecer una mejor experiencia a los visitantes y a mis alumnos de Formación Profesional les servirá como un recurso educativo de cómo construir una web moderna usando distintas tecnologías de desarrollo y despliegue.</p>
<!-- more -->
<h2>De dónde partíamos: VuePress 1 y el tema Reco</h2>
<p>Durante mucho tiempo, mi blog ha funcionado con <strong>VuePress 1</strong> combinado con el tema <strong>Reco</strong>. Esta configuración, aunque funcional en su momento, fue quedándose obsoleta con el paso del tiempo.</p>
<h3>Los problemas de la obsolescencia</h3>
<p>Con el tiempo, surgieron varias dificultades:</p>
<ol>
<li><strong>Compatibilidad con Node.js</strong>: El tema Reco requería versiones antiguas de Node.js que ya no recibían soporte.</li>
<li><strong>Falta de mantenimiento</strong>: El tema Reco dejó de recibir actualizaciones activas, lo que complicaba la incorporación de nuevas funcionalidades.</li>
<li><strong>Rendimiento</strong>: Webpack, aunque potente, resultaba lento en los procesos de desarrollo.</li>
<li><strong>Dependencias</strong>: Cada actualización de paquetes planteaba riesgos de romper la configuración existente.</li>
<li><strong>Limitaciones de personalización</strong>: Modificar el tema Reco requería tocar <code>node_modules</code>, lo que era insostenible a largo plazo.</li>
</ol>
<h2>El gran salto tecnológico: VuePress 2 con Vite y Hope Theme</h2>
<p>La solución pasaba por migrar a tecnologías modernas que ofrecieran mayor flexibilidad y rendimiento. Así, decidí adoptar <strong>VuePress 2</strong> junto con el tema <strong>vuepress-theme-hope</strong>, y utilizar <strong>Vite</strong> como herramienta de construcción. La idea es no salir del ecosistema Vue, pero aprovechar las mejoras que estas nuevas herramientas ofrecen y que además se integran perfectamente con la arquitectura <strong>Jamstack</strong> y el renderizado del lado del servidor (SSR) que VuePress 2 implementa de forma nativa. Vue.js es una tecnología que domino y que me permite crear componentes personalizados para enriquecer la experiencia de los usuarios.</p>
<h3>¿Qué es VuePress y cómo funciona?</h3>
<p>VuePress es un generador de sitios web estáticos que combina <strong>Markdown</strong> con <strong>Vue.js</strong>. Cada página que escribes en Markdown se convierte en una aplicación Vue completa, lo que permite:</p>
<ul>
<li>Utilizar componentes Vue dentro del contenido Markdown.</li>
<li>Personalizar cualquier aspecto del sitio con Vue.</li>
<li>Generar sitios web estáticos de alto rendimiento.</li>
<li>Implementar funcionalidades avanzadas como SSR, PWA y SEO de forma nativa.</li>
</ul>
<h3>¿Qué es el renderizado del lado del servidor (SSR)?</h3>
<p>El <strong>SSR (Server-Side Rendering)</strong> es una técnica que permite renderizar las páginas en el servidor antes de enviarlas al navegador del usuario. VuePress utiliza esta arquitectura híbrida:</p>
<p><strong>Beneficios del SSR:</strong></p>
<ul>
<li><strong>SEO mejorado</strong>: Los motores de búsqueda pueden indexar el contenido directamente.</li>
<li><strong>Carga más rápida</strong>: El usuario recibe HTML listo para mostrar.</li>
<li><strong>Mejor experiencia</strong>: La página es visible antes de que Vue tome el control.</li>
<li><strong>Accesibilidad</strong>: Mejor soporte para lectores de pantalla.</li>
</ul>
<h3>¿Qué es Jamstack?</h3>
<p><strong>Jamstack</strong> es una arquitectura moderna para crear sitios web rápidos y seguros. El nombre proviene de:</p>
<ul>
<li><strong>J</strong>avaScript: Lógica del cliente</li>
<li><strong>A</strong>PI: Funcionalidad del servidor</li>
<li><strong>M</strong>arkup: Contenido estático</li>
</ul>
<p><strong>Ventajas de Jamstack:</strong></p>
<ul>
<li><strong>Seguridad</strong>: No hay servidor que atacar.</li>
<li><strong>Escalabilidad</strong>: Servir archivos estáticos es muy económica.</li>
<li><strong>Rendimiento</strong>: CDN distribuida globalmente.</li>
<li><strong>Experiencia de desarrollador</strong>: Flujo de trabajo moderno con Git.</li>
<li><strong>Flexibilidad</strong>: Cualquier backend o API puede ser integrado.</li>
</ul>
<p>VuePress es perfectamente compatible con esta arquitectura, generando archivos estáticos que pueden desplegarse en cualquier CDN.</p>
<h3>¿Por qué Vite?</h3>
<p><strong>Vite</strong> es la herramienta de construcción de nueva generación que ha revolucionado el desarrollo frontend. Ha sido diseñada para ser rápida, eficiente y fácil de usar, superando las limitaciones de herramientas anteriores como Webpack. Vite utiliza ES Modules nativos en el navegador durante el desarrollo, lo que permite un arranque instantáneo y una experiencia de desarrollo fluida. Además, ha sido desarrollada por el mismo creador de Vue.js, lo que garantiza una integración perfecta con VuePress y Vue.js.</p>
<p><strong>Ventajas principales:</strong></p>
<p>| Característica | Webpack (antes) | Vite (ahora) |
|</p>
]]></content:encoded>
      <enclosure url="https://sharklabs.com.br/assets/img/2020-04/vuepress-documentacao-markdown.png" type="image/png"/>
    </item>
    <item>
      <title>Soy embajador de GitKraken</title>
      <link>https://joseluisgs.dev/posts/2021/2021-05-26-gitkraken-embajador.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-05-26-gitkraken-embajador.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Soy embajador de GitKraken</source>
      <description>Te ayudaré en todo lo que quieras de GitKraken Suite</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 26 May 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Como ya habréis visto en mi <em>banner</em> y redes sociales, aparece un nuevo icono conmigo. Es un orgullo, placer y responsabilidad comunicar que he sido nombrado este mes de mayo embajador de <a href="https://www.gitkraken.com/invite/wdJ7HntT" target="_blank" rel="noopener noreferrer">GitKraken</a>. Para mí es una alegría inmensa que confíen para esta labor y así reconozcan el trabajo que se ha realizado estos dos últimos años aplicando <em>GitKraken Suite</em> en la enseñanza del desarrollo de <em>software</em>.</p>
<!-- more -->
<h2>Embajador GitKraken</h2>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/XKIknr7.gif" alt="Logo"></p>
<p>Me gusta <a href="https://www.gitkraken.com/invite/wdJ7HntT" target="_blank" rel="noopener noreferrer">GitKraken</a>, como <a href="https://joseluisgs.github.io/blog/2021-05-20-gitkraken-superpoder-git.html" target="_blank" rel="noopener noreferrer">ya os dije</a>. Ahora me han nombrado embajador. Es un reto apasionante poder representar y ayudar a usar esta <em>suite</em> que nos ofrece superpoderes para desarrollar nuestro código usando Git/GitHub. A la misma vez, me hace sentir muy feliz por mi centro, compañeros/as y alumnado, que se beneficiarán de ello. Y, sobre todo, me ilusiona poder ayudar y responder dudas que la comunidad me haga. Es un honor que una de las mejores herramientas existentes confíe en mí para esta labor.</p>
<h3>¿Qué haré?</h3>
<p>Pues es sencillo. Casi igual a lo que hago día a día. Intentaré aportar mi experiencia docente y como desarrollador. Con ello participaré en reuniones para dar mi opinión sobre funcionalidades de la herramienta. Luego estará mi labor para con la comunidad. Haré tutoriales de cómo usarla o ejemplos especiales de aspectos concretos que sean reseñables. Así que, si os interesa el material o simplemente os puedo ayudar en algo, no dudéis en contactar conmigo. Será un placer poder echaros un cable con ello. ¡Cuenta conmigo! 💪</p>
<p>Nos esperan grandes aventuras juntos y <a href="https://www.gitkraken.com/invite/wdJ7HntT" target="_blank" rel="noopener noreferrer">GitKraken</a> será protagonista en ellas como gran herramienta que es y que me encanta utilizar.</p>
<h2>Kit de embajador GitKraken</h2>
<p>¡Mola! 😁 Ya me estoy imaginando lo bien que me quedarán esos calcetines. De hecho, estoy pensando en una falda escocesa a juego y dar mis ponencias, talleres y tutoriales en chanclas. La verdad es que me ha gustado y lo más importante es la confianza hacia mi labor y, por supuesto, mis calcetines 😎.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/qMDxOZj.jpg" alt="Logo"></p>
<div class="hint-container tip">
<p class="hint-container-title">¡Obtén tu superpoder!</p>
<p>¿Quieres GitKraken gratis? <a href="https://www.gitkraken.com/invite/wdJ7HntT" target="_blank" rel="noopener noreferrer">Haz clic aquí</a>.</p>
</div>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/sTDaBTu.png" alt="Educación">
</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/1Dnghu1.png" type="image/png"/>
    </item>
    <item>
      <title>Mejorando en Kotlin como profesor gracias a una decepción</title>
      <link>https://joseluisgs.dev/posts/2022/2022-06-25-decepciones-superaciones.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-06-25-decepciones-superaciones.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Mejorando en Kotlin como profesor gracias a una decepción</source>
      <description>Circunstancias inesperadas que te hacen mejorar y dar un paso más</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Sat, 25 Jun 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>A veces las decepciones te hacen crecer. Este año me ha pasado con Kotlin. Una decepción me ha llevado a profundizar más en el lenguaje y convertirme en mejor profesor.</p>
<!-- more -->
<h2>La decepción</h2>
<p>Todo comenzó cuando no pude dar una clase que tenía preparada. Pero esa situación me llevó a investigar más, a profundizar en Kotlin y a descubrir herramientas y técnicas que ahora uso a diario en clase.</p>
<h2>El aprendizaje</h2>
<p>Las decepciones son oportunidades de mejora. Lo importante es no rendirse y usar esa frustración como combustible para mejorar.</p>
<h2>Nuevo reconocimiento</h2>
<p>Fruto de todo este trabajo, he obtenido la certificación de <em>Kotlin Trainer</em> por parte de JetBrains. Un reconocimiento que me llena de orgullo y que me compromete a seguir mejorando.</p>
<p>Gracias a todos los que me acompañáis en este viaje.</p>
]]></content:encoded>
      <enclosure url="https://kotlinlang.org/assets/images/twitter/general.png" type="image/png"/>
    </item>
    <item>
      <title>Kotlin Expert</title>
      <link>https://joseluisgs.dev/posts/2022/2022-06-27-kotlin-expert.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-06-27-kotlin-expert.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Kotlin Expert</source>
      <description>La mejor formación para ser un todoterreno en Kotlin</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Mon, 27 Jun 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Hoy te vengo a presentar una formación que seguro que te gustará: <em>Kotlin Expert</em>. A mí me ha encantado la idea y me está encantando el curso. Sí, lo estoy cursando. No suelo hacer recomendaciones vacías, sino basadas en mi propia experiencia.</p>
<!-- more -->
<h2>Ir más allá de Android</h2>
<p>Kotlin es mucho más que un lenguaje para Android. De hecho, este es el gran error para quienes lo desconocen. Kotlin es un lenguaje moderno, ágil y potente para la JVM y más. Puedes usarlo como sustituto de Java para todos tus proyectos, desde hacer microservicios con Kotlin hasta hacer aplicaciones completamente multiplataforma.</p>
<h2><em>Kotlin Expert</em></h2>
<p><em>Kotlin Expert</em> es una formación creada por Antonio Leiva dentro de las formaciones usando Kotlin que puedes encontrar en <em>DevExperto</em>. Antonio es formador en Kotlin certificado por JetBrains y <em>Google Developer Expert</em>.</p>
<p>El objetivo de esta formación es dar respuesta a las necesidades del sector donde los programadores Kotlin son cada vez más valorados para el desarrollo que va más allá de Android.</p>
<p>La formación es 100% práctica, en vídeo y materiales prácticos. Crea una <em>App</em> desde cero donde tanto el servidor como el cliente estarán programados en Kotlin.</p>
<h2>Contenidos</h2>
<ul>
<li>Módulo 1 - Conceptos básicos.</li>
<li>Módulo 2 - Conceptos avanzados.</li>
<li>Módulo 3 - Compatibilidad de Java.</li>
<li>Módulo 4 - Servidor <em>backend</em> con Ktor.</li>
<li>Módulo 5 - Cliente CLI.</li>
<li>Módulo 6 - UI con <em>Compose Desktop</em>.</li>
<li>Módulo 7 - <em>Kotlin Multiplatform</em>.</li>
<li>Módulo 8 - <em>APP</em> Web con Kotlin/JS.</li>
<li>Módulo 9 - Android con <em>Kotlin Multiplatform</em>.</li>
<li>Módulo 10 - <em>Kotlin Multiplatform Mobile</em> e iOS.</li>
</ul>
<h2>Opinión personal</h2>
<p>Desde hace poco soy certificador en tecnologías Kotlin por parte de JetBrains; además tengo el título de <em>Kotlin Developer</em> por <em>JetBrains Academy</em>. Sinceramente, te puedo garantizar que la formación que ofrece Antonio es de muchísima calidad y te centra en un solo curso lo que te supondría horas y meses de trabajo y formación.</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/kXUCPP1.png" type="image/png"/>
    </item>
    <item>
      <title>Vacaciones para el alumnado. Reflexiones, despedidas y hasta luego</title>
      <link>https://joseluisgs.dev/posts/2022/2022-07-11-alumnado-verano.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-07-11-alumnado-verano.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Vacaciones para el alumnado. Reflexiones, despedidas y hasta luego</source>
      <description>Algunas reflexiones de fin de curso con el calor de protagonista</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Mon, 11 Jul 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Se acaba el curso y llega el momento de las despedidas. Ha sido un año intenso con mucho trabajo, pero también con muchas satisfacciones. El alumnado ha crecido tanto académica como personalmente.</p>
<!-- more -->
<h2>Reflexiones del curso</h2>
<p>Este año hemos trabajado duro en programación, resolviendo problemas reales y aprendiendo de nuestros errores. El resultado ha sido muy positivo: proyectos finales de gran calidad y un aprendizaje sólido.</p>
<h2>Despedidas</h2>
<p>A los que os vais, deciros que ha sido un placer acompañaros en este camino. Recordad que el aprendizaje no termina aquí, sino que empieza realmente ahora.</p>
<h2>Hasta luego</h2>
<p>Os deseo unas buenas vacaciones y mucho éxito en vuestra andadura profesional. Recordad que siempre podéis contactar conmigo para cualquier duda o recomendación.</p>
<p>¡Hasta luego!</p>
]]></content:encoded>
      <enclosure url="https://i0.wp.com/wanderin.dev/wp-content/uploads/2019/12/crop-0-0-1170-390-0-about-cover.png" type="image/png"/>
    </item>
    <item>
      <title>Nuevo curso, nuevos retos</title>
      <link>https://joseluisgs.dev/posts/2022/2022-11-02-nuevo-curso-nuevos-retos.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-11-02-nuevo-curso-nuevos-retos.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Nuevo curso, nuevos retos</source>
      <description>Asentando y mejorando lo que tenemos en nuestro día a día</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 02 Nov 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Nuevo curso, nuevos retos. Y es que un año más toca empezar con ilusión y ganas de mejorar. Este curso vamos a intentar consolidar lo aprendido el año anterior y añadir nuevas tecnologías y metodologías.</p>
<!-- more -->
<h2>Objetivos del curso</h2>
<p>Este curso vamos a trabajar en mejorar la calidad del código, profundizar en patrones de diseño y arquitectura, y preparar al alumnado para el mundo laboral con proyectos realistas.</p>
<h2>Nuevas tecnologías</h2>
<p>Introduciremos Spring Boot para el desarrollo <em>backend</em>, Kubernetes para el despliegue y profundizaremos en Kotlin y su ecosistema.</p>
<h2>Metodología</h2>
<p>Seguimos con el aprendizaje basado en proyectos, donde el alumnado aprende resolviendo problemas reales del sector.</p>
<p>¡Mucho ánimo a todos!</p>
]]></content:encoded>
      <enclosure url="https://www.besthrcertification.org/Images/New-trends-to-tackle-new-challenges.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Me encantan las Corrutinas</title>
      <link>https://joseluisgs.dev/posts/2022/2022-11-29-me-gusta-corrutinas.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-11-29-me-gusta-corrutinas.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Me encantan las Corrutinas</source>
      <description>Cómo manejar la asincronía, concurrencia y reactividad de forma sencilla en tus módulos</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Tue, 29 Nov 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Si hay algo de lo que he disfrutado desde hace mucho tiempo es del uso de las Corrutinas en Kotlin. Este año he decidido explotar su uso en el <em>backend</em> y no puedo estar más satisfecho: rapidez, sencillez y nuevas filosofías de programación fácilmente aplicables sin recurrir a otros recursos.</p>
<!-- more -->
<h2>Corrutinas</h2>
<p>Las corrutinas son una característica de Kotlin que viene a facilitar la programación asíncrona y la reactividad de una manera muy sencilla. Si vienes de otros lenguajes ya conoces <em>async/await</em> de JavaScript o C# o los observables de RxJava y compañía. Las corrutinas permiten escribir código asíncrono de forma secuencial, ocultando la complejidad subyacente de las <em>callbacks</em> y el manejo de hilos.</p>
<p>La diferencia principal es que el hilo no se bloquea, sino que se suspende mientras espera el resultado de una operación. Esto es fundamental en aplicaciones donde la concurrencia y el rendimiento son importantes, como en aplicaciones móviles o servicios web.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> ejemplo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> resultado </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> operacionAsincrona</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(resultado)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2><em>Scope</em> y <em>Context</em></h2>
<p>Las corrutinas necesitan un contexto y un alcance (<em>scope</em>) para ejecutarse. El contexto incluye el <em>Dispatcher</em> que define en qué hilo se ejecuta la corrutina (<em>Main</em>, IO, etc.) y el alcance define el tiempo de vida de la corrutina.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> scope </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> CoroutineScope</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Dispatchers.Main </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">+</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> SupervisorJob</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">())</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h2>Funciones <em>suspend</em></h2>
<p>Las funciones <em>suspend</em> son funciones que pueden ser pausadas y reanudadas. Solo pueden ser llamadas desde una corrutina u otra función <em>suspend</em>.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> getDatos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Datos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> withContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Dispatchers.IO) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        baseDeDatos.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">obtener</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2><em>async/await</em></h2>
<p>Permite ejecutar varias operaciones en paralelo y esperar sus resultados.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> dato1 </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> async</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getDatos1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> dato2 </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> async</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getDatos2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() }</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(dato1.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">await</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">+</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> dato2.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">await</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">())</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2><em>Flows</em></h2>
<p>Los <em>Flows</em> son secuencias asíncronas que pueden emitir múltiples valores, perfectos para el manejo de <em>streams</em> de datos.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> getDatosFlow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Flow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Datos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> flow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    while</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">        emit</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getDatos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">())</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">        delay</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1000</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2><em>Channels</em></h2>
<p>Los <em>channels</em> permiten comunicación entre corrutinas de forma segura.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> channel </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Channel</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">channel.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">send</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"mensaje"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> mensaje </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> channel.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">receive</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Las corrutinas han revolucionado la forma de programar en Kotlin, facilitando el manejo de operaciones asíncronas de forma clara y eficiente.</p>
]]></content:encoded>
      <enclosure url="https://kotlin.desarrollador-android.com/wp-content/uploads/2019/04/coroutines-kotlin.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>El día que diga basta</title>
      <link>https://joseluisgs.dev/posts/2022/2022-11-30-dia-diga-basta.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-11-30-dia-diga-basta.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">El día que diga basta</source>
      <description>Cuando el sistema puede quemar al profesorado</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 30 Nov 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Este curso está siendo un poco atípico por muchas circunstancias. A veces es bueno reflexionar qué está pasando con el sistema donde te mueves y realizas tu labor diaria y saber qué puedes cambiar y qué no.</p>
<!-- more -->
<h2>Antes de comenzar</h2>
<p>Antes de nada, debo recordar que esto es mi web personal y que no representa al centro donde trabajo u otros centros donde he trabajado. No soy un portavoz de nadie más que de mí mismo. Si no estás de acuerdo con lo que digo, no te lo tomes a mal: es mi opinión y punto de vista, y lo expresaré de la forma más respetuosa posible.</p>
<h2>Ser profesor y vivir de la enseñanza</h2>
<p>Ser profesor de Formación Profesional es algo vocacional. Te dedicas más horas de las que querrías y de las que el sistema te da. Estás continuamente formándote (al menos los que nos dedicamos a esto) y sacrificas tu tiempo personal, que también lo necesitas.</p>
<p>Y como toda profesión, tiene su lado oscuro. El sistema está para ayudar, pero no lo hace. Las ratios son inasumibles, el apoyo insuficiente y la frustración va en aumento. Pero lo peor es cuando ves que no importas y que el sistema está más interesado en el papeleo que en el aprendizaje real.</p>
<p>Hoy me levanto pensando en qué puedo hacer para cambiar el sistema y me doy cuenta de que solo puedo cambiar una cosa: yo mismo. Puedo decidir cómo actuar a pesar de las circunstancias. Y así, empezaré a cuidarme un poco más.</p>
<p>Este será mi último curso dando clase. Ha sido una experiencia increíble, pero ha llegado el momento de decir basta. Me marcho a dedicar mi conocimiento y energía a otros proyectos donde sí se valore mi trabajo.</p>
<p>Gracias a todos los que me han apoyado en este camino. Sigue buscando formas de mejorar la educación, incluso cuando el sistema no coopera.</p>
]]></content:encoded>
      <enclosure url="https://img.freepik.com/premium-photo/broken-chain-with-red-weak-link_147644-166.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Ya no sé programar si no es reactivo</title>
      <link>https://joseluisgs.dev/posts/2022/2022-12-06-ya-no-se-programar-sin-reactividad.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-12-06-ya-no-se-programar-sin-reactividad.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Ya no sé programar si no es reactivo</source>
      <description>O como la reactividad ayuda en determinadas aplicaciones</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Tue, 06 Dec 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>La frase no es mía, es del gran Antonio Leiva 👈, en una de las charlas que tenemos a menudo. Pero me ha dado pie a escribir esta entrada, que no es más que una reflexión sobre la programación reactiva y su uso en determinadas aplicaciones y cómo llevarla al terreno de la docencia para explicar sus beneficios al alumnado. ¡Ya somos dos, mi admirado DevExperto! 💪</p>
<!-- more -->
<h2>Reactividad: ¿esto qué es?</h2>
<p>Vayamos por partes. O sigamos los pasos que hemos realizado en Programación de Servicios y Procesos este curso. La programación reactiva es un paradigma de programación que se basa en la reactividad. La reactividad es un concepto que se basa en la capacidad de reaccionar ante un estímulo. En el caso de la programación reactiva, se basa en la capacidad de reaccionar ante un cambio de estado. Y no es un unicornio mágico del que los desarrolladores hablemos por rincones oscuros de la web; es algo más simple y sencillo que combina distintas ideas y patrones de programación para conseguir ese resultado: <em>reaccionar ante un cambio de estado</em>.</p>
<h2>¿Qué es un cambio de estado?</h2>
<p>Por ejemplo, si estamos en nuestro <em>timeline</em> de Twitter/LinkedIn o red favorita y alguien nos menciona, nos llega un mensaje, nos llega una notificación, etc. ¿Estamos constantemente preguntando si nos han mencionado para mostrar esa notificación? ¡No! De alguna manera, delegamos esa acción y somos nosotros los que reaccionamos ante ese cambio de estado.</p>
<h2>Patrón <em>Observer</em> y los eventos</h2>
<p>Esto es tan viejo como el patrón <em>Observer</em> o el propio uso de los eventos. En el patrón <em>Observer</em>, tenemos un sujeto que es el que emite los cambios de estado y un observador que es el que reacciona ante esos cambios de estado. De hecho, lenguajes como Kotlin lo tienen incorporado en el sistema en varios tipos de delegados: <em>observable</em> o <em>vetoable</em>. En el caso de los eventos, tenemos un emisor de eventos y un receptor de eventos. En ambos casos, el emisor y el receptor no se conocen entre sí, pero ambos saben que el otro existe y que reaccionará ante un cambio de estado. ¿Lo ves? Lo llevas usando todo el tiempo y no lo sabías.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://refactoring.guru/images/patterns/content/observer/observer-comic-1-es.png" alt="Imagen">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://refactoring.guru/images/patterns/diagrams/observer/structure.png" alt="Imagen">
</p>
<p>Aquí te dejo una simple implementación en Kotlin de un patrón <em>Observer</em> por si tienes curiosidad, usada en clase. Lo ves, ya sabes: reactividad.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Una interfaz, podríamos usar genéricos</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">interface</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Publisher</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> onNews</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(news: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Radio Una clase que implementa la interfaz</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> RadioChannel</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Publisher</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> onNews</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(news: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"La radio informa: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$news</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Periodico Una clase que implementa la interfaz</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Newspaper</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Publisher</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> onNews</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(news: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"El periódico informa: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$news</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Agencia que es observada</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> NewsAgency</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Lista de observadores, son los que implementan la interfaz</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> listeners </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> mutableListOf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Publisher</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Usamos los delegados que automáticamente, si detectan un cambio, avisan</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> news: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> by</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Delegates</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">observable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(initialValue </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ""</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) { _, old, new </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">-></span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (new </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> old) listeners.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">map</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { listener </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">-></span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> listener.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">onNews</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(new) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Añadimos un observador</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> subscribe</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(publisher: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Publisher</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> listeners.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">add</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(publisher)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Eliminamos un observador</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> unsubscribe</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(publisher: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Publisher</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> listeners.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">remove</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(publisher)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Preparamos los objetos observadores que reaccionarán ante un cambio de estado</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> radioChannel </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> RadioChannel</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> newspaper </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Newspaper</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> newsAgency </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> NewsAgency</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Suscribimos a la agencia, me observan si cambio de estado</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    newsAgency.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">subscribe</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(radioChannel)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    newsAgency.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">subscribe</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(newspaper)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Lanzamos una noticia. Al estar el delegado, la observa y automáticamente notifica</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // A mis observadores ante el cambio de estado, ¿lo ves?</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    newsAgency.news </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "¡Nadal Gana!"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    newsAgency.news </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "¡Hoy llueve!"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    newsAgency.news </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "¡Todos han aprobado!"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Los periódicos se retiran</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    newsAgency.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">unsubscribe</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(newspaper)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    newsAgency.news </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Llegan las vacaciones :)"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>Programación asíncrona</h2>
<p>La programación asíncrona es un concepto que se basa en la ejecución de tareas de forma no síncrona. Esto quiere decir que no se tiene por qué ejecutar una detrás de otra en el mismo hilo de ejecución esperando una instrucción a la anterior para poder ejecutarse. La programación asíncrona nos da la capacidad de &quot;diferir&quot; la ejecución de una rutina a la espera de que se complete una operación, normalmente de I/O (red, disco duro, etc.), y así evitar bloquear la ejecución hasta que se haya completado la tarea en cuestión. Esto además nos permite ejecutar varias tareas a la vez de manera concurrente, sin tener que esperar a que una de ellas termine para poder ejecutar la siguiente.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://blog.kakaocdn.net/dn/btDTJ1/btqKEAB7QfT/frFPyvHrNiygGeJLzyW7tK/img.png" alt="Imagen">
</p>
<p>¿Por qué es importante la asincronía para ser reactivos? ¿Sabes cuándo has de reaccionar? Por ejemplo, ¿debes bloquearte para ello si necesitas reaccionar si hay un cambio en la base de datos? Es por ello que la programación reactiva se basa en la programación asíncrona como uno de sus pilares fundamentales.</p>
<h2>Procesamiento de colecciones</h2>
<p>Y sí, procesar colecciones de manera asíncrona es fundamental para la reactividad; si no, ¿cómo vas a detectar el famoso cambio de estado ante distintos valores? Si tenemos una fuente de datos como una base de datos que puede cambiar, lo ideal es reaccionar ante el cambio y procesar esos datos de manera asíncrona.</p>
<h2>Programación reactiva</h2>
<p>Ahora sí estamos preparados para saber qué es la programación reactiva de acuerdo a su manifiesto.</p>
<p>La programación reactiva es un paradigma enfocado en el trabajo con flujos de datos (nuestras colecciones) finitos o infinitos de manera asíncrona con el objetivo de reaccionar al cambio de estado que pueda producirse en los mismos. Su concepción y evolución ha ido ligada a la publicación del <em>Reactive Manifesto</em>, que establecía las bases de los sistemas reactivos, los cuales deben ser:</p>
<ul>
<li>Responsivos: aseguran la calidad del servicio cumpliendo unos tiempos de respuesta establecidos.</li>
<li>Resilientes: se mantienen responsivos incluso cuando se enfrentan a situaciones de error.</li>
<li>Elásticos: se mantienen responsivos incluso ante aumentos en la carga de trabajo.</li>
<li>Orientados a mensajes: minimizan el acoplamiento entre componentes al establecer interacciones basadas en el intercambio de mensajes de manera asíncrona.</li>
</ul>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://www.reactivemanifesto.org/images/reactive-traits-es.svg" alt="Imagen">
</p>
<div class="hint-container tip">
<p class="hint-container-title">Programación reactiva: programación asíncrona de flujos observables</p>
<p>No te líes, procesas el cambio de estado observando y procesando colecciones (flujos de datos) de manera asíncrona. Es decir: patrón observador, programación asíncrona y procesamiento de colecciones en base a ideas similares al productor-consumidor y el manejo del <em>Backpressure</em> (se genera más elementos de los que un <em>Subscriber</em> puede consumir).</p>
</div>
<h2>Librerías de programación reactiva</h2>
<p>Existen varias librerías de programación reactiva:</p>
<ul>
<li>Kotlin <em>Flows</em>: es la librería de Kotlin que te permite procesar flujos de datos de manera asíncrona y reaccionar ante el cambio de los mismos.</li>
<li>Rx: fueron las pioneras en el desarrollo reactivo JS, .NET y Java/Kotlin, se encuentran completamente integradas en <em>frameworks</em> como Spring MVC, Spring Cloud y Netflix OSS.</li>
<li><em>Reactor</em>: Parte de RxJava 2. Esta API introduce los tipos <em>Flux</em> y <em>Mono</em> como implementaciones de <em>Publisher</em>.</li>
<li>NgRx: es un marco para construir aplicaciones reactivas en Angular.</li>
<li><em>LiveData</em> es una clase de contenedor de datos observables. <em>LiveData</em> está optimizado para ciclos de vida.</li>
</ul>
<p>Los que me conocen saben que soy muy pro Kotlin, por lo que te dejo una sencilla implementación de Kotlin <em>Flows</em> para procesar un RSS. Como puedes ver, esta vez no preguntamos activamente si hay noticias; simplemente observamos el flujo de datos y reaccionamos ante el cambio de estado asíncronamente; simplemente cuando lleguen o vayan llegando las procesamos.</p>
<h2>Aplicaciones de programación reactiva</h2>
<p>Entramos en la parte interesante: ¿cuáles son las aplicaciones de la programación reactiva? Pues si has seguido el artículo, en todo lo que implique reaccionar ante un cambio que no sabes cuándo se producirá y por lo tanto no puedes preguntar activamente por el mismo bloqueando el hilo principal.</p>
<h3>Interactividad en aplicaciones</h3>
<p>En aplicaciones de escritorio, móviles o web, la interactividad es un factor clave. La programación reactiva permite que las aplicaciones reaccionen ante los cambios de estado de manera asíncrona y sin bloquear el hilo principal.</p>
<h3>Servicios reactivos</h3>
<p>En servicios, la ejecución asíncrona y sin bloqueo y la E/S suelen ser más rentables mediante un uso más eficiente de los recursos. Ayuda a minimizar la contención en los recursos compartidos del sistema, que es uno de los mayores obstáculos para la escalabilidad, la baja latencia y el alto rendimiento.</p>
<p>Imagina un servicio que necesita realizar 10 solicitudes a una base de datos y esperar sus respuestas. Digamos que cada solicitud tarda 100 milisegundos. Si necesita ejecutarlas de manera secuencial síncrona, el tiempo total de procesamiento será de aproximadamente 1 segundo. Mientras que si es capaz de ejecutarlas todas de forma asíncrona, el tiempo de procesamiento será de solo 100 milisegundos.</p>
<p>Por otro lado, sabemos que los accesos a la base de datos con JDBC, por ejemplo, son bloqueantes; si conseguimos trabajar con <em>drivers</em> reactivos como R2DBC, podemos mejorar el rendimiento de nuestro servicio al no bloquear nunca la llamada.</p>
<h3>Conclusiones</h3>
<p>A lo largo de esta entrada he intentado acercaros a la programación reactiva, sus conceptos básicos y sus aplicaciones. No es algo tan temible y creo que podrás usarla en tus proyectos sin problemas. Para mí es fundamental en módulos como Acceso a Datos y Programación de Servicios y Procesos saber dominar los elementos clave detectando cuándo es necesaria y qué ventajas nos aporta.</p>
<p>Te invito a que te adentres en este mundo, sobre todo, que poco a poco lo integres en tu <em>back</em> o <em>front</em> y, por supuesto, en tus proyectos y clases.</p>
<div class="hint-container tip">
<p class="hint-container-title">Yo tampoco sé programar sin reactividad</p>
<p>Y sí, amigo Antonio, yo tampoco sé programar sin reactividad y así nos va. Interfaces que reaccionan, microservicios más ligeros, bases de datos que no bloquean, etc... Todo es reactividad.</p>
</div>
]]></content:encoded>
      <enclosure url="https://reactiveweb.org/wp-content/uploads/2024/11/Reactive-Programming-vs-Asynchronous-%E2%80%93-A-Comparison.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Al alumno que copió en su proyecto</title>
      <link>https://joseluisgs.dev/posts/2022/2022-12-30-al-alumno-que-cpio-en-su-proyecto.html</link>
      <guid>https://joseluisgs.dev/posts/2022/2022-12-30-al-alumno-que-cpio-en-su-proyecto.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Al alumno que copió en su proyecto</source>
      <description>Seguro que al mirarte te sientes satisfecho</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Fri, 30 Dec 2022 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Sé que copiaste. Lo sé porque el código es demasiado bueno para ser verdad. Y lo más triste es que te has perdido la oportunidad de aprender de verdad.</p>
<!-- more -->
<h2>El problema de copiar</h2>
<p>Copiar no te hace más listo, te hace más dependiente. El día que tengas que enfrentar un problema real y no tengas a quién copiar, te darás cuenta de que no sabes resolver nada.</p>
<h2>La reflexión</h2>
<p>Si hubieras dedicado ese tiempo a entender el código, a buscar información, a preguntar, habrías aprendido de verdad. Y lo más importante, habrías desarrollado tu capacidad de resolver problemas.</p>
<h2>El consejo</h2>
<p>No te rindas. Todo el mundo comienza copiando. Lo importante es dar el paso de copiar a entender, y de entender a crear. Ese es el verdadero aprendizaje.</p>
<p>Recuerda: el único que sale perdiendo cuando copias eres tú mismo.</p>
]]></content:encoded>
      <enclosure url="https://cdn.setapp.com/blog/images/how-to-fix-copy-and-paste-not-working-1920-646.png" type="image/png"/>
    </item>
    <item>
      <title>Crea tu API REST reactiva con Kotlin y Ktor Parte II</title>
      <link>https://joseluisgs.dev/posts/2023/2023-06-19-reactive-api-rest-pt-ii.html</link>
      <guid>https://joseluisgs.dev/posts/2023/2023-06-19-reactive-api-rest-pt-ii.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Crea tu API REST reactiva con Kotlin y Ktor Parte II</source>
      <description>Cómo crear un servicio REST aplicando reactividad y consideraciones a tener en cuenta para ello</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Mon, 19 Jun 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Continuamos con nuestro tutorial de cómo crear una API REST reactiva y cómo asentar las bases de este paradigma en el desarrollo de servicios como ya vimos en la primera parte. Te recuerdo que esta serie de artículos forman parte de mis actividades con Hyperskill de JetBrains Academy.</p>
<!-- more -->
<div class="hint-container warning">
<p class="hint-container-title">Sobre la traducción</p>
<p>Lamentablemente no tengo tiempo para traducir todos los artículos que escribo; para continuar con la primera parte he usado ChatGPT y pulido los errores que me he encontrado. De nuevo te recomiendo la lectura del original en inglés si buscas el 100% de exactitud: <a href="https://medium.com/p/7b6b087f61e7" target="_blank" rel="noopener noreferrer">Creating Your Reactive REST API with Kotlin and Ktor Part II</a>.</p>
</div>
<h2>Introducción</h2>
<p>Estos tutoriales tienen como objetivo mostrarte todo lo que aprenderás a través de los <em>tracks</em> de Hyperskill de JetBrains Academy. Hyperskill es el lugar perfecto para profundizar, ampliar tus conocimientos y aprender más sobre lo que se presenta en este tutorial.</p>
<p>Esta parte es un poco larga porque creamos toda la estructura de características increíbles de nuestro servicio. Tómalo con calma y visita Hyperskill para obtener más funciones geniales para tus servicios de Kotlin y Ktor.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*0f7cDW-SPHD23LtnVi2Bgw.png">
</p>
<h2>DTOs y <em>Mappers</em></h2>
<p>Nuestro primer punto es mejorar las solicitudes y respuestas. Para lograr esto, introduciremos un patrón interesante: DTO (<em>Data Transfer Object</em>). Los DTO nos permiten transferir datos entre diferentes componentes de una aplicación, encapsulando la información de forma estandarizada.</p>
<p>Gracias a los DTO, podemos encapsular las solicitudes y respuestas mapeando entidades o diferentes tipos de datos entre ellos.</p>
<h2>Validaciones y Páginas de Estado</h2>
<p>Uno de nuestros problemas fundamentales es asegurarnos de que la información recibida sea correcta. No tiene sentido que nuestra raqueta tenga un precio negativo o que tengamos una marca o modelo vacío.</p>
<p>Para asegurar la corrección de la información entrante, utilizamos el complemento <em>RequestValidation</em> en Ktor. Este complemento nos permite validar el cuerpo de las solicitudes entrantes.</p>
<h2>Almacenamiento Reactivo en la Base de Datos</h2>
<p>Sabemos que tener un almacenamiento en memoria puede resultar tedioso. Implementemos nuestro repositorio para trabajar con una base de datos reactiva.</p>
<p>La diferencia principal entre JDBC y R2DBC radica en su enfoque y funcionamiento. JDBC es una API tradicionalmente utilizada en entornos Java para interactuar de manera síncrona con bases de datos relacionales. Sigue un enfoque de bloqueo.</p>
<p>Por otro lado, R2DBC es una API reactiva diseñada para la interacción asíncrona y reactiva con bases de datos relacionales. Permite operaciones de base de datos sin bloqueo.</p>
<p>El primer paso es seleccionar una biblioteca que nos ayude a programar con una base de datos reactiva. Utilizaremos Kotysa. Kotysa es un ORM ligero que ofrece una forma idiomática de escribir SQL seguro en términos de tipos de Kotlin utilizando el enfoque JDBC o R2DBC.</p>
<h2>Codificando nuestros servicios</h2>
<p>El siguiente paso es enriquecer el sistema de notificación en tiempo real con WebSockets y otras características avanzadas.</p>
<h3><em>Railway Oriented Programming</em> (ROP)</h3>
<p>ROP es un concepto de programación funcional para manejar errores y componer operaciones secuenciales. Organiza las operaciones en una secuencia de pasos, donde cada paso puede tener éxito y continuar al siguiente paso en el camino deseado o fallar y dirigirse a un camino de manejo de errores.</p>
<h2><em>Testing</em></h2>
<p>Ahora entramos en una parte crucial y esencial: probar nuestro código. Para hacer esto, haremos uso de JUnit. JUnit es un marco de pruebas de código abierto ampliamente utilizado para aplicaciones Java.</p>
<p>Puedes encontrar el código de este proyecto en GitHub. Por favor, no olvides darle una estrella o seguirme para estar al tanto de nuevos tutoriales y noticias.</p>
<p><strong>Puedes seguirlo <em>commit</em> por <em>commit</em> y utilizar el archivo de respaldo de Postman para probarlo.</strong></p>
<p>Recuerda que este no es un código para usar en un entorno real o de producción. Es un proyecto didáctico para que puedas experimentar, analizar, mejorar o adaptar a tu forma de programar.</p>
<p>Las siguientes pistas ofrecidas por JetBrains Academy en Hyperskill pueden ser un punto de partida perfecto:</p>
<ul>
<li>Desarrollador Kotlin</li>
<li>Desarrollador <em>Backend</em> Kotlin (Ktor)</li>
</ul>
]]></content:encoded>
      <enclosure url="https://miro.medium.com/v2/resize:fit:720/format:webp/1*0f7cDW-SPHD23LtnVi2Bgw.png" type="image/png"/>
    </item>
    <item>
      <title>Crea tu API REST reactiva con Kotlin y Ktor Parte III</title>
      <link>https://joseluisgs.dev/posts/2023/2023-07-10-reactive-api-rest-pt-iii.html</link>
      <guid>https://joseluisgs.dev/posts/2023/2023-07-10-reactive-api-rest-pt-iii.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Crea tu API REST reactiva con Kotlin y Ktor Parte III</source>
      <description>Cómo crear un servicio REST aplicando reactividad y consideraciones a tener en cuenta para ello</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Mon, 10 Jul 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Continuamos y finalizamos nuestro tutorial con esta tercera parte de cómo crear una API REST reactiva, siguiendo lo aprendido en la primera parte y en la segunda parte.</p>
<!-- more -->
<div class="hint-container warning">
<p class="hint-container-title">Sobre la traducción</p>
<p>Lamentablemente no tengo tiempo para traducir todos los artículos que escribo, pero este me pareció interesante y que podía aportar algo a la comunidad. Por ello, he usado ChatGPT y pulido los errores que me he encontrado. De nuevo te recomiendo la lectura del original en inglés.</p>
</div>
<h2>Introducción</h2>
<p>En esta tercera parte vamos a completar nuestro servicio API REST reactivo abordando los últimos temas avanzados que nos faltan por ver.</p>
<h2>Inyección de Dependencias con Koin</h2>
<p>Koin es un marco de inyección de dependencias pragmático y ligero escrito en Kotlin. Proporciona una forma nativa de Kotlin de inyectar tus dependencias sin generación de código.</p>
<h2>Conexiones Seguras con SSL/TLS</h2>
<p>La seguridad es fundamental en cualquier aplicación. Vamos a configurar SSL/TLS para asegurar nuestras conexiones.</p>
<h2>Autenticación y Autorización con JWT</h2>
<p>JSON Web Token (JWT) es un estándar abierto para transmitir información de forma segura entre partes. Vamos a implementar autenticación y autorización con JWT.</p>
<h2>Documentación con Swagger y OpenAPI</h2>
<p>Swagger es una especificación para describir APIs RESTful. OpenAPI es la evolución de Swagger. Vamos a documentar nuestro servicio.</p>
<h2>Despliegue con Docker</h2>
<p>Docker nos permite empaquetar nuestra aplicación con todas sus dependencias en un contenedor portable.</p>
<h2>Conclusión</h2>
<p>Hemos completado nuestra serie de tutoriales sobre cómo crear una API REST reactiva con Kotlin y Ktor. Ahora tienes una base sólida para seguir desarrollando.</p>
<p>Puedes encontrar el código de este proyecto en GitHub. No olvides darle una estrella para estar al tanto de nuevos tutoriales.</p>
<p>Las siguientes pistas ofrecidas por JetBrains Academy en Hyperskill pueden ser un punto de partida perfecto:</p>
<ul>
<li>Desarrollador Kotlin</li>
<li>Desarrollador <em>Backend</em> Kotlin (Ktor)</li>
</ul>
]]></content:encoded>
      <enclosure url="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*MNYTHm0fGgHP_UUalJuCUg.png" type="image/png"/>
    </item>
    <item>
      <title>Visita de Antonio Leiva y DevExpert.io al IES Luis Vives</title>
      <link>https://joseluisgs.dev/posts/2024/2024-01-03-vista-devexpert-luisvives.html</link>
      <guid>https://joseluisgs.dev/posts/2024/2024-01-03-vista-devexpert-luisvives.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Visita de Antonio Leiva y DevExpert.io al IES Luis Vives</source>
      <description>Compartiendo y aprendiendo con Antonio Leiva y DevExpert.io junto al alumnado</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Fri, 05 Jan 2024 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>El martes 7 de noviembre de 2023, Antonio Leiva, CEO de <a href="https://devexpert.io/" target="_blank" rel="noopener noreferrer">DevExpert.io</a>, visitó el IES Luis Vives para impartir una charla dirigida al alumnado de la familia de informática. Durante el evento, Leiva compartió nuevas tendencias y buenas prácticas en el desarrollo de <em>software</em> basado en arquitecturas de calidad ante un salón de actos completo de asistentes. Respondió a las preguntas de los estudiantes y brindó consejos para enfocar su aprendizaje y futura carrera profesional.</p>
<!-- more -->
<p>El martes 7 de noviembre de 2023, Antonio Leiva, CEO de DevExpert.io, visitó el IES Luis Vives para impartir una charla dirigida al alumnado de la familia de informática. La expectación era alta y el evento resultó ser todo un éxito, ya que el salón de actos se llenó por completo de alumnado y profesorado que querían disfrutar de la visita de Antonio.</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/8iR6GEl.png" type="image/png"/>
    </item>
    <item>
      <title>José Luis González, PhD.</title>
      <link>https://joseluisgs.dev/info/personal.html</link>
      <guid>https://joseluisgs.dev/info/personal.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">José Luis González, PhD.</source>
      <description>Soy José Luis, y aquí podrás conocer un poco más de mí</description>
      <category>Personal</category>
      <pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>👋 Me llamo José Luis González Sánchez. Soy <a href="https://joseluisgs.github.io/docs/info/investigacion/tesis.html" target="_blank" rel="noopener noreferrer"><strong>doctor en Informática especializado en desarrollo de software y sistemas interactivos</strong></a> 👨‍🎓 y <a href="https://www.iesluisvives.es/" target="_blank" rel="noopener noreferrer"><strong>profesor de Formación Profesional</strong></a> especializado en <strong>Desarrollo de Software</strong> 💻. Además, soy <a href="https://www.jetbrains.com/es-es/company/partners/kotlin/" target="_blank" rel="noopener noreferrer"><strong>Kotlin Trainer Certified by JetBrains</strong></a> y <a href="https://education.github.com/teachers/advisors" target="_blank" rel="noopener noreferrer"><strong>GitHub Campus Advisor</strong></a> 👨‍💻.</p>
<p>Mi foco principal es el desarrollo de <strong>aplicaciones web y multiplataforma</strong>, desde el servidor ⚙️ hasta el cliente 📱. Imparto docencia en másteres, doctorado y cursos de especialización en diseño, desarrollo y evaluación de productos de software. Me apasiona el ecosistema de <strong>Kotlin</strong>, <strong>.NET</strong> y <strong>Vue.js</strong> 💓.</p>
<!-- more -->
<p>Aparte de mi docencia diaria, imparto formación en cursos de máster/doctorado y programas de especialización o grupos de trabajo. Mi especialidad es el diseño, desarrollo y evaluación de la experiencia de usuario y productos interactivos, usabilidad, accesibilidad y, sobre todo, jugabilidad. Colaboro en distintos proyectos de investigación y desarrollo. Soy formador en nuevas tecnologías y metodologías de desarrollo de aplicaciones web, desarrollo de servicios, multiplataforma, web o móviles (siempre centradas en el usuario). Me encanta evaluar y poder ayudarte a mejorar tus productos interactivos y aprender de lo que haces 🙂.</p>
<p>Personalmente, he sido galardonado con distintos premios y distinciones a lo largo de mi carrera profesional, que podrás conocer en la sección de <a href="/investigacion/" target="_blank">Investigación</a>.</p>
<p>Me siento orgulloso de ser <a href="https://www.jetbrains.com/es-es/company/partners/kotlin/" target="_blank" rel="noopener noreferrer"><strong>Kotlin Trainer Certified by JetBrains</strong></a> y <a href="https://education.github.com/teachers/advisors" target="_blank" rel="noopener noreferrer"><strong>GitHub Campus Advisor</strong></a>. Te puedo ayudar a aplicar superpoderes para desarrollar nuestro código o a cómo aplicarlos a la docencia. Actualmente, soy uno de los responsables de contenidos en <a href="https://hyperskill.org/" target="_blank" rel="noopener noreferrer"><strong>Hyperskill</strong></a>/<a href="https://www.jetbrains.com/academy/" target="_blank" rel="noopener noreferrer"><strong>JetBrains Academy</strong></a> para tecnologías relacionadas con Kotlin. Será un placer echarte un cable con ello. ¡Cuenta conmigo! 💪</p>
<p style="text-align: center;">
  <a href="https://www.jetbrains.com/es-es/company/partners/kotlin/" target="_blank"> 
    <img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/Ca7Yu1B.png" alt="Log1" height="100" borderradius="1rem" boxshadow="0 5px 18px rgba(0,0,0,0.3)">
  </a> &nbsp;
  <a href="https://education.github.com/teachers/advisors" target="_blank"> 
    <img loading="lazy" style="border-radius: 0.25rem;" src="https://i.ytimg.com/vi/uWsXEmaM3PA/maxresdefault.jpg" alt="Log2" height="100" borderradius="1rem" boxshadow="0 5px 18px rgba(0,0,0,0.3)">
  </a>
</p>
<p>En esta página web podrás conocer un poco más de mí.</p>
<h2>Aficiones</h2>
<p>Aparte de enseñar y desarrollar, disfruto con la música, especialmente todo tipo de música rock 🎵 , me encanta el tenis 🎾, tocar la guitarra 🎸, jugar a videojuegos 🎮, leer 📚 , ver series/películas/anime 📺 y compartir buenos momentos (¿una caña y una buena charla?🍺). Me encanta seguir aprendiendo y seguir avanzando.</p>
<h2>Contacto</h2>
<p style="text-align: center;">
  Cualquier cosa que necesites házmelo saber por si puedo ayudarte 💬.
</p>
<p style="text-align: center;">
    <a href="https://joseluisgs.dev/" target="_blank">
        <img loading="lazy" src="https://raw.githubusercontent.com/joseluisgs/joseluisgs/master/images/social-icons/favicon.png" height="32">
    </a>&nbsp;
    <a href="https://github.com/joseluisgs" target="_blank">
        <img loading="lazy" src="https://raw.githubusercontent.com/joseluisgs/joseluisgs/master/images/social-icons/github.svg" height="32">
    </a>&nbsp;
    <a href="https://www.linkedin.com/in/JoseLuisGSDev" target="_blank">
        <img loading="lazy" src="https://raw.githubusercontent.com/joseluisgs/joseluisgs/master/images/social-icons/linkedin.png" height="32">
    </a>&nbsp;
    <a href="https://www.youtube.com/@joseluisgs" target="_blank">
        <img loading="lazy" src="https://raw.githubusercontent.com/joseluisgs/joseluisgs/master/images/social-icons/youtube.png" height="32">
    </a>&nbsp;
    <a href="https://x.com/JoseLuisGSDev" target="_blank">
        <img loading="lazy" src="https://raw.githubusercontent.com/joseluisgs/joseluisgs/master/images/social-icons/twitter.png" height="32">
    </a>&nbsp;
    <a href="https://www.instagram.com/joseluisgs.dev/" target="_blank">
        <img loading="lazy" src="https://raw.githubusercontent.com/joseluisgs/joseluisgs/master/images/social-icons/instagram.png" height="32">
    </a>
</p>
<h2>Una cita</h2>
<blockquote>
<p>«Programa siempre tu código como si el tipo que va a tener que mantenerlo en el futuro fuera un violento psicópata que sabe dónde vives».</p>
<p>Martin Goldin</p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://joseluisgs.dev/img/avatar.png" type="image/png"/>
    </item>
    <item>
      <title></title>
      <link>https://joseluisgs.dev/404.html</link>
      <guid>https://joseluisgs.dev/404.html</guid>
      <source url="https://joseluisgs.dev/rss.xml"></source>
      <description>404 - Not Found ← Volver al inicio</description>
      <pubDate>Thu, 16 Apr 2026 15:40:25 GMT</pubDate>
      <content:encoded><![CDATA[<div class="vp-404-content">
  <img src="/img/404.gif" alt="404 - Not Found">
  <a href="/" class="vp-404-btn">← Volver al inicio</a>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Regreso a .NET en DAW. 20 razones para el cambio</title>
      <link>https://joseluisgs.dev/posts/2025/2025-12-31-csharpnet_docencia_daw.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-12-31-csharpnet_docencia_daw.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Regreso a .NET en DAW. 20 razones para el cambio</source>
      <description>Reflexiones sobre la transición en el contexto de la nueva Formación Profesional de DAW y el auge de la IA.</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 31 Dec 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>En noviembre de 2021, escribí un artículo que muchos de vosotros recordáis: <strong><a href="https://joseluisgs.dev/blogs/2021/2021-11-28-kotlin-en-mi-equipo.html" target="_blank" rel="noopener noreferrer">«Kotlin siempre en mi equipo»</a></strong>. En aquel entonces, defendía a Kotlin como la herramienta pedagógica definitiva (y lo sigue siendo). Sigo pensando que su diseño es magistral para su uso en la docencia, pero los tiempos han cambiado. La <strong>Nueva Ley de FP</strong>, la presión por enviar alumnos a la empresa en primero (quien pueda) y la realidad de un mercado <em>backend</em> en España que sigue anclado en tecnologías de «gran empresa», me han obligado a realizar este cambio, con el objetivo de adaptar la enseñanza a las necesidades de esta nueva ley y su impacto en la docencia de primero (y segundo) de DAW, donde tenemos que buscar el equilibrio entre la enseñanza de conceptos fundamentales (mi principal objetivo) y la preparación para el mercado laboral y la Fase de Formación en Empresas.</p>
<p>Este curso, he decidido pasarme a la <strong>plataforma .NET con C#</strong>. No es una decisión tomada a la ligera. Es una respuesta crítica y meditada a los retos que tenemos como docentes en este nuevo panorama educativo y profesional.</p>
<!-- more -->
<p>Antes de terminar este año 2025 y comenzar el 2026, quiero compartir con vosotros las razones detrás de este cambio y cómo .NET no solo es una plataforma robusta y moderna, sino también una herramienta pedagógica excepcional.</p>
<p>Te voy a dejar mis 20 razones para este cambio de paradigma en la docencia de DAW.</p>
<h2>1. Nueva FP: ¿Por qué Kotlin ya no es suficiente?</h2>
<p>Aunque Kotlin es un lenguaje moderno y expresivo, capaz de tocar de manera clara todos los paradigmas y conceptos de la programación, su ecosistema sigue siendo más limitado. Es un lenguaje que ha experimentado un crecimiento significativo en los últimos años, especialmente en el desarrollo de aplicaciones Android y <em>backend</em> con Ktor o Spring Boot en todo el mundo. Sin embargo, su adopción en el mercado laboral español, especialmente en el ámbito empresarial, sigue siendo limitada en comparación con otras tecnologías más establecidas como .NET y JVM.</p>
<p>Antes, sin la fase de formación en empresas en primero, podía permitirme el lujo de enseñar Kotlin, centrándome en los conceptos fundamentales de la programación y dejando que los alumnos se adaptaran a las tecnologías específicas del mercado en segundo o durante sus prácticas. Kotlin es el mejor lenguaje puente para luego saltar a Java, C#, TypeScript, Swift o incluso Python. El objetivo era claro: enseñar a pensar en código, no en tecnologías específicas, y en segundo especializarnos en las tecnologías más demandadas con una base sólida y trabajada en primero.</p>
<p>El nuevo calendario de FP exige que los alumnos estén listos para formarse en empresas desde el primer año, con suerte. De hecho, ya no formamos tanto: se cede parte de esa formación a las empresas (y perdonad que siga sin creérmelo, por lo menos en esta especialidad y en estos días; ojalá en el futuro cambie). Aparte, tenemos el problema de las horas y si van todos los alumnos, si no van, adónde van, etc. Un caos didáctico que me obliga a priorizar y buscar el equilibrio entre seguir transmitiendo una base sólida de programación y preparar a los alumnos para que al menos puedan desenvolverse en la Fase de Formación en Empresas. Pero ya tenemos menos horas en primero y segundo, por lo que debo priorizar.</p>
<p>Debido a que yo no puedo asegurar la formación, ni horas ni a las empresas adonde van a ir los alumnos o simplemente saber qué contenidos trabajarán en la empresa (y si los van a trabajar realmente), debo asegurarme de que los conocimientos que adquieran en el aula sean directamente aplicables en el entorno laboral de una forma más directa. Y ahí es donde .NET brilla sobre Kotlin, por lo menos en España.</p>
<p><strong>C# y .NET</strong> ofrecen un equilibrio perfecto entre <strong>modernidad, robustez y demanda laboral</strong>. Permiten a los alumnos aprender conceptos fundamentales de programación (según mi punto de vista) mientras se preparan para un mercado que valora estas habilidades.</p>
<p>Quiero destacar que no soy un novato de C# y .NET; de hecho gané un premio con C# junto a mi equipo, Microsoft Imagine Cup España 2007 en la especialidad de «Software Design» <a href="https://www.eleconomista.es/empresas-finanzas/noticias/196598/04/07/Estudiantes-granadinos-representaran-a-Espana-en-la-Imagine-Cup-Microsoft.html" target="_blank" rel="noopener noreferrer">ver</a>, pasé a Kotlin porque es la leche, y soy formador/certificador con JetBrains a nivel mundial y ahora vuelvo a C#. He visto la evolución de ambos lenguajes y plataformas, y creo que .NET ha madurado de una manera que lo hace ideal para la enseñanza en DAW actual, bajo mi filosofía de aprendizaje. Creo que mi experiencia me permite hacer esta afirmación con fundamento y objetividad.</p>
<h2>Ventajas clave de C# y .NET para 1.º DAW</h2>
<h2>2. El «Muro de Entrada»: La pedagogía del punto de entrada</h2>
<p>Sabemos que Java es poderoso, pero que le cuesta evolucionar y que otros lenguajes han tomado la delantera en términos de características modernas. Sin embargo, uno de los mayores errores de Java ha sido su incapacidad para adaptarse a una enseñanza gradual, para ir conociendo todos los paradigmas y fundamentos algorítmicos. Para que un alumno escriba su primera línea de código en Java, tiene que realizar un «acto de fe» masivo. Tiene que escribir una clase, un método estático y entender qué es un <em>array</em> de <em>strings</em>.</p>
<p><strong>Java (El peso de la herencia):</strong>
Para explicar esto, tengo que adelantar conceptos de programación orientada a objetos (clases), visibilidad (<code>public</code>) y contexto estático (<code>static</code>) y ver que hay un <em>array</em> de <em>strings</em> (<code>String[] args</code>) cuando el alumno aún no sabe qué es una variable y solo quiere escribir su primera línea de código.</p>
<div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[] </span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">args</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">        System</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">out</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"¿Por qué tengo que escribir todo esto y para qué sirve?"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Incluso con las mejoras recientes (JEP 445), Java lucha por ocultar su complejidad, pero la estructura subyacente sigue asomando de forma prematura, enseñando lo que es un método <code>main</code> antes de que el alumno entienda la lógica básica.</p>
<div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">() {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    System</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">out</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Pero sigo necesitando un método main y no lo he visto antes"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>En cambio, <strong>C# con Top-Level Statements</strong> permite seguir el orden lógico del razonamiento humano: <strong>Estructurada -&gt; Modular -&gt; Objetos -&gt; Funcional</strong>. Empezamos con la lógica, y el resto viene después, cuando el alumno está preparado. Sin duda es más eficiente pedagógicamente: no necesitas ni un método <code>main</code>.</p>
<p><strong>C# (Moderno):</strong></p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Directo, limpio, sin ruido.</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Aquí empieza la lógica, no la burocracia"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><h3>Modularidad sin redundancia</h3>
<p>Mientras Java intenta simplificar su sintaxis, C# ya ofrece un ecosistema maduro donde la modularidad es limpia y potente. Un ejemplo claro es la gestión de parámetros: en lugar de obligar al alumno a aprender la <strong>sobrecarga de métodos</strong> (escribir el mismo método varias veces con distintos argumentos), C# utiliza <strong>parámetros opcionales y nombrados</strong>.</p>
<p>Esto no solo es más fácil de enseñar, sino que es más eficiente en el mundo real.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Una sola definición cubre múltiples escenarios</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Saludar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Mundo"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> veces</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    for</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> i</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> &#x3C;</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> veces</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">i</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">++</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"Hola, </span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">nombre</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">!"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Flexibilidad total en la llamada:</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Saludar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();                         </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Usa valores por defecto</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Saludar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Ana"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);                 </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Argumentos posicionales</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Saludar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">veces</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">5</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Luis"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">); </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Argumentos nombrados: claridad absoluta</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Además, C# permite un control granular de la memoria y la intención mediante modificadores de parámetros que Java no posee de forma equivalente:</p>
<ul>
<li><code>out</code>: Para devolver múltiples valores de forma explícita.</li>
<li><code>ref</code>: Para paso por referencia real (no solo de la referencia al objeto).</li>
<li><code>in</code>: Para asegurar que un parámetro es de solo lectura, optimizando el rendimiento en estructuras grandes.</li>
</ul>
<h2>3. Tipado fuerte y explícito desde el principio</h2>
<p>C# es un lenguaje fuertemente tipado, lo que significa que los tipos de datos son explícitos y claros desde el principio. Esto ayuda a los alumnos a comprender mejor cómo funcionan los datos y las operaciones en programación, así como a evitar errores comunes relacionados con tipos de datos y conversiones implícitas.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> numero</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 10</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Tipo explícito</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> texto</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Hola, Mundo"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Tipo explícito</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">double</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> decimalNumero</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 15.5</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Tipo explícito</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Inferencia de tipos con 'var'</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> otroNumero</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 20</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// El compilador infiere que es int</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> otroTexto</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Adiós, Mundo"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// El compilador infiere que es string</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>4. Seguridad ante nulos: Compilación frente a tiempo de ejecución</h2>
<p>Para mí esto es fundamental, y más aún si este control es a nivel de compilación. El <code>NullPointerException</code> es el cáncer del aprendizaje. Java intentó solucionarlo con <code>Optional</code>, pero es un parche verboso que ensucia el código. En C#, el control de nulos es nativo y ocurre en <strong>tiempo de compilación y de manera elegante</strong>.</p>
<p><strong>La verbosidad de Java con Optional:</strong></p>
<div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Optional</span><span style="--shiki-light:#E45649;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#E45649;--shiki-dark:#ABB2BF">></span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> nombre </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> Optional</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ofNullable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getNombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">());</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> resultado </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">map</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(String</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">::</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">toUpperCase).</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">orElse</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"SIN NOMBRE"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>La elegancia de C# (Null Safety nativo):</strong>
En C#, si una variable puede ser nula, lleva un <code>?</code>. Si no, el compilador te avisa. Es el mismo poder que me enamoró de Kotlin.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> resultado</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ToUpper</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() ?? </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"SIN NOMBRE"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// El compilador me protege</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><h2>5. Propiedades: encapsulamiento sin el ruido</h2>
<p>En Java, el encapsulamiento requiere escribir métodos <code>get</code> y <code>set</code>, lo que añade ruido y complejidad innecesaria, salvo que uses librerías de terceros como Lombok. C# introduce <strong>propiedades</strong> que permiten un acceso limpio y controlado a los campos, manteniendo el encapsulamiento sin la verbosidad, muy similar a Kotlin.</p>
<p><strong>Java (Verbosidad):</strong></p>
<div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    private</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> getNombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> nombre;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> setNombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">        this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> nombre;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>C# (Propiedades limpias):</strong></p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; } </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Encapsulamiento sin ruido</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Podemos indicar que una propiedad es de solo lectura o escritura, usando <code>get;</code> o <code>set;</code> según convenga, o si es inmutable usando <code>init;</code> o la visibilidad, por ejemplo, que no se pueda modificar desde fuera de la clase con <code>private set;</code>.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; } </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Solo lectura desde fuera</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">init</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; } </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Inmutable después de la inicialización</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Luis"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 20</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> };</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Finalmente, con las <em>Backing Fields</em>, podemos tener lógica adicional en las propiedades sin perder la simplicidad, o usar campos calculados si es necesario.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; } </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Solo lectura desde fuera</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">field</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // Podemos usar init para inmutabilidad después de la inicialización, pero lo dejamos con set para este ejemplo</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">            if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">value</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> &#x3C;</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">throw</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">ArgumentException</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"La edad no puede ser negativa"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">            field</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>6. Tuplas, estructuras, clases y registros: elección según la necesidad</h2>
<p>C# ofrece <strong>estructuras (<em>structs</em>)</strong> para datos ligeros (en el <em>stack</em>) y <strong>clases</strong> para objetos complejos, además de <strong>registros (<em>records</em>)</strong> para inmutabilidad y comparación por valor. Esto permite enseñar a los alumnos a elegir la herramienta adecuada según el caso, fomentando un diseño de <em>software</em> más consciente. Lo mejor de las clases y <em>data classes</em> de Kotlin juntos. Y además, los registros son inmutables por defecto, lo que fomenta buenas prácticas de programación funcional.</p>
<p>Si no, tienes las tuplas para datos temporales y ligeros, ideal para devolver múltiples valores sin crear una clase específica.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> struct</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Punto</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> X</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Y</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> record</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">); </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Inmutable y por valor</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> punto</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Punto</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">X</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 10</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Y</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 20</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> };</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Juan"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">20</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> otroAlumno</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> with { </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 21</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }; </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Nuevo objeto con cambio</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Tuplas para múltiples valores</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> suma</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> producto</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Calcular</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> b</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> b</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">a</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> *</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> b</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Usamos desestructuración</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (suma, producto) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Calcular</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">3</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">4</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>7. Inicializadores con propiedades o constructores con parámetros nombrados y valores por defecto</h2>
<p>C# permite inicializar objetos usando inicializadores de propiedades, lo que hace que el código sea más limpio y fácil de leer, de forma similar a las <em>data classes</em> de Kotlin.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">    Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Ana"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">    Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 22</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">};</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Además, los constructores en C# pueden tener parámetros nombrados y valores por defecto, lo que facilita la creación de objetos sin necesidad de múltiples sobrecargas de constructores, cosa que ya hemos visto antes.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Desconocido"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 18</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">        Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">        Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">22</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">); </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Nombre por defecto, Edad especificada</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Finalmente, tenemos constructores primarios, ideales para ir al grano cuando queremos definir clases inmutables o registros.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; } </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; } </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Luis"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">20</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>8. LINQ: el lenguaje de las colecciones que habla SQL</h2>
<p>Los <code>Streams</code> de Java son potentes, pero su sintaxis es alienígena para alguien que está aprendiendo bases de datos. <strong>LINQ</strong> en C# es el puente perfecto. Su similitud con SQL es tan grande que los alumnos aprenden ambos conceptos a la vez.</p>
<p><strong>Ejemplo comparativo de procesamiento de datos:</strong></p>
<p>Imagina esto en Java:</p>
<div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Streams: Verboso y menos intuitivo</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">List</span><span style="--shiki-light:#E45649;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#E45649;--shiki-dark:#ABB2BF">></span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> aprobadas </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> listaAlumnos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">stream</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">filter</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(a </span><span style="--shiki-light:#C18401;--shiki-dark:#C678DD">-></span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getNota</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">>=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 5</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">sorted</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">Comparator</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">comparing</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Alumno</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">::</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">getApellidos))</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">map</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(a </span><span style="--shiki-light:#C18401;--shiki-dark:#C678DD">-></span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> new</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> AlumnoDTO</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getNombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">+</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> " "</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getApellidos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(), </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getNota</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()))</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">collect</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">Collectors</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">());</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// LINQ: Parecido a SQL, fácil de razonar</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> aprobadas</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> listaAlumnos</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Where</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nota</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> >=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 5</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">OrderBy</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Apellidos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Select</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">AlumnoDTO</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nombre</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF"> {</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Apellidos</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nota</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">))</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ToList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Aparte de todo esto, C# y .NET ofrecen muchas colecciones y estructuras de datos listas para usar, lo que facilita la enseñanza de algoritmos y estructuras fundamentales sin tener que reinventar la rueda, ya sean listas, diccionarios, conjuntos, pilas o colas, todas con una sintaxis coherente y fácil de entender, además de sus versiones inmutables y concurrentes.</p>
<h2>9. Programación funcional, extensiones y <em>pattern matching</em></h2>
<p>Java mete la programación funcional con calzador. Es demasiado verboso y poco intuitivo.</p>
<p>C# la abraza, ofreciendo características que hacen que el código sea más expresivo y limpio como pasaba con Kotlin.</p>
<ul>
<li><strong>Funciones de extensión:</strong> me permiten «limpiar» el código y extender sin heredar. De lo que más usaba en Kotlin.</li>
<li><strong>Pattern Matching:</strong> permite descomponer y analizar datos de forma clara y concisa.</li>
<li><strong>Sobrecarga de operadores:</strong> permite definir cómo se comportan los operadores para tipos personalizados, haciendo el código más intuitivo.</li>
</ul>
<p><strong>C# (Poder expresivo):</strong></p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Función de extensión + Pattern Matching + Switch Expression</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Categorizar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">this</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> double</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> nota</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) => </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">nota</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> switch</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">    >=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 9</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Sobresaliente"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">    >=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 7</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Notable"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">    >=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 5</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Aprobado"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    _</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    => </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Suspenso"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Uso:</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">double</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> miNota</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 8.5</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">miNota</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Categorizar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()); </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Imprime: Notable</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>10. Genéricos con «guardas» (<em>constraints</em>)</h2>
<p>En Java, los genéricos son «borrados» en tiempo de ejecución (<em>type erasure</em>). En C#, son reales y, lo más importante, se pueden restringir. Esto permite enseñar <strong>diseño de <em>software</em></strong> de alto nivel.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Solo aceptamos tipos que sean clases y tengan constructor</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Almacen</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">where</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">class</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, new() </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> T</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> GetNuevo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() => new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">T</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>11. Interoperabilidad total con otras plataformas</h2>
<p>C# y .NET ofrecen una interoperabilidad excepcional con otras plataformas y lenguajes, lo que permite a los alumnos trabajar en entornos mixtos sin problemas. Esto es especialmente útil en proyectos empresariales donde la integración con sistemas existentes es común.</p>
<h2>12. Ficheros y serialización simplificada</h2>
<p>Al contrario de Java, que requiere librerías externas para muchas tareas comunes, C# y .NET incluyen soporte nativo para trabajar con ficheros y serialización (JSON, XML) de manera sencilla y directa tal y como ocurre en Kotlin.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Leer y escribir JSON nativo</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Luis"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 23</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> };</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">string</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> json</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> JsonSerializer</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Serialize</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnoRecuperado</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> JsonSerializer</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Deserialize</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">json</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>13. Bases de Datos</h2>
<p>Podemos usar las ventajas de ADO.NET para conexiones directas o <strong>Entity Framework Core</strong> para un ORM potente y moderno. EF Core permite trabajar con bases de datos relacionales usando LINQ, lo que facilita la enseñanza de conceptos de bases de datos y programación al mismo tiempo.</p>
<p><strong>Ejemplo con ADO.NET:</strong></p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">using</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> conexion</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">SqlConnection</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">cadenaConexion</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    conexion</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Open</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> comando</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">SqlCommand</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"SELECT * FROM Alumnos WHERE Edad > @edad"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">conexion</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    comando</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Parameters</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">AddWithValue</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"@edad"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">18</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    using</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> lector</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> comando</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ExecuteReader</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        while</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">lector</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Read</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">            Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">lector</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Nombre"</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">]}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> - </span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">lector</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Edad"</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">]}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>Ejemplo con Entity Framework Core:</strong></p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    [</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Key</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Id</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    [</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Required</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> string</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    [</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Range</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">120</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)]</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF"> Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> EscuelaContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">DbContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> DbSet</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#4078F2;--shiki-dark:#ABB2BF">Alumnos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">set</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">; }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> contexto</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">EscuelaContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnos</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> contexto</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Alumnos</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Where</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> ></span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 18</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ToList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>14. Interfaces de usuario: muchas opciones para todos los niveles</h2>
<p>C# y .NET ofrecen múltiples opciones para crear interfaces de usuario, desde aplicaciones de consola hasta aplicaciones web y de escritorio. Esto permite adaptar la enseñanza según el nivel del alumno y el contexto del proyecto transmitiendo conceptos de estado de la interfaz, eventos y, si es necesario, patrones de diseño como MVVM o MVC.</p>
<h2>15. <em>Testing</em> con NUnit y Moq</h2>
<p>C# tiene un ecosistema de <em>testing</em> muy maduro con <em>frameworks</em> como <strong>NUnit</strong> para pruebas unitarias y <strong>Moq</strong> para <em>mocking</em>. Esto facilita la enseñanza de buenas prácticas de desarrollo y asegura que los alumnos comprendan la importancia de las pruebas en el desarrollo de <em>software</em>.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">TestFixture</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> AlumnoServiceTests</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    [</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Test</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> TestRegistrarAlumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> servicio</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">AlumnoService</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> resultado</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> servicio</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Registrar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(new </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Test"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> });</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        Assert</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">IsTrue</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">resultado</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>Ventajas clave de C# y .NET para 2.º DAW</h2>
<p>Aquí viene el gran cambio, pues uno de mis objetivos era enseñar tres entornos: JVM (SpringBoot), .NET (ASP.NET Core) y PHP (Laravel). Y así fue hasta el año pasado. Este será el último año que lo haga; de hecho, ya ha desaparecido PHP. Por lo que solo me centraré en JVM y .NET a nivel de servicios y páginas web dinámicas. El motivo es que no hay tiempo: puedes tener alumnos que no han hecho la fase de formación en empresas de primero y a los que se les suman las horas de segundo. Un caos que de nuevo me obliga a priorizar. Más vale poco bien hecho que mucho mal hecho. Además, no salir de .NET me permite profundizar más y mejor en un solo ecosistema.</p>
<h2>16. Asincronía, reactividad y ROP: las bases del <em>backend</em> moderno</h2>
<p>Enseñarle a un alumno <code>CompletableFuture</code> en Java es invitarle a dejar la programación (sé que he exagerado), pero debes unirle lo que es el <code>ExecutorService</code>. En C#, el modelo <code>async/await</code> es tan transparente que la programación asíncrona deja de ser un «muro» para ser una herramienta más. Muy similar a lo que pueden encontrar en JavaScript/TypeScript.</p>
<p><strong>Asincronía limpia:</strong></p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> async</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Task</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">GetAlumnoAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">int</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> id</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">{</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> response</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> await </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">_client</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">GetAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"/api/alumnos/</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">id</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> await </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">response</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Content</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ReadFromJsonAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>Reactividad y LINQ:</strong>
Podemos usar colecciones asíncronas o reactivas dependiendo si queremos un patrón «pull» o «push». Además, con LINQ podemos transformar flujos de datos de manera declarativa.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Patrón Pull (IAsyncEnumerable):</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> async</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> IAsyncEnumerable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">GetAlumnosAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> response</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> await </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">_client</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">GetAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"/api/alumnos"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnos</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> await </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">response</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Content</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ReadFromJsonAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">List</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>>();</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    foreach</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> in</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        yield</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> return</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Patrón Push (IObservable):</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> IObservable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">GetAlumnosObservable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> Observable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Create</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">async</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> observer</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> response</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> await </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">_client</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">GetAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"/api/alumnos"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnos</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> await </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">response</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Content</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ReadFromJsonAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">List</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>>();</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        foreach</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> in</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">            observer</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">OnNext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">        observer</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">OnCompleted</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    });</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnosAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> GetAlumnosAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">().</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Where</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> >=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 18</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Select</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => new { </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> } );</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">await </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">foreach</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumno</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> in</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnosAsync</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nombre</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> - </span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Edad</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> alumnosObservable</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> GetAlumnosObservable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Where</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> >=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 18</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Select</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => new { </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nombre</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">a</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Edad</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumnosObservable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Subscribe</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Nombre</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> - </span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Edad</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">));</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>ROP: adiós al Try-Catch infinito</strong>
C# permite implementar el patrón de «vía de tren». Si algo falla, el flujo se desvía, pero el código sigue siendo una línea recta. Los errores de dominio no son excepciones: son datos.</p>
<div class="language-csharp line-numbers-mode" data-highlighter="shiki" data-ext="csharp" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-csharp"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Result</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Registrar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">AlumnoRequest</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> req</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) =></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Ensure</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ValidarDatos</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">))</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Bind</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">ComprobarDuplicados</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">))</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Bind</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">GuardarEnBD</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">))</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Bind</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">EnviarEmailBienvenida</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">r</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">))</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Tap</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Log</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Info</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"Alumno registrado: </span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Id</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">));</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> resultado</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> servicio</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Registrar</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">req</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Match</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    alumno</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"Alumno registrado: </span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">alumno</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Id</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">),</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">      error</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> => </span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">WriteLine</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">$"Error: </span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">{</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">error</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">Mensaje</span><span style="--shiki-light:#50A14F;--shiki-dark:#ABB2BF">}</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div>]]></content:encoded>
      <enclosure url="https://www.ryadel.com/wp-content/uploads/2020/09/asp-net-core-c-sharp-logo.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Desarrollo Web en Entorno Servidor 02 - Servicios Web con JVM y Spring Boot</title>
      <link>https://joseluisgs.dev/posts/2025/2025-10-24-dwes_ud_02_servicios_jvm_springboot.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-10-24-dwes_ud_02_servicios_jvm_springboot.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Desarrollo Web en Entorno Servidor 02 - Servicios Web con JVM y Spring Boot</source>
      <description>Programación avanzada de Java y desarrollo de servicios web utilizando Spring Boot sobre la JVM</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Fri, 24 Oct 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>El <strong>Desarrollo de Servicios Web en JVM</strong> con <strong>Spring Boot</strong> es el pilar del desarrollo <em>backend</em> moderno.</p>
<p>La <strong>Máquina Virtual de Java (JVM)</strong> es un <strong>estándar indiscutible</strong> en el desarrollo empresarial y de alto tráfico gracias a su <strong>rendimiento estable</strong>, su capacidad de <strong>escalabilidad horizontal</strong> (manejo de miles de hilos) y su robusto ecosistema de herramientas.</p>
<p><strong>Spring Boot</strong> es el <strong>framework de referencia</strong> en este ecosistema. Ofrece una solución completa para simplificar la configuración, implementar la <strong>Inyección de Dependencias (DI)</strong> y abordar de forma modular aspectos complejos como la <strong>Seguridad</strong>, el <strong>Acceso a Datos</strong> y la <strong>Comunicación Asíncrona</strong>. Su filosofía es la <strong>&quot;convención sobre la configuración&quot;</strong>, lo que acelera dramáticamente el desarrollo.</p>
<!-- more -->
<p>Esta unidad de <strong>2.º DAW</strong> te enseñará a construir APIs REST de nivel industrial, cubriendo desde la arquitectura Java hasta la optimización con caché Redis y la seguridad JWT.</p>
<h2>1. Proyecto del Curso 🚀</h2>
<p>El <strong>Proyecto</strong> es el eje central de la unidad. Aquí se integran todos los conocimientos adquiridos para construir una <strong>API REST completa y robusta</strong> de nivel de producción. Servirá como tu <strong>porfolio</strong> de <em>backend</em> profesional.</p>
<p><strong>Link al repositorio del Proyecto completo:</strong> <a href="https://github.com/joseluisgs/DesarrolloWebEntornosServidor-02-Proyecto-SpringBoot" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/DesarrolloWebEntornosServidor-02-Proyecto-SpringBoot</a></p>
]]></content:encoded>
      <enclosure url="https://puroguramu.akcstudio.com/spring-boot.png" type="image/png"/>
    </item>
    <item>
      <title>Programación 03 - Aplicación de Estructuras de Almacenamiento Estáticas</title>
      <link>https://joseluisgs.dev/posts/2025/2025-10-20-prog_ud_03_estrecturas_estaticas.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-10-20-prog_ud_03_estrecturas_estaticas.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Programación 03 - Aplicación de Estructuras de Almacenamiento Estáticas</source>
      <description>Arrays, Cadenas (String), Expresiones Regulares, y la base de los algoritmos de Ordenación y Búsqueda</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Las <strong>Estructuras de Almacenamiento Estáticas</strong> son la columna vertebral de la programación. En esta unidad, nos adentraremos en el manejo eficiente de datos mediante estructuras de tamaño fijo, centrándonos en los <strong>Arrays</strong> (vectores y matrices), la gestión avanzada de <strong>Cadenas de Texto</strong> y, finalmente, explorando los fundamentos de los <strong>Algoritmos de Ordenación y Búsqueda</strong> que nos permiten manipular y consultar estos datos.</p>
<p>Comprender la <strong>inmutabilidad</strong> y el modelo de <strong>paso por referencia</strong> de estas estructuras en el lenguaje C# es esencial para evitar errores comunes y escribir código robusto.</p>
<!-- more -->
<h2>1. Arrays: La Base del Almacenamiento Estático</h2>
<p>Un <strong>array</strong> es una estructura de datos fundamental que almacena una colección ordenada de elementos del <strong>mismo tipo</strong>.</p>
<h3>1.1. Características Clave</h3>
<p>Los arrays en DAW son estructuras de datos muy eficientes, caracterizadas por:</p>
<ul>
<li><strong>Homogeneidad (Tipo Fijo):</strong> Todos los elementos son del mismo tipo de dato.</li>
<li><strong>Tamaño Fijo (Inmutabilidad):</strong> El tamaño se establece al crearse y no puede alterarse. Si se requiere un tamaño diferente, se debe crear un <strong>nuevo array</strong> y <strong>copiar</strong> los datos.</li>
<li><strong>Contigüidad en Memoria:</strong> Los elementos se almacenan en posiciones de memoria contiguas, lo que garantiza su alta eficiencia.</li>
<li><strong>Acceso por Índice:</strong> El acceso a cualquier elemento se realiza mediante su índice, proporcionando un tiempo de acceso constante (<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>).</li>
</ul>
<h3>1.2. Indexación y Tipos de Referencia</h3>
<p>En C#, la indexación es <strong>cero-basada</strong>, es decir, el primer elemento se encuentra en el índice <code>0</code>. Acceder a un índice fuera de los límites (negativo o mayor o igual al tamaño) produce una excepción (<code>IndexOutOfRangeException</code>).</p>
<p>Crucialmente, los arrays son <strong>tipos de referencia</strong> en C#. Esto implica que:</p>
<ul>
<li>Al pasar un array a una función, se pasa una <strong>copia de su dirección de memoria</strong> (referencia).</li>
<li>Cualquier modificación a los elementos dentro de la función afecta al array <strong>original</strong>.</li>
<li>Para obtener una copia verdaderamente independiente, se debe realizar una <strong>Clonación Manual</strong> (copia profunda), creando un nuevo array y copiando los valores elemento por elemento.</li>
</ul>
<h2>2. Arrays Multidimensionales (Matrices) y Técnicas Avanzadas</h2>
<p>Los arrays multidimensionales (matrices) extienden el concepto del array unidimensional para manejar estructuras de datos con múltiples dimensiones, como tablas o rejillas (filas y columnas).</p>
<h3>2.1. Gestión de la Identidad y Clonación</h3>
<p>En las matrices, el concepto de referencia se vuelve más complejo.</p>
<ul>
<li><strong>Identidad (<code>==</code>)</strong>: El operador <code>==</code> solo compara si dos matrices apuntan a la <strong>misma dirección de memoria</strong> (Identidad), no si tienen el mismo contenido.</li>
<li><strong>Clonación Profunda</strong>: Una simple asignación o copia superficial resulta en una <strong>doble referencia</strong>. Para obtener una copia realmente independiente de una matriz, se requiere una <strong>Clonación Profunda Manual</strong>, que implica copiar tanto las referencias de las filas como los valores internos de cada elemento.</li>
</ul>
<h3>2.2. Doble Búfer (<code>Double Buffering</code>)</h3>
<p>El <code>Double Buffering</code> es una técnica avanzada esencial para la simulación y la animación.</p>
<ul>
<li><strong>Teoría</strong>: Consiste en usar dos matrices (<strong>búfer frontal</strong> y <strong>búfer trasero</strong>). Mientras el búfer frontal se muestra al usuario, los cálculos y modificaciones se aplican en el búfer trasero.</li>
<li><strong>Eficiencia</strong>: Una vez completado el cálculo, el <strong>mecanismo de intercambio (<code>Swap</code>)</strong> simplemente intercambia las referencias (punteros) de los dos búferes. Esta operación es de <strong>tiempo constante</strong> (<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>), mucho más eficiente que copiar todos los datos (que sería <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>).</li>
</ul>
<h2>3. Cadenas de Texto (<code>string</code>) y Expresiones Regulares</h2>
<p>Las cadenas de texto son, fundamentalmente, arrays de caracteres, pero con una característica crucial: la <strong>inmutabilidad</strong>.</p>
<h3>3.1. Inmutabilidad y Rendimiento con <code>StringBuilder</code></h3>
<ul>
<li><strong>Inmutabilidad</strong>: Una vez que una cadena (<code>string</code>) se ha creado, no puede ser modificada. Cada operación de concatenación (<code>+</code>) o modificación crea una <strong>nueva cadena</strong> en memoria.</li>
<li><strong>Rendimiento</strong>: Para operaciones intensivas de manipulación o concatenación de texto, el uso repetido del operador <code>+</code> es ineficiente.</li>
<li><strong><code>StringBuilder</code></strong>: Es una clase diseñada para construir cadenas de manera <strong>mutable</strong>. Almacena la cadena internamente y solo crea el objeto <code>string</code> final cuando se solicita, optimizando el rendimiento.</li>
</ul>
<h3>3.2. Expresiones Regulares (<code>Regex</code>)</h3>
<p>Las expresiones regulares son secuencias de caracteres que definen un patrón de búsqueda.</p>
<ul>
<li><strong>Funcionalidad</strong>: Se usan para tareas como la <strong>validación</strong> de formatos (ej. email, DNI), la <strong>búsqueda</strong> de patrones complejos y la <strong>sustitución</strong> de texto.</li>
<li><strong>Conceptos Clave</strong>: Se basan en <strong>metacaracteres</strong> que representan conjuntos de caracteres o cuantificadores (ej. <code>.</code> para cualquier carácter, <code>+</code> para una o más ocurrencias).</li>
</ul>
<h2>4. Algoritmos de Ordenación (<code>Sorting</code>) y Búsqueda (<code>Searching</code>)</h2>
<h3>4.1. Algoritmos de Ordenación</h3>
<p>La ordenación es el proceso de organizar los elementos de una colección siguiendo un orden específico (ascendente o descendente).</p>
<ul>
<li><strong>Burbuja (<code>Bubble Sort</code>)</strong>: Intercambia pares de elementos adyacentes si están en el orden incorrecto. Es simple pero ineficiente (<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>).</li>
<li><strong>Selección (<code>Selection Sort</code>)</strong>: Encuentra repetidamente el elemento mínimo (o máximo) y lo coloca en la posición inicial no ordenada. También ineficiente (<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>).</li>
<li><strong>Inserción (<code>Insertion Sort</code>)</strong>: Construye la matriz final un elemento a la vez, insertando el elemento actual en su posición correcta dentro de la sub-matriz ya ordenada.</li>
<li><strong>Rápida (<code>QuickSort</code>)</strong>: Utiliza una estrategia de &quot;divide y vencerás&quot;. Elige un elemento pivote y particiona la matriz en dos sub-arrays, con los elementos menores al pivote en un lado y los mayores en el otro. Es el método más rápido en promedio (<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n \log n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>).</li>
</ul>
<h3>4.2. Algoritmos de Búsqueda</h3>
<p>La búsqueda es el proceso de encontrar la ubicación de un elemento dentro de una colección.</p>
<ul>
<li><strong>Búsqueda Lineal (<code>Linear Search</code>)</strong>: Recorre la colección elemento por elemento hasta encontrar el valor deseado. Funciona en cualquier array, pero es lento (<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>).</li>
<li><strong>Búsqueda Binaria (<code>Binary Search</code>)</strong>: Requiere que la colección esté <strong>ordenada</strong>. Divide repetidamente a la mitad el intervalo de búsqueda. Es extremadamente rápida (<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\log n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>).</li>
</ul>
]]></content:encoded>
      <enclosure url="https://miro.medium.com/v2/resize:fit:1400/1*XcIydJ9zewURnwQJl9yo1w.png" type="image/png"/>
    </item>
    <item>
      <title>Entornos de Desarrollo 03 - Sistema de Control de Versiones con Git y GitHub</title>
      <link>https://joseluisgs.dev/posts/2025/2025-10-20-ed_ud_03_sistemas_control_versiones.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-10-20-ed_ud_03_sistemas_control_versiones.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Entornos de Desarrollo 03 - Sistema de Control de Versiones con Git y GitHub</source>
      <description>Fundamentos, comandos esenciales y metodologías para la colaboración eficiente con Git y GitHub</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Thu, 23 Oct 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>En el dinámico mundo del desarrollo de software, un pilar esencial para la eficiencia, la calidad y la colaboración es el <strong>Control de Versiones</strong>. Un sistema de control de versiones registra los cambios realizados en un conjunto de archivos a lo largo del tiempo, permitiendo a los desarrolladores recuperar versiones específicas de sus proyectos, rastrear la evolución del software y coordinar el trabajo de múltiples personas en archivos compartidos.</p>
<p>Este tema explora en profundidad cómo <strong>Git</strong> y <strong>GitHub</strong> han revolucionado la gestión de proyectos, desde los conceptos fundamentales hasta los comandos esenciales para optimizar tu flujo de trabajo.</p>
<!-- more -->
<h2>1. Fundamentos: Git y Conceptos Clave</h2>
<h3>1.1. ¿Qué es Git?</h3>
<p><strong>Git</strong> es un <em>software</em> de control de versiones <strong>distribuido</strong> (DVCS), creado por Linus Torvalds en 2005. A diferencia de los sistemas centralizados, Git proporciona a cada desarrollador una <strong>copia local completa</strong> del historial de desarrollo del proyecto. Esta naturaleza distribuida fomenta el desarrollo no lineal y agiliza la gestión de ramas y la fusión de diferentes versiones del código.</p>
<h3>1.2. El Ciclo de Vida y los Estados de un Archivo</h3>
<p>El flujo de trabajo básico en Git sigue una secuencia lógica de tres etapas:</p>
<ol>
<li><strong>Modificar</strong>: Se realizan cambios en el <strong>Directorio de Trabajo</strong> (la copia de los archivos en tu máquina local).</li>
<li><strong>Preparar (<code>Staged</code>)</strong>: Los archivos modificados se añaden al <strong>Área de Preparación (<code>Staging Area</code> o <code>Index</code>)</strong>, que es la zona intermedia donde se seleccionan los cambios que se incluirán en el próximo <em>commit</em>.</li>
<li><strong>Confirmar (<code>Committed</code>)</strong>: Se realiza un <code>commit</code>, que almacena la instantánea de los archivos preparados de forma permanente en el <strong>Repositorio Local</strong>.</li>
</ol>
<p>| Concepto Clave               | Descripción                                                                                                                                               |
| :</p>
]]></content:encoded>
      <enclosure url="https://miro.medium.com/1*b21FyqUbowHYAOQDXH0tDw.jpeg" type="image/jpeg"/>
    </item>
    <item>
      <title>Despliegue de Aplicaciones Web 03 - Arquitectura Web y Fundamentos</title>
      <link>https://joseluisgs.dev/posts/2025/2025-10-20-daw_ud_03_arquitectura_web_despliegue.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-10-20-daw_ud_03_arquitectura_web_despliegue.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Despliegue de Aplicaciones Web 03 - Arquitectura Web y Fundamentos</source>
      <description>Arquitectura web, APIs, protocolos y despliegue moderno</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>El <strong>Despliegue de Aplicaciones Web</strong> es el proceso fundamental que garantiza que una aplicación pase del código fuente a un entorno de producción accesible. Este proceso no solo se trata de subir archivos, sino de asegurar la <strong>estabilidad</strong>, <strong>seguridad</strong> y <strong>escalabilidad</strong> del sistema para los usuarios finales.</p>
<p>En este tema, exploraremos la estructura detrás de cada acción web, desde el <strong>Modelo Cliente-Servidor</strong> hasta las arquitecturas modernas, pasando por los componentes clave como el <strong>Protocolo HTTP</strong> y las <strong>APIs</strong> que hacen posible el desarrollo web contemporáneo.</p>
<!-- more -->
<h2>1. Componentes Clave y Roles en el Desarrollo Web</h2>
<p>El desarrollo web se divide conceptualmente para gestionar la complejidad de la interacción entre el usuario y el servidor.</p>
<ul>
<li><strong>Front-end (Lado del Cliente)</strong>: Es la parte visible y con la que el usuario interactúa directamente. Se construye con <strong>HTML</strong>, <strong>CSS</strong> y <strong>JavaScript</strong>, y su ejecución ocurre en el navegador del usuario.</li>
<li><strong>Back-end (Lado del Servidor)</strong>: Es el cerebro de la aplicación, responsable de la <strong>lógica de negocio</strong>, la <strong>interacción con bases de datos</strong> y la generación de <strong>contenido dinámico</strong>. Tecnologías como PHP, Java, Python o C# se usan aquí.</li>
</ul>
<p>La tendencia moderna es que el Back-end sea <strong>agnóstico</strong> o <strong>universal</strong>, exponiendo su funcionalidad a través de <strong>APIs (Application Programming Interfaces)</strong> que pueden ser consumidas por cualquier cliente, ya sea web, móvil o de escritorio.</p>
]]></content:encoded>
      <enclosure url="https://thumbs.dreamstime.com/b/web-software-developer-banner-seacrh-bar-thin-line-objects-work-tools-desk-59453071.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Entornos de Desarrollo 02 - Entornos de Desarrollo (IDEs)</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-25-ed_ud_02_entornos_desarrollo.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-25-ed_ud_02_entornos_desarrollo.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Entornos de Desarrollo 02 - Entornos de Desarrollo (IDEs)</source>
      <description>Herramientas esenciales para programar de manera eficiente</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>El <strong>Entorno de Desarrollo Integrado (IDE)</strong> es, sin duda, la herramienta más importante en el día a día de un desarrollador. Más que un simple editor de texto, es una aplicación diseñada para <strong>facilitar la tarea de codificación</strong> y hacer que el ciclo de vida del software sea mucho más ágil y eficiente.</p>
<p>En esta segunda unidad del módulo, exploraremos qué define un IDE, cuáles son sus componentes esenciales y te guiaremos en la instalación y configuración de las herramientas que utilizaremos en el curso: <strong>IntelliJ IDEA</strong>, <strong>JetBrains Rider</strong> y <strong>Visual Studio Code (VS Code)</strong>.</p>
<!-- more -->
<h2>1. ¿Qué es un IDE?</h2>
<p>Un IDE no es solo un editor; es una suite integrada de herramientas. Entender sus componentes te permite aprovechar al máximo sus capacidades:</p>
<ul>
<li><strong>Editor de Código Fuente:</strong> Herramienta principal con <strong>Resaltado de Sintaxis</strong> y <strong>Autocompletado de Código</strong> (o <strong>IntelliSense</strong> en VS Code).</li>
<li><strong>Compilador/Intérprete:</strong> Se encarga de traducir el código fuente a código legible para la máquina, permitiendo la ejecución directa.</li>
<li><strong>Depurador (<em>Debugger</em>):</strong> Esencial para probar y corregir errores. Permite detener la ejecución con <strong>puntos de ruptura</strong> (<em>Breakpoints</em>) y examinar el estado de las variables.</li>
<li><strong>Control de Versiones:</strong> Integración nativa con sistemas como <strong>Git</strong> para gestionar los cambios del código a lo largo de la construcción de la aplicación.</li>
<li><strong>Terminal Integrada y Plugins:</strong> La terminal ofrece acceso a herramientas externas, mientras que los <strong>plugins</strong> extienden la funcionalidad del IDE, brindando la modularidad necesaria.</li>
</ul>
<h2>2. Preparación del Entorno: Herramientas Clave</h2>
<p>Para el curso, configuraremos una estación de trabajo robusta con las siguientes herramientas:</p>
<h3>Entornos de Desarrollo (IDE)</h3>
<ul>
<li><strong>JetBrains (IntelliJ IDEA y Rider):</strong> Ambos comparten la misma <strong>filosofía de uniformidad</strong> en su flujo de trabajo y la gestión de <strong>Tool Windows</strong>. Se recomienda la instalación a través de la <strong>Toolbox App</strong> de JetBrains para gestionar fácilmente actualizaciones y diferentes versiones.</li>
<li><strong>Visual Studio Code (VS Code):</strong> Un editor/IDE ligero, organizado en torno a un <em>workspace</em> y altamente personalizable mediante <strong>extensiones</strong>. Su herramienta de navegación central es la <strong>Paleta de Comandos</strong> (<code>Ctrl+Shift+P</code>).</li>
</ul>
<h3>Plataformas de Desarrollo (SDK)</h3>
<p>Instalar los Kits de Desarrollo es un paso fundamental, ya que son la base para que el IDE pueda compilar y ejecutar el código:</p>
<ul>
<li><strong>JDK 21 (Java Development Kit)</strong>.</li>
<li><strong>.NET 8 (SDK/Runtime)</strong>.</li>
</ul>
<h3>Herramientas de Apoyo</h3>
<ul>
<li><strong>Git y GitKraken:</strong> Git es esencial para el Control de Versiones, y GitKraken lo complementa con una interfaz visual intuitiva.</li>
<li><strong>Personalización Visual:</strong> Se recomienda la instalación de fuentes como <strong>Fira Code</strong> o <strong>JetBrains Mono</strong> y la herramienta <strong>Oh My Posh</strong> para personalizar la terminal y mejorar la experiencia de codificación.</li>
</ul>
<h2>Enlaces de interés:</h2>
<p><strong>Vídeo resumen del tema</strong></p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/ExmTL4x6mhc" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Podcast del tema</strong></p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/_C0GPbvw1dA" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Link al repositorio del módulo:</strong> <a href="https://github.com/joseluisgs/EntornosDesarrollo-02-2025-2026" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/EntornosDesarrollo-02-2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://images.shiksha.com/mediadata/ugcDocuments/images/wordpressImages/2023_07_IDE-full-form-1.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Despliegue de Aplicaciones Web 02 - Introducción a la virtualización</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-23-daw_ud_02_introduccion_virtualizacion_docker.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-23-daw_ud_02_introduccion_virtualizacion_docker.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Despliegue de Aplicaciones Web 02 - Introducción a la virtualización</source>
      <description>La tecnología que ha revolucionado la forma de desarrollar y desplegar software</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Sun, 21 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>La segunda unidad didáctica del módulo de Despliegue de Aplicaciones Web se centra en la virtualización con contenedores, una tecnología que ha cambiado las reglas del juego en el mundo del desarrollo y despliegue de aplicaciones. El objetivo principal es solucionar la inconsistencia de entornos que a menudo lleva a la clásica frase: &quot;Pero si en mi máquina funciona&quot;.</p>
<!-- more -->
<p>El problema fundamental es que cada entorno es un mundo, con diferentes versiones de software, librerías y configuraciones de red, lo que hace que las aplicaciones sean impredecibles. Los contenedores de Docker solucionan esto al empaquetar una aplicación con todo lo que necesita para funcionar en una unidad aislada y estandarizada.</p>
<h2>Contenidos clave de la unidad</h2>
<p>En esta unidad, aprenderás los conceptos esenciales de Docker y cómo aplicarlos en el desarrollo y despliegue de aplicaciones, incluyendo:</p>
<p>Diferencia entre contenedores y máquinas virtuales (VM): Los contenedores son mucho más ligeros y eficientes porque comparten el <em>kernel</em> del sistema operativo del anfitrión, mientras que una VM simula un ordenador completo y es más pesada.</p>
<p>Componentes principales de Docker: Descubrirás los dos pilares fundamentales:</p>
<ul>
<li>Imágenes: La plantilla o la receta inmutable que contiene todo lo necesario para la aplicación.</li>
<li>Contenedores: La instancia viva y en ejecución de una imagen, donde reside y funciona la aplicación.</li>
<li>Comandos básicos de Docker CLI: Conocerás los comandos esenciales para gestionar el ciclo de vida de los contenedores, como <code>docker run</code> para poner en marcha un contenedor, <code>docker ps</code> para ver los activos, <code>docker pull</code> para descargar imágenes y <code>docker stop</code> y <code>docker rm</code> para detenerlos y borrarlos.</li>
<li>Creación de imágenes con Dockerfile: Aprenderás a crear tus propias imágenes personalizadas de forma reproducible y transparente. El Dockerfile es un simple archivo de texto con una lista de instrucciones paso a paso para construir la imagen.</li>
<li>Orquestación con Docker Compose: Verás cómo Docker Compose simplifica el despliegue de aplicaciones multicontenedor (como un servidor web y una base de datos) usando un único archivo de configuración (<code>docker-compose.yml</code>) y un solo comando.</li>
</ul>
<p>Esta forma de trabajar no solo proporciona portabilidad, consistencia y eficiencia, sino que también promueve la reproducibilidad, eliminando el problema de que &quot;solo funciona en mi máquina&quot;.</p>
<p>Enlaces de interés:</p>
<p><strong>Vídeo resumen del tema</strong></p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/ScgZLt8Ek8Y" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Podcast del tema</strong></p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/-8EoCawmjuw" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Link al repositorio del módulo:</strong> <a href="https://github.com/joseluisgs/DespliegueAplicacionesWeb-02-2025-2026" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/DespliegueAplicacionesWeb-02-2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://www.nextgencodingcompany.com/research/docker.png" type="image/png"/>
    </item>
    <item>
      <title>Programación 02 - Programación Estructurada y Modular</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-23-prog_ud_02_programacion_estructurada_modular.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-23-prog_ud_02_programacion_estructurada_modular.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Programación 02 - Programación Estructurada y Modular</source>
      <description>Los pilares para un desarrollo de software robusto</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>La segunda unidad didáctica del módulo de Programación se centra en dos paradigmas fundamentales para cualquier desarrollador: la Programación Estructurada y la Programación Modular. El dominio de estos conceptos es crucial para crear programas que sean no solo funcionales, sino también claros, fáciles de mantener y escalables.</p>
<!-- more -->
<p>La programación estructurada busca crear programas más limpios y legibles mediante el uso de tres estructuras de control básicas: secuencia, selección (condicionales) e iteración (bucles). Por su parte, la programación modular se basa en la técnica &quot;Divide y Vencerás&quot; para dividir un programa grande en partes más pequeñas y manejables, llamadas módulos, que resuelven problemas específicos.</p>
<h2>Contenidos clave de la unidad</h2>
<p>En este tema, aprenderás los siguientes conceptos esenciales:</p>
<ol>
<li>Programación Estructurada: Conocerás los tres tipos de estructuras de control que forman la base de la programación:</li>
</ol>
<ul>
<li>Secuencias: Las instrucciones se ejecutan una tras otra en el orden en que están escritas.</li>
<li>Condicionales: Se usan para tomar decisiones y ejecutar código de forma selectiva (<em>if</em>, <em>if-else</em>, <em>switch</em>).</li>
<li>Bucles: Permiten repetir un bloque de código. Se abordan bucles definidos (<em>for</em>) e indefinidos (<em>while</em>, <em>do-while</em>), así como mecanismos de control como las banderas (<em>flags</em>) y los centinelas.</li>
</ul>
<ol start="2">
<li>Programación Modular: Aprenderás a dividir el código en módulos reutilizables para facilitar la organización y la colaboración. Los módulos se implementan como funciones (que devuelven un valor) y procedimientos (que no devuelven nada).</li>
</ol>
<ul>
<li>Paso de parámetros: Entenderás la diferencia clave entre el paso por valor (se pasa una copia del dato) y el paso por referencia (se pasa la dirección de memoria de la variable original con la palabra clave <code>ref</code>).</li>
<li>Ámbito de las variables: Descubrirás dónde pueden ser accesibles las variables, distinguiendo entre el ámbito global y el local.</li>
<li>Técnicas avanzadas: Se exploran otros conceptos como los parámetros por defecto, la sobrecarga de funciones, el uso de parámetros variables (<em>params</em>) y la recursividad, una técnica en la que una función se llama a sí misma.</li>
</ul>
<ol start="3">
<li>Control de Excepciones: Verás cómo utilizar los bloques <em>try</em>, <em>catch</em> y <em>finally</em> para manejar errores inesperados de forma controlada. También se incluye el uso de la sentencia <em>throw</em> para lanzar excepciones de forma manual y las aserciones (<em>assert</em>) para depurar el código.</li>
</ol>
<p>Enlaces de interés:</p>
<p><strong>Vídeo resumen del tema</strong></p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/EHGiJiN9kEY" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Podcast del tema</strong></p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/oO7GtR0G1AQ" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Link al repositorio del módulo:</strong> <a href="https://github.com/joseluisgs/Programacion-02-2025-2026" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/Programacion-02-2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSp_-Y7sdX-XyYQ9HUsAqExFaQ3qdLdRApItw&amp;s" type="image/"/>
    </item>
    <item>
      <title>Despliegue de Aplicaciones Web - 01 Control de Versiones y Documentación</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-16-daw_ud_01_control_versiones_documentacion.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-16-daw_ud_01_control_versiones_documentacion.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Despliegue de Aplicaciones Web - 01 Control de Versiones y Documentación</source>
      <description>Las bases para un desarrollo colaborativo y sostenible</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Fri, 26 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>La primera unidad didáctica del módulo de Despliegue de Aplicaciones Web se centra en dos pilares fundamentales para el desarrollo de software profesional y colaborativo: el control de versiones y la documentación del código. Estos conceptos no solo son esenciales para mantener la integridad y la calidad del <em>software</em>, sino que también facilitan la colaboración entre equipos de desarrollo, permitiendo un flujo de trabajo más eficiente y organizado.</p>
<!-- more -->
<h3>UD1 - Desarrollo, control de versiones y documentación</h3>
<p>El tema destaca que el control de versiones es un sistema esencial que registra los cambios en un proyecto a lo largo del tiempo, permitiendo a los desarrolladores rastrear la evolución del <em>software</em>, revertir errores y coordinar el trabajo en equipo de manera eficiente.</p>
<p><strong>Puntos clave de la unidad:</strong></p>
<ul>
<li><strong>Git y GitHub:</strong> La unidad explora en profundidad el uso de <strong>Git</strong>, un <em>software</em> de control de versiones distribuido, y <strong>GitHub</strong>, una plataforma que facilita la colaboración.</li>
<li><strong>Conceptos esenciales de Git:</strong> Se explican conceptos cruciales como <strong>repositorio</strong>, <strong>commit</strong>, <strong>rama</strong>, <strong>área de preparación</strong> y el <strong>directorio de trabajo</strong>.</li>
<li><strong>Comandos de Git:</strong> El documento detalla los comandos esenciales para la configuración inicial, creación de repositorios, gestión de cambios (<code>add</code>, <code>commit</code>, <code>status</code>), historial de revisiones (<code>log</code>, <code>show</code>), deshacer cambios (<code>revert</code>, <code>reset</code>), ramificación y fusión (<code>branch</code>, <code>checkout</code>, <code>merge</code>, <code>rebase</code>), ignorar archivos (<code>.gitignore</code>) y el trabajo con repositorios remotos (<code>push</code>, <code>pull</code>, <code>fetch</code>).</li>
<li><strong>Colaboración con GitHub:</strong> Se cubren las herramientas clave de colaboración de GitHub, como las <strong>Pull Requests</strong>, que son esenciales para la revisión de código, y los <strong>Forks</strong>, que permiten a los desarrolladores contribuir a proyectos de código abierto.</li>
<li><strong>Flujos de trabajo:</strong> Se presentan dos de los flujos de trabajo más comunes: <strong>GitHub Flow</strong>, un modelo simple y ágil, y <strong>GitFlow</strong>, una metodología más estructurada para proyectos complejos.</li>
<li><strong>Documentación de Código:</strong> El segundo pilar de la unidad es la documentación. El documento explica cómo usar <strong>Markdown</strong> para la documentación general, y cómo generar documentación técnica directamente desde el código usando herramientas como <strong>Javadoc</strong> para Java y <strong>XMLdoc</strong> para C#.</li>
</ul>
<p>Este tema es crucial para que los estudiantes adquieran las herramientas necesarias para trabajar en un entorno profesional, asegurando que el código no solo sea funcional, sino también colaborativo y bien documentado.</p>
<p>Tienes un vídeo introductorio del tema en el siguiente enlace:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/c7h95IZX46Q" frameborder="0" allowfullscreen></iframe>
</p>
<p>El podcast del tema en el siguiente enlace:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/HX2gSuX0oow?si=IREAw7yYbvHjSxdZ" frameborder="0" allowfullscreen></iframe>
</p>
<p>Puedes encontrar todos los materiales y ejemplos de código relacionados con esta unidad en el siguiente repositorio de GitHub:</p>
<p><strong>Link al repositorio del módulo:</strong> <a href="https://github.com/joseluisgs/DespliegueAplicacionesWeb-01-2025-2026" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/DespliegueAplicacionesWeb-01-2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://www.arcadsoftware.com/arcad/wp-content/uploads/2024/01/banner-transition-git-source-code-advantages.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Desarrollo Web en Entorno Servidor 01 - Introducción a la Programación de Software en Servidor</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-16-dwes_ud_01_introduccion_programacion_servidor.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-16-dwes_ud_01_introduccion_programacion_servidor.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Desarrollo Web en Entorno Servidor 01 - Introducción a la Programación de Software en Servidor</source>
      <description>Asentando las bases del desarrollo en el lado del servidor</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Fri, 19 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>La primera unidad del módulo de Desarrollo Web en Entornos Servidor es una inmersión en los conceptos fundamentales que sustentan las aplicaciones web modernas. Se centra en el <em>backend</em>, la parte de una aplicación que se ejecuta en el servidor y gestiona la lógica de negocio y los datos, a diferencia del <em>front-end</em>, que es la interfaz visible para el usuario en el navegador.</p>
<!-- more -->
<h3>UD1 - Introducción a la Programación de Software en Servidor</h3>
<p>En esta unidad, se abordan varios temas clave que son esenciales para comprender cómo funcionan las aplicaciones web desde el lado del servidor y cómo se integran con el <em>front-end</em>.</p>
<p><strong>Puntos Clave de la Unidad:</strong></p>
<ul>
<li><strong>Modelos de Ejecución</strong>: La unidad diferencia entre el código que se ejecuta en el lado del cliente (<em>Front-end</em>, con tecnologías como HTML, CSS y JavaScript) y el que se ejecuta en el lado del servidor (<em>Back-end</em>, con lenguajes como PHP, Java o Python).</li>
<li><strong>Front-end y Back-end</strong>: El documento resalta la división de roles entre el desarrollador <em>front-end</em>, enfocado en la interfaz de usuario, y el desarrollador <em>back-end</em>, centrado en la lógica de negocio y la interacción con bases de datos. Además, se explica cómo el <em>back-end</em> se ha vuelto &quot;agnóstico&quot;, exponiendo su funcionalidad a través de <strong>APIs</strong> que pueden ser consumidas por cualquier tipo de cliente, como aplicaciones web o móviles.</li>
<li><strong>Arquitecturas Web</strong>: Se exploran diferentes modelos arquitectónicos, como la <strong>Arquitectura Cliente-Servidor</strong> y la evolución de la web (de la Web 1.0 a la 3.0). Se describen arquitecturas de software como la <strong>monolítica</strong>, la de <strong>capas</strong>, los <strong>microservicios</strong> (con el ejemplo de Netflix) y la <strong>serverless</strong>, comparando sus características en una tabla. También se presenta el patrón <strong>Modelo-Vista-Controlador (MVC)</strong>, que es un pilar en el desarrollo web del lado del servidor.</li>
<li><strong>Protocolos y Servicios</strong>: La unidad detalla el funcionamiento del protocolo <strong>HTTP</strong> y <strong>HTTPS</strong>, incluyendo sus métodos (GET, POST, etc.) y códigos de estado. También se aborda el concepto de <strong>Servicio Web</strong> y la comunicación a través de <strong>APIs</strong>.</li>
<li><strong>Despliegue y Seguridad</strong>: Finalmente, se introduce el concepto de <strong>despliegue</strong> de una aplicación web, sus objetivos (accesibilidad, estabilidad, escalabilidad y seguridad), y las tecnologías asociadas, como los <strong>contenedores (Docker)</strong>, la <strong>computación en la nube</strong> y la <strong>Integración Continua / Despliegue Continuo (CI/CD)</strong>.</li>
</ul>
<p>Este tema es crucial para entender el funcionamiento de cualquier aplicación web moderna y los diferentes roles y tecnologías que se utilizan en su creación y mantenimiento.</p>
<p>Puedes ver un vídeo resumen del tema en el siguiente enlace y consultar el código de ejemplo en el repositorio de GitHub:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/8hMghl24sP8" frameborder="0" allowfullscreen></iframe>
</p>
<p>El podcast del tema en el siguiente enlace:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/SJaedJqq8pE?si=3l_nlNvB4ekchZLX" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Link al repositorio del módulo:</strong> <a href="https://github.com/joseluisgs/DesarrolloWebEntornosServidor-01-2025-2026" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/DesarrolloWebEntornosServidor-01-2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://www.shutterstock.com/image-vector/software-development-coding-process-concept-600nw-1396210841.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Entornos de Desarrollo - 01 Desarrollo de Software</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-16-ed_ud_01_desarrollo_software.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-16-ed_ud_01_desarrollo_software.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Entornos de Desarrollo - 01 Desarrollo de Software</source>
      <description>Los pilares de la programación profesional</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Thu, 18 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Antes de sumergirnos en las herramientas y técnicas, es esencial entender el proceso completo que hay detrás de la creación de una aplicación. La Unidad Didáctica 1: Desarrollo de Software nos proporciona esa base sólida. Aquí no solo se aprende a escribir código, sino a entender todo el ciclo de vida de una aplicación, desde la idea inicial hasta su retirada del mercado.</p>
<!-- more -->
<h3>UD1: Desarrollo de Software: Los pilares de la programación profesional</h3>
<p>Antes de sumergirnos en las herramientas y técnicas, es esencial entender el proceso completo que hay detrás de la creación de una aplicación. La <strong>Unidad Didáctica 1: Desarrollo de Software</strong> nos proporciona esa base sólida. Aquí no solo se aprende a escribir código, sino a entender todo el ciclo de vida de una aplicación, desde la idea inicial hasta su retirada del mercado.</p>
<p>El <strong>desarrollo de software</strong> es un proceso complejo y estructurado. Abarca la disciplina de planificar, diseñar, construir y mantener sistemas, lo que garantiza que los programas sean eficientes, seguros y que satisfagan las necesidades del usuario final.</p>
<h3>El ciclo de vida del software: Un viaje en 8 fases</h3>
<p>Comprender el <strong>ciclo de vida del software</strong> es crucial. Es la hoja de ruta que nos guía en cada proyecto, y sus fases son un estándar en la industria.</p>
<ol>
<li><strong>Planificación</strong>: Aquí se define el <strong>qué</strong> y el <strong>porqué</strong> del proyecto, estableciendo objetivos, alcance y viabilidad.</li>
<li><strong>Análisis</strong>: En esta fase se recopilan los <strong>requisitos funcionales y no funcionales</strong> del cliente. El resultado es el documento de <strong>Especificación de Requisitos del Software (ERS)</strong>, un contrato entre cliente y desarrollador.</li>
<li><strong>Diseño</strong>: Una vez que sabemos lo que hay que hacer, esta fase define el <strong>cómo</strong>. Se descompone el sistema en módulos y se toman decisiones sobre la arquitectura, la base de datos y el lenguaje de programación.</li>
<li><strong>Codificación</strong>: La fase donde el diseño se transforma en <strong>código fuente</strong> en un lenguaje de programación. Es la tarea del programador y debe ser modular, legible y eficiente.</li>
<li><strong>Pruebas</strong>: El objetivo es encontrar y corregir defectos. Incluye pruebas unitarias, de integración, funcionales y estructurales.</li>
<li><strong>Documentación</strong>: Fundamental para el mantenimiento y la reutilización del <em>software</em>. Se crean guías técnicas, manuales de usuario y de instalación.</li>
<li><strong>Explotación (Despliegue)</strong>: Se instala y configura la aplicación en el entorno final del cliente para que pueda empezar a usarla.</li>
<li><strong>Mantenimiento</strong>: La fase más larga, donde el <em>software</em> se adapta, corrige y mejora a lo largo del tiempo.</li>
</ol>
<h3>Metodologías: De lo clásico a lo ágil</h3>
<p>El tema destaca la evolución de las metodologías de desarrollo. Aunque los modelos clásicos como el de <strong>cascada</strong> o el <strong>modelo en V</strong> son secuenciales y rígidos, el curso se enfoca en las <strong>metodologías ágiles</strong>, más modernas y flexibles. Estas se basan en el <strong>Manifiesto Ágil</strong>, que prioriza la colaboración con el cliente y la adaptación al cambio sobre la rigidez de un plan.</p>
<p>Además, se estudian enfoques como <strong>Scrum</strong>, que usa iteraciones cortas llamadas <em>sprints</em> para entregar valor de forma continua, y <strong>Kanban</strong>, que gestiona el flujo de trabajo de forma visual y orientada a la demanda.</p>
<h3>Lenguajes, traductores y herramientas</h3>
<p>El tema también profundiza en conceptos clave como los <strong>lenguajes de programación</strong> (desde los de bajo nivel como el ensamblador, hasta los de alto nivel como Java o Python), el proceso de <strong>traducción de código</strong> (compilación, interpretación y modelos mixtos), y el papel de las <strong>máquinas virtuales</strong> y los <strong>frameworks</strong> para la portabilidad y el desarrollo rápido de aplicaciones.</p>
<p>Finalmente, se presentan las <strong>herramientas de apoyo</strong>, como los <strong>IDE</strong> (Entornos de Desarrollo Integrado), que agrupan todas las utilidades para aumentar la productividad.</p>
<p>Puedes ver el vídeo introductorio del tema en el siguiente enlace y consultar el código de ejemplo en el repositorio de GitHub:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/GDCJGR5XERA" frameborder="0" allowfullscreen></iframe>
</p>
<p>El podcast del tema en el siguiente enlace:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/KybhDIWZj44?si=nOQNzSV6uCXPr24u" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Link al repositorio del módulo:</strong> <a href="https://github.com/joseluisgs/EntornosDesarrollo-01-2025-2026" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/EntornosDesarrollo-01-2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://t4.ftcdn.net/jpg/04/19/26/97/360_F_419269782_9LsP3TQndMVnZ2j3ZhTPhMjaqQpFAth9.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Programación 01 - Introducción a la Programación Software</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-16-prog_ud_01_introduccion_programacion.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-16-prog_ud_01_introduccion_programacion.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Programación 01 - Introducción a la Programación Software</source>
      <description>Los pilares de la programación profesional y el desarrollo de software</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>El primer tema del módulo de Programación se centra en sentar las bases teóricas y prácticas del desarrollo de software. Va más allá de escribir código para introducir los conceptos esenciales que todo programador debe dominar. Ahondaremos en cómo pasar de algo tan abstracto como una idea o un problema a una solución concreta y funcional mediante la programación y las bases iniciales de un lenguaje de programación.</p>
<!-- more -->
<h3>UD1 - Introducción a la Programación Software</h3>
<p><strong>Fundamentos de la Programación y Algoritmos</strong>
La unidad comienza definiendo <strong>qué es programar</strong>: el proceso de crear <em>software</em>, que abarca desde la concepción de una idea hasta su implementación. Un elemento central es el <strong>algoritmo</strong>, una serie de pasos claros y ordenados para resolver un problema de forma genérica e independiente de cualquier lenguaje. Se exploran las características esenciales de los algoritmos, como ser finitos, precisos y definidos, y se resalta la diferencia fundamental entre un algoritmo (la idea) y un programa (la implementación en un lenguaje específico).</p>
<p><strong>Lenguajes y Paradigmas de Programación</strong>
La segunda parte del resumen se enfoca en los <strong>lenguajes de programación</strong>. Se detallan conceptos fundamentales de un lenguaje (léxico, sintaxis y semántica) y se hace un recorrido por los principales <strong>paradigmas de programación</strong>, como la programación imperativa, orientada a objetos (POO), declarativa y funcional. El documento también clasifica los lenguajes según su nivel de abstracción (bajo, medio y alto nivel), su mecanismo de traducción (compilados, interpretados y mixtos) y su sistema de tipado (fuerte, débil, estático y dinámico).</p>
<p><strong>Elementos Fundamentales</strong>
Finalmente, la unidad introduce los elementos básicos de cualquier programa, como la estructura (<code>Main()</code>), las reglas de sintaxis, los <strong>tipos de datos</strong> (<code>int</code>, <code>decimal</code>, <code>string</code>, <code>bool</code>), las <strong>variables</strong>, las <strong>constantes</strong> y los <strong>literales</strong>. Estos conceptos son cruciales para empezar a escribir código de forma estructurada y legible.</p>
<p>Puedes ver un resumen en vídeo de este tema en el canal del profesor y encontrar todo el material en su repositorio de GitHub:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/55elQsN0mF4" frameborder="0" allowfullscreen></iframe>
</p>
<p>El podcast del tema en el siguiente enlace:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/V1btrIVQvyw?si=oZ5Ww4eT93aqgjAz" frameborder="0" allowfullscreen></iframe>
</p>
<p><strong>Link al repositorio del módulo:</strong> <a href="https://github.com/joseluisgs/Programacion-01-2025-2026" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs/Programacion-01-2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://t3.ftcdn.net/jpg/04/57/93/78/360_F_457937822_6guyVRMr4cCdCr36zPg7Er7WRaf0FbSn.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Arranca el curso de Despliegue de Aplicaciones Web 2025-2026</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_depliegue_web.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_depliegue_web.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Arranca el curso de Despliegue de Aplicaciones Web 2025-2026</source>
      <description>Describimos el inicio del curso de Despliegue de Aplicaciones Web en 2.º DAW</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Sun, 07 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>El ciclo de Desarrollo de Aplicaciones Web va mucho más allá de escribir código funcional. El verdadero desafío y el broche de oro de cualquier proyecto es el despliegue. Aquí es donde el módulo de Despliegue de Aplicaciones Web se convierte en la pieza fundamental para cualquier futuro profesional del sector. Describimos a continuación qué nos espera en este curso académico.</p>
<!-- more -->
<p>El objetivo central de este módulo es formarnos en las competencias necesarias para implantar, gestionar y mantener aplicaciones web en entornos de producción. No solo se trata de que una aplicación funcione en nuestro ordenador, sino de asegurar su funcionamiento en servidores reales, con la fiabilidad y seguridad que el mercado exige.</p>
<h1>Una ruta didáctica inspirada en el ciclo de vida profesional</h1>
<p>La programación de este módulo está diseñada para simular un flujo de trabajo profesional, priorizando el aprendizaje de herramientas y metodologías que son estándar en la industria. El temario va de lo general a lo específico, comenzando con las prácticas más relevantes antes de profundizar en los detalles técnicos del despliegue.</p>
<p>Aquí tienes un vistazo a las unidades didácticas que componen el curso:</p>
<ul>
<li>UD1: Introducción a la virtualización: Se sientan las bases de tecnologías como Docker y la virtualización de servidores en la nube y contenedores, fundamentales para crear entornos de trabajo aislados y replicables.</li>
<li>UD2: Control de versiones y documentación: Se aborda el uso de Git y GitHub, herramientas colaborativas que nos permiten gestionar la evolución del código y mantener una documentación precisa y accesible.</li>
<li>UD3: Arquitectura Web y Fundamentos del Despliegue: Una unidad teórica-práctica para comprender las bases de las arquitecturas web y los protocolos sobre los que se sustenta el funcionamiento de un servidor.</li>
<li>UD4: Despliegue de Páginas y Aplicaciones Web: Es el corazón del módulo. Aquí se aprende a instalar y configurar servidores web y de aplicaciones, creando sitios virtuales y asegurando las comunicaciones con certificados digitales.</li>
<li>UD5: Despliegue de Sistema de Transferencia de Ficheros: Se profundiza en la administración de servidores de archivos, aprendiendo a garantizar la disponibilidad y seguridad del servicio.</li>
<li>UD6: Verificación y Validación: Esta unidad subraya la importancia de las pruebas. Se analizan los servicios de red implicados en la ejecución de las aplicaciones, como los sistemas de nombres jerárquicos y los servicios de directorio.</li>
<li>UD7: Integración y Despliegue Continuo (CI/CD): El broche de oro. Se aprende a automatizar los flujos de trabajo con herramientas de integración continua, un proceso que representa el estándar de la industria DevOps.</li>
</ul>
<h1>La metodología: Construyendo conocimiento de forma iterativa</h1>
<p>El curso se apoya en el Aprendizaje Basado en Retos y Proyectos, donde resolveremos problemas reales del sector donde se promueve una metodología de aprendizaje iterativo e incremental. Esto significa que los conocimientos se refuerzan continuamente y cada unidad se construye sobre la base de la anterior, garantizando una comprensión profunda y aplicable.</p>
<p>Como puedes ver en el vídeo a continuación, la metodología se centra en que el alumno sea el protagonista de su propio aprendizaje, investigando y resolviendo desafíos prácticos.</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/phRvxD-IdCc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</p>
<p>El módulo no es un bloque aislado, sino que actúa como un puente que conecta el desarrollo con la puesta en producción, proporcionando las herramientas de operación y calidad necesarias para llevar las aplicaciones a un estado productivo, fiable y seguro. Los proyectos que se desarrollen en los módulos de Desarrollo Web en Entorno Servidor y Cliente, por ejemplo, se gestionarán y desplegarán utilizando las competencias adquiridas aquí.</p>
<p>Para que no te pierdas ni un solo detalle, todos los materiales y el código de ejemplo estarán disponibles en el siguiente repositorio de GitHub, el cual será nuestro manual de referencia durante todo el curso.</p>
<p>Link al repositorio del módulo: <a href="https://github.com/joseluisgs/DespliegueAplicacionesWeb-00-2025-2026" target="_blank" rel="noopener noreferrer">GitHub - Despliegue de Aplicaciones Web DAW 2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://www.unir.net/wp-content/uploads/2022/11/concepto-de-computaciC3B3n-en-nube-como-grupo-de-iconos.jpg_s1024x1024wisk20c2_ZA4fNdzAughsN2BhqGVRbvrVgxwJOgPyuuhS8bkJU.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Arranca el curso de Desarrollo Web en Entorno Servidor 2025-2026</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_desarrollo_web_servidor.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_desarrollo_web_servidor.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Arranca el curso de Desarrollo Web en Entorno Servidor 2025-2026</source>
      <description>Describimos el inicio del curso de Desarrollo Web en Entorno Servidor en 2.º DAW</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Desarrollo Web en Entorno Servidor (DWES) se erige como un pilar fundamental en el ciclo de Desarrollo de Aplicaciones Web, con el objetivo de dotar a los estudiantes de las habilidades y conocimientos necesarios para diseñar, construir, implementar y mantener aplicaciones del lado del servidor. Este curso profundiza en los conceptos esenciales del desarrollo web y la creación de servicios y aplicaciones que impulsan la funcionalidad de los sitios en la web. Mostramos a continuación qué nos espera en este curso académico.</p>
<!-- more -->
<h1>Unidades Didácticas: El camino hacia la maestría del servidor</h1>
<p>La programación didáctica de este módulo está diseñada para un aprendizaje progresivo y coherente. A continuación, se detallan las unidades que nos guiarán a través de las complejidades del desarrollo en el servidor:</p>
<ul>
<li>UD1: Introducción al desarrollo de software en Servidor: Se establecen los fundamentos teóricos, como los modelos de ejecución de código en el servidor y el cliente, y se introducen las tecnologías y los <em>frameworks</em> más relevantes para la programación web en este entorno.</li>
<li>UD2: Desarrollo de servicios web en JVM: Se profundiza en las tecnologías que permiten el acceso a bases de datos y en la creación de servicios web reutilizables. Se aprende a programar y verificar la funcionalidad de los servicios, a consumirlos y a documentarlos adecuadamente.</li>
<li>UD3: Desarrollo de páginas web dinámicas en JVM: En esta unidad, se aprende a incrustar código en lenguajes de marcas para crear páginas dinámicas. Se enseñan las estructuras de programación, el manejo de formularios, el mantenimiento del estado de las aplicaciones y la separación de la lógica de negocio y la presentación.</li>
<li>UD4: Desarrollo de servicios web en .NET: Similar a la UD2, esta unidad aplica los mismos conceptos de bases de datos y servicios web, pero utilizando el entorno y las tecnologías de .NET.</li>
<li>UD5: Desarrollo de páginas web dinámicas en .NET: De forma análoga a la UD3, esta unidad se centra en la generación de páginas web dinámicas en el entorno de .NET, incluyendo la validación de formularios y la modificación dinámica de contenidos.</li>
</ul>
<h1>Una metodología práctica y orientada a la industria</h1>
<p>La metodología del curso se apoya en el Aprendizaje Basado en Retos y Proyectos, con un enfoque en la resolución de problemas reales. En lugar de un libro de texto, se utilizarán recursos digitales gratuitos como vídeotutoriales y documentación técnica. Esto nos acostumbra a buscar información de forma autónoma, una habilidad vital en el mundo profesional.</p>
<p>El módulo está diseñado para construir el conocimiento de forma iterativa, permitiendo que las habilidades se consoliden y refuercen continuamente. El módulo establece una transversalidad vertical y horizontal con otras asignaturas del ciclo, por lo que los proyectos que se desarrollen aquí se integrarán con los de otros módulos como Desarrollo Web en Entorno Cliente y Despliegue de Aplicaciones Web.</p>
<p>Aquí puedes ver el vídeo introductorio del módulo:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/tlRgLmopS1g" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</p>
<p>Todos los materiales del curso, incluyendo los apuntes y proyectos, se encuentran disponibles en el siguiente repositorio de GitHub, que sirve como un recurso central para el aprendizaje y la colaboración:</p>
<p>Link al repositorio del módulo: <a href="https://github.com/joseluisgs/DesarrolloWebEntornosServidor-00-2025-2026" target="_blank" rel="noopener noreferrer">GitHub - Desarrollo Web en Entorno Servidor DAW 2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://media.geeksforgeeks.org/wp-content/uploads/20221123153249/SkillsRequiredtoBecomeaBackendDeveloper.png" type="image/png"/>
    </item>
    <item>
      <title>Arranca el curso de Entornos de Desarrollo 2025-2026</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_entornos_desarrollo.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_entornos_desarrollo.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Arranca el curso de Entornos de Desarrollo 2025-2026</source>
      <description>Describimos el inicio del curso de Entornos de Desarrollo en 1.º DAW</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Tue, 09 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Si el módulo de Programación nos enseña a &quot;hablar&quot; con la máquina, el módulo de Entornos de Desarrollo nos enseña a hacerlo de forma profesional, eficiente y colaborativa. Este curso no se limita a las herramientas; se centra en el ciclo de vida completo del <em>software</em>, una habilidad clave en cualquier equipo de desarrollo. Veamos qué nos espera en este emocionante viaje.</p>
<!-- more -->
<p>El objetivo principal de este módulo es dotarte de los conocimientos y las destrezas necesarias para que puedas enfrentarte al desarrollo de aplicaciones de principio a fin, desde la planificación hasta la puesta en marcha. Se hace un gran hincapié en el diseño orientado a objetos, las mejores prácticas en pruebas de <em>software</em>, y el uso de sistemas de control de versiones.</p>
<h1>Unidades didácticas</h1>
<p>El temario de este módulo está estructurado de manera progresiva, garantizando que cada concepto construya sobre el anterior.</p>
<ul>
<li>
<p>UD1: Desarrollo de Software: Una introducción a los cimientos teóricos. Aquí se definen los conceptos de programa informático y se analizan las fases del ciclo de desarrollo de una aplicación, incluyendo las metodologías ágiles.</p>
</li>
<li>
<p>UD2: Entornos de Desarrollo de Software: Aprenderás a dominar los IDEs (Entornos de Desarrollo Integrados). Esto no es solo instalar, sino también personalizar, automatizar y usar estas herramientas para generar ejecutables.</p>
</li>
<li>
<p>UD3: Sistemas de Control de Versiones: Fundamental para el trabajo en equipo. Se profundiza en el uso de Git y GitHub, los estándares de la industria para el desarrollo colaborativo y el control del código a lo largo del tiempo.</p>
</li>
<li>
<p>UD4: Diseño Orientado a Objetos: Diagrama de Clases: La planificación es clave. Utilizarás herramientas específicas para diseñar la arquitectura de tus proyectos, creando diagramas de clases y generando código a partir de ellos (y viceversa).</p>
</li>
<li>
<p>UD5: Diseño Orientado a Objetos: Diagramas de Comportamiento: Complementando al anterior, aprenderás a modelar el comportamiento de las aplicaciones a través de diagramas de casos de uso, interacción, actividades y estados.</p>
</li>
<li>
<p>UD6: Optimización y Refactorización: Esta unidad se centra en mejorar la calidad y la eficiencia del código sin alterar su funcionalidad. Se abordarán patrones de refactorización y el uso de analizadores de código.</p>
</li>
<li>
<p>UD7: Diseño y Realización de Pruebas: La validación es esencial. Aprenderás a definir casos de prueba, usar herramientas de depuración y a implementar pruebas automáticas para garantizar la fiabilidad de tu <em>software</em>.</p>
</li>
</ul>
<h1>Un enfoque práctico y conectado con la industria</h1>
<p>La metodología del curso se basa en el Aprendizaje Basado en Retos y Proyectos y el concepto de Aula Invertida. Esto significa que, en lugar de clases magistrales, te enfrentarás a problemas reales del sector, utilizando tutoriales y documentación para resolverlos, mientras el tiempo en clase se dedica a la práctica y la colaboración.</p>
<p>Aquí puedes ver el vídeo introductorio al módulo:</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/VoamKywLez8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</p>
<p>Link al repositorio del módulo: <a href="https://github.com/joseluisgs/EntornosDesarrollo-00-2025-2026" target="_blank" rel="noopener noreferrer">GitHub - Entornos de Desarrollo DAW 2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://tech-radix.com/wp-content/uploads/2022/03/Mob-App.webp" type="image/webp"/>
    </item>
    <item>
      <title>Arranca el curso de Programación 2025-2026</title>
      <link>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_programacion.html</link>
      <guid>https://joseluisgs.dev/posts/2025/2025-09-15-arranca_programacion.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Arranca el curso de Programación 2025-2026</source>
      <description>Describimos el inicio del curso de Programación en 1.º DAW</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 10 Sep 2025 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>¡Hola a todos! Estamos emocionados de comenzar un nuevo curso en el ciclo de Desarrollo de Aplicaciones Web. Este año, el módulo de Programación se presenta más interesante que nunca, con un enfoque fresco y alineado con las últimas tendencias del sector tecnológico.</p>
<!-- more -->
<h1>Un temario moderno y práctico</h1>
<p>Olvídate de los temarios anticuados. Nuestro plan de estudios se ha actualizado para que domines los conceptos más relevantes en el mundo del desarrollo de <em>software</em>. Aprenderemos a manejar las herramientas y tecnologías que realmente se usan en las empresas, y no solo teoría.</p>
<h1>El viaje a través del temario: Las 10 unidades clave</h1>
<p>El temario de este módulo está diseñado como un camino evolutivo, comenzando por los fundamentos y avanzando hacia la creación de <em>software</em> complejo y moderno. Las unidades se han secuenciado de forma lógica para que asimiles cada concepto antes de pasar al siguiente.</p>
<p>A continuación, te damos más detalles sobre lo que verás en cada una de ellas:</p>
<ul>
<li>
<p>UD1: Introducción a la programación de software: En esta unidad se sientan las bases de todo el curso. Aprenderás qué es un programa, los conceptos de algoritmos, y los elementos fundamentales como variables, constantes y operadores. Es el punto de partida para que te familiarices con la sintaxis y la estructura de un programa.</p>
</li>
<li>
<p>UD2: Programación estructurada y modular: Aquí te sumergirás en la lógica de programación. Trabajarás con estructuras de control como <em>if-else</em> y <em>for</em>. Además, aprenderás a escribir y depurar código, a manejar excepciones y a crear funciones, lo que te permitirá empezar a organizar tus programas de forma más eficiente.</p>
</li>
<li>
<p>UD3: Aplicación de las estructuras de almacenamiento: Esta unidad se enfoca en cómo manipular y almacenar datos. Verás la gestión de matrices (<em>arrays</em>) y la manipulación de cadenas de texto. Aprenderás sobre expresiones regulares, muy útiles para buscar patrones en textos. También tendrás una primera toma de contacto con algoritmos básicos de ordenación y búsqueda.</p>
</li>
<li>
<p>UD4: Programación Orientada a Objetos: El corazón del curso. Conocerás los conceptos de objeto, clase, métodos y propiedades. Aprenderás a crear tus propias clases y a instanciar objetos, entendiendo los principios de la programación orientada a objetos (POO) y cómo utilizar librerías externas.</p>
</li>
<li>
<p>UD5: Programación Avanzada Orientada a Objetos: Una vez dominada la base de la POO, esta unidad te lleva al siguiente nivel. Profundizarás en conceptos como la herencia, las interfaces y la composición. Aprenderás a diseñar jerarquías de clases y a aplicar principios avanzados que te permitirán crear <em>software</em> más escalable y robusto.</p>
</li>
<li>
<p>UD6: Programación funcional, Genérica y Colecciones: Un tema muy moderno y demandado. En esta unidad, trabajarás con colecciones de datos, como las listas, y utilizarás iteradores para procesar información.</p>
</li>
<li>
<p>UD7: Lectura y escritura de información externa. Ficheros: En esta unidad aprenderás a interactuar con el exterior. Verás cómo leer y escribir información en ficheros, manejar diferentes formatos de intercambio de datos como CSV, JSON y XML.</p>
</li>
<li>
<p>UD8: Operaciones con Bases de Datos de Objetos y Colecciones: Te introducirás en el mundo de las bases de datos. Aprenderás las características de las bases de datos orientadas a objetos, cómo instalarlas y a programar aplicaciones para almacenar, recuperar, actualizar y eliminar información.</p>
</li>
<li>
<p>UD9: Gestión de Bases de Datos Relacionales: Una unidad esencial para cualquier desarrollador. Aquí te centrarás en las bases de datos relacionales, la forma más común de gestionar información en el mundo laboral. Programarás conexiones, escribirás código para almacenar y recuperar datos, y gestionarás información de manera eficiente.</p>
</li>
<li>
<p>UD10: Programación Interactiva e Interfaces Gráficas: Para cerrar el ciclo, esta unidad te enseña a crear interfaces de usuario sencillas. Aprenderás a programar controladores de eventos y a escribir programas que usen una interfaz gráfica para la entrada y salida de información, dándole a tus proyectos una cara visible para los usuarios.</p>
</li>
</ul>
<p>La metodología: Aprender haciendo
La clave de este módulo es la metodología de aprendizaje. No nos quedaremos en la teoría. La mayoría de las clases se centrarán en la resolución de retos y casos prácticos, que son la mejor forma de consolidar conocimientos. Como verás en el vídeo a continuación, el profesor nos animará a preguntar y a trabajar en grupo para resolver problemas, fomentando un ambiente de colaboración y apoyo mutuo.</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/wKCdgacEr4Q" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</p>
<p>Tu compañero de viaje: El repositorio de GitHub
Para que no pierdas el hilo de lo que se hace en clase, todos los materiales y ejemplos de código estarán disponibles en un repositorio de GitHub. Esto no solo facilita el acceso, sino que también nos introduce en el uso de una herramienta fundamental para cualquier desarrollador. Podrás consultar el código, ver los cambios y, lo que es mejor, contribuir y hacer tus propios proyectos.</p>
<p>Link al repositorio del módulo: <a href="https://github.com/joseluisgs/Programacion-00-2025-2026" target="_blank" rel="noopener noreferrer">GitHub - Programación DAW 2025-2026</a></p>
]]></content:encoded>
      <enclosure url="https://mytasker.com/assets/images/WD-D-banner.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Podcast en Algoritmo Salvaje</title>
      <link>https://joseluisgs.dev/posts/2024/2024-07-31-podcast-algoritmo-salvaje.html</link>
      <guid>https://joseluisgs.dev/posts/2024/2024-07-31-podcast-algoritmo-salvaje.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Podcast en Algoritmo Salvaje</source>
      <description>Hablando un poco de la nueva FP y de DAW-DAM</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 31 Jul 2024 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Hace muy poquito tuve el placer de conocer y hablar con la gente de <a href="https://www.algoritmosalvaje.com/" target="_blank" rel="noopener noreferrer">Algoritmo Salvaje</a>. En este pódcast hemos hablado un poco de la nueva FP con lo que conocemos y de cómo serán las novedades DAW-DAM en este nuevo curso 2024-2025. Ha sido un placer poder compartir un rato con ellos y poder hablar de la nueva FP y mostrar, con lo que conocemos actualmente y mi visión personal, cómo será el nuevo ciclo de DAW-DAM.</p>
<!-- more -->
<p>Te dejo el enlace a su vídeo en <a href="https://www.youtube.com/watch?v=RGW3oqZf0BY" target="_blank" rel="noopener noreferrer">YouTube</a> y te invito a que te suscribas a su canal y a su pódcast. El equipo de Algoritmo Salvaje es un equipo de profesionales que se dedican a llevarte al siguiente nivel en el mundo del desarrollo y compartir su experiencia con todos nosotros. Sobre todo, si eres alumno de DAW o DAM, te puede interesar su contenido. Te invito a que te suscribas y disfrutes del resto de los pódcast que vendrán en su canal.</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/RGW3oqZf0BY?si=GSl7nS6qSF1aZWMj" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/RaFcO83.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Ajustando biorritmos y cosas del día a día</title>
      <link>https://joseluisgs.dev/posts/2024/2024-01-04-ajustando-biorritmos.html</link>
      <guid>https://joseluisgs.dev/posts/2024/2024-01-04-ajustando-biorritmos.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Ajustando biorritmos y cosas del día a día</source>
      <description>Los cambios no son tan fáciles como parecen</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Thu, 04 Jan 2024 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Esta primera parte del curso 2023/2024 no solo ha traído los cambios de turno de mañana a tarde en el centro, sino diversas consecuencias que han hecho que ajustemos los biorritmos, siguiendo una de las bromas que he tenido por ello. Te comento estos cambios.</p>
<!-- more -->
<p>Cambiar de turno a veces no es tan fácil como uno imagina. En mi caso ha venido unido a multitud de cambios y ajustes que han hecho el proceso un poco más complicado de lo habitual.</p>
<p>Se dice que es bueno cambiar de costumbres en el entorno laboral cada dos años. En el fondo, ir al turno de tarde ha tenido ese efecto. Aunque sea el mismo centro, implica conocer nuevas personas, adaptarse a nuevos horarios y cambiar determinados hábitos docentes. Por supuesto, ha implicado preparar un nuevo módulo: <a href="https://github.com/joseluisgs/DesarrolloWebEntornosServidor-00-2023-2024" target="_blank" rel="noopener noreferrer">Desarrollo Web en Entornos Servidor</a>. En este aspecto, he querido centrarme en varias tecnologías revisitando los contenidos del currículo con ellas y agrupándolas en varios grupos: JVM (y SpringBoot), Node.js (y Nest.js) y finalmente PHP (y Laravel). Esperemos que el experimento salga bien. En el fondo, es un buen aliciente y una buena responsabilidad que me ha gustado.</p>
<p>El turno de tarde es más tranquilo y agradable. A veces es como si no estuvieses en el mismo centro. Agradezco a todos mis compañeros/as que me hayan ayudado al cambio.</p>
<p>Pero este cambio ha supuesto cambiar muchas cosas en mi día a día. Y aunque no lo parezca, una de las cosas que más me han costado es el cambio de los entrenamientos de tenis. Esto hace que ahora tenga que ir por las mañanas y me ha costado ajustar todo. Quizás porque me gustaba ir por la tarde cuando ya &quot;todas las obligaciones docentes estaban hechas&quot;. Ahora vas con la cabeza pensando en lo que debes hacer esa tarde. Esto para mí es duro y a veces no lo disfruto, porque entre &quot;bola y bola&quot; tengo una petición REST en la cabeza por resolver o explicar para que entiendan el poder de los JWT. De hecho, la broma de los &quot;cambios de biorritmos&quot; salió de mi entrenador del CT Alborada, Miguel Ángel, y le doy las gracias por ello. Primero por su ayuda en todo el proceso, y luego por darme la excusa perfecta de mi lamentable estado algunos días.</p>
<p>Además, se ha juntado el tema de la guitarra. Ha habido muchos cambios, debido a mi nuevo luthier, el genial y fantástico <a href="https://www.youtube.com/@juanbrievaluthier6637" target="_blank" rel="noopener noreferrer">Juan Brieva</a>. Él ha puesto al día y me ha ayudado a mejorar mis guitarras. Esto ha llevado un tiempo, y luego hay que volver a educar mi oído a tantos cambios. No siempre ha salido todo a la primera, y seguimos luchando con ello. Además, estos cambios implican mover algunos componentes, vender pedales, ajustar <em>set-up</em>. Seguimos en ello. Creo que eso es algo que no acaba nunca.</p>
<p>Si seguimos con la guitarra, he iniciado el proceso de buscar otra persona que me ayude a avanzar un poco. Mi profesor desaparece más que el Guadiana con sus nuevas obligaciones en Shanghái. Yo pensaba que el nuevo horario nos daría la libertad de avanzar más (diferencias horarias), pero no ha sido así. Así que en eso andamos. Otra cosa que está minando un poco de energía diaria.</p>
<p>El cambio horario ha implicado que ya determinadas personas no cuenten conmigo a nivel formativo. Pero esto es algo normal y aunque, como bien se dice, &quot;los negocios son negocios&quot;, aprendes de ello para futuras ocasiones. En este tiempo sé que se siguen usando mis materiales y para eso están.</p>
<p>Estos meses también me han dejado desengañado con determinados aspectos de la comunidad de internet del desarrollo. Quizás a veces es bueno desconectar y alejarse de todo un poco para comprender mejor las cosas. Podría decir que hay tantos refritos y saturación que cansa tener que explicar determinados puntos de vista y que algunos sectores están cada vez más radicalizados.</p>
<p>Finalmente, si no fueran suficientes tantos cambios, he tenido que comprar un nuevo portátil. Lo siento mucho, pero para mí es imposible dar clase en los ordenadores congelados, donde día sí, día también me tengo que traer el código, probar librerías, ajustar los <em>dockers</em>, etc. Somos el único gremio que debe poner su propio material para dar clase. Espero que esto cambie. El año pasado, al menos, tenía un portátil del departamento; este año no. Quizás porque este año ni lo he pedido. En cualquier caso, tenía uno, pero no daba la talla con IntelliJ y otro <em>software</em> corriendo a la vez.</p>
<p>No quiero olvidarme de mi &quot;gran vecino o empresa de limpieza&quot; que me ha rayado el coche, otro cambio más.</p>
<p>En fin, muchos cambios y ajustes que espero que merezcan la pena.</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/tBVHqKU.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Pódcast en Devexpert.io</title>
      <link>https://joseluisgs.dev/posts/2024/2024-01-03-podcast-devexpert.html</link>
      <guid>https://joseluisgs.dev/posts/2024/2024-01-03-podcast-devexpert.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Pódcast en Devexpert.io</source>
      <description>Una de las mejores cosas que me trajo este año 2023</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 03 Jan 2024 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Una de las mejores cosas de este curso 2023-2024 es haber participado en el pódcast de <a href="https://devexpert.io/" target="_blank" rel="noopener noreferrer">DevExpert.io</a>. En este nuevo pódcast hemos hablado un poco de la labor docente y de muchas cosas en general con mi amigo y gran profesional Antonio Leiva.</p>
<!-- more -->
<p>Te dejo el enlace a su vídeo en <a href="https://www.youtube.com/watch?v=g4QnBXb0KnQ" target="_blank" rel="noopener noreferrer">YouTube</a> y te invito a que te suscribas a su canal y a su pódcast. Antonio es uno de los profesionales y personas que más respeto, no solo por su excelente contenido, sino por su labor diaria en la generación de contenido de calidad. El equipo de DevExpert.io es un equipo de profesionales que se dedican a llevarte al siguiente nivel en el mundo del desarrollo con una calidad increíble. Aparte de que me une una especial amistad con Antonio, lo que hizo que el rato que pasamos fuera de lo más ameno. Te invito a que te suscribas y disfrutes del resto de los pódcast que vendrán en su canal.</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/g4QnBXb0KnQ?si=o-hFoQtcNcQ1LfmX" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/icsPKsj.png" type="image/png"/>
    </item>
    <item>
      <title>Curso nuevo, y nuevos objetivos</title>
      <link>https://joseluisgs.dev/posts/2023/2023-10-27-curso-nuevo.html</link>
      <guid>https://joseluisgs.dev/posts/2023/2023-10-27-curso-nuevo.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Curso nuevo, y nuevos objetivos</source>
      <description>Comienza un nuevo curso, y con ello nuevos objetivos, nuevos retos y nuevas ilusiones. ¿Te apuntas?</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Fri, 27 Oct 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Comenzamos un nuevo curso, y con ello nuevos objetivos, nuevos retos y nuevas ilusiones. También ha habido cambios en mi vida profesional. Te cuento algunas cosas que me han pasado y que me van a pasar. ¿Te apuntas?</p>
<!-- more -->
<p>Este curso 2023/2024 ha comenzado con muchos cambios en el departamento, y además a nivel personal. En el departamento de Informática del IES Luis Vives ha habido cambios en la dirección del departamento. Además, se han producido cambios en la plantilla de profesorado, y con ello, cambios en la distribución de la docencia.</p>
<p>Por mi parte, el curso pasado en el centro no fue uno de los mejores por distintos aspectos. A veces, los cursos son como cosechas: por mucho que te esfuerces, puede que venga una granizada y eche parte de tu trabajo por tierra. Además, sabemos que la profesión de docente a veces no es muy valorada en estos tiempos y que el alumnado y otros factores externos no siempre ayudan. Pero no pasa nada, hay que seguir adelante.</p>
<p>Por otro lado, fue increíble poder estrechar lazos y colaborar con distintas personas que admiro o ayudar a compañeros/as en cursos de formación. Esto me hizo crecer como persona y como profesional y fue una experiencia muy enriquecedora.</p>
<p>Por los factores de cambios en el departamento y nuevos intereses vi la oportunidad de dar clases en DAW en turno de tarde y dejar DAM.</p>
<p>En estos momentos, estoy impartiendo en DAW:</p>
<ul>
<li>Programación 1.º DAW.</li>
<li>Entornos de Desarrollo 1.º DAW.</li>
<li>Desarrollo Web en Entorno Servidor 2.º DAW.</li>
</ul>
<p>El turno de tarde ha implicado ajustar mi horario y mi vida personal, pero creo que ha sido una buena decisión. Pero como dice mi entrenador de tenis, el primer paso ha sido ajustar mis biorritmos a este nuevo horario. Esto ha implicado cambiar mis hábitos de sueño, alimentación y ejercicio. Aunque no lo creas, esto es muy importante para poder rendir al máximo en el trabajo y en la vida personal.</p>
<p>Por otro lado, este año estoy organizando una serie de charlas en un proyecto de &quot;Laboratorio de Desarrollo de Software&quot;, donde personas de interés en el mundo del desarrollo vengan a compartir su experiencia con todo el alumnado y profesorado del centro. Por otro lado, el turno de tarde, al ser algo más tranquilo, me deja más tiempo para poder dedicarme a otros proyectos y a la investigación de nuevos aspectos, tecnologías para aplicarlas en el aula y fuera de ella.</p>
<p>Finalmente seguiré colaborando en formación del profesorado, amigos y otras entidades aportando mi granito de arena en la formación en el desarrollo del <em>software</em>. Esto sin duda se ha convertido en uno de mis pilares y me encanta poder compartirlo con otras personas.</p>
<p>Espero que este nuevo curso nos ayude a seguir aprendiendo, disfrutando y aportando al alumnado lo mejor de nosotros para que salga lo mejor preparado posible. ¡Vamos a por ello!</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/OYSk3iD.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Entrevista y podcast en Disco Duro de Roer</title>
      <link>https://joseluisgs.dev/posts/2023/2023-07-09-entrevista-disco-duro.roer.html</link>
      <guid>https://joseluisgs.dev/posts/2023/2023-07-09-entrevista-disco-duro.roer.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Entrevista y podcast en Disco Duro de Roer</source>
      <description>Podcast donde hablo un poco de mí y de mi trabajo con Disco Duro de Roer</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Sun, 09 Jul 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Estos días he tenido la inmensa fortuna de que la gente de <a href="https://www.discoduroderoer.es/" target="_blank" rel="noopener noreferrer">Disco Duro de Roer</a> se ha interesado por mi labor y hemos pasado un rato genial y muy agradable hablando de ello.</p>
<!-- more -->
<p>Te dejo el enlace a su vídeo en <a href="https://www.youtube.com/watch?v=uyACMK9G0RQ" target="_blank" rel="noopener noreferrer">YouTube</a> y te invito a que te suscribas a su canal y a su pódcast. Además, en su web puedes encontrar un montón de información interesante sobre tecnología y sobre el mundo de la informática y resolución de prácticas de DAM/DAW. Yo que tú no dejaría pasar la oportunidad de echarle un vistazo y estar al tanto de sus publicaciones.</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/uyACMK9G0RQ?si=WnsDocQcBHA_AN8Z" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</p>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/xJnegMC.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Crea tu API REST reactiva con Kotlin y Ktor Parte I</title>
      <link>https://joseluisgs.dev/posts/2023/2023-05-29-reactive-api-rest-pt-i.html</link>
      <guid>https://joseluisgs.dev/posts/2023/2023-05-29-reactive-api-rest-pt-i.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Crea tu API REST reactiva con Kotlin y Ktor Parte I</source>
      <description>Cómo crear un servicio REST aplicando reactividad y consideraciones a tener en cuenta para ello</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Mon, 29 May 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>La programación reactiva tiene muchas aplicaciones, una de ellas es poder aplicarse en cualquier parte del <em>front</em> y <em>back</em> para aprovecharse de algunas de sus ventajas. Este año también la hemos trabajado en clase. Como uno de los responsables del <em>track</em> de Kotlin Developer estoy escribiendo una serie de tutoriales para <a href="https://hyperskill.org/" target="_blank" rel="noopener noreferrer">Hyperskill</a> de <a href="https://lp.jetbrains.com/jba-students/" target="_blank" rel="noopener noreferrer">JetBrains Academy</a>, donde se explica cómo crear una API REST reactiva con Kotlin y Ktor y repaso una serie de conceptos en los que se basa. Os lo dejo traducido, pero recomiendo que leáis el original en inglés y votéis para que sigan saliendo más partes: <a href="https://medium.com/p/f217be55c0bf" target="_blank" rel="noopener noreferrer">Creating Your Reactive REST API with Kotlin and Ktor Part I</a>.</p>
<!-- more -->
<div class="hint-container warning">
<p class="hint-container-title">Sobre la traducción</p>
<p>Lamentablemente no tengo tiempo para traducir todos los artículos que escribo, pero este me pareció interesante y que podía aportar algo a la comunidad. Por ello, he usado ChatGPT y pulido los errores que me he encontrado. De nuevo te recomiendo la lectura del original en inglés si buscas el 100% de exactitud: <a href="https://medium.com/p/f217be55c0bf" target="_blank" rel="noopener noreferrer">Creating Your Reactive REST API with Kotlin and Ktor Part I</a>.</p>
</div>
<h2>Introducción</h2>
<p>Una de las cosas esenciales que cualquier desarrollador de <em>backend</em> debe dominar es la creación de servicios REST.</p>
<p>En la actualidad, la gestión eficiente y efectiva de los recursos es una ventaja que debemos lograr. La programación reactiva nos ofrece soluciones para la demanda de tiempos de respuesta más rápidos y alta disponibilidad de los sistemas, características que se logran con modelos anteriores de microservicios, pero que dan soluciones a los problemas de uso excesivo de la CPU, bloqueo en operaciones de entrada y salida, o uso de memoria (debido a grandes grupos de subprocesos) de los que sufrían estos modelos.</p>
<p>A lo largo de esta serie de artículos, se presentan una serie de contenidos que aprenderás a través de las diferentes pistas de formación de <a href="https://hyperskill.org/" target="_blank" rel="noopener noreferrer">Hyperskill</a>. Con Hyperskill podrás profundizar en ellos, ampliarlos, analizar diferentes alternativas y convertirte en un verdadero desarrollador de <em>backend</em>.</p>
<p>En estos tutoriales mostramos cómo configurar una API REST reactiva utilizando Ktor y Kotlin, analizando todos los elementos, utilizando la programación orientada a ferrocarriles, la inyección de dependencias, las pruebas y aplicando varias configuraciones de seguridad, autenticación y autorización hasta que puedas implementarlo y disfrutar de tus logros. Recuerda que este código es pedagógico y muestra muchos de los contenidos que aprenderás en Hyperskill de una manera didáctica y fácil de leer. No tiene la intención de crear el mejor código de producción en entornos reales.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*6sqcDNSinKG2uQb7UrJB7A.png">
</p>
<h2>La Programación Reactiva</h2>
<p>Siguiendo los principios del <a href="https://www.reactivemanifesto.org/" target="_blank" rel="noopener noreferrer">Manifiesto Reactivo</a>, los sistemas reactivos deben ser:</p>
<ul>
<li>Responsivos: El sistema responde de manera oportuna siempre que sea posible.</li>
<li>Resilientes: El sistema se mantiene responsivo ante fallos.</li>
<li>Elásticos: El sistema se mantiene responsivo bajo cargas de trabajo variables.</li>
<li>Basados en mensajes: Los sistemas reactivos se basan en el paso de mensajes asincrónico para establecer una frontera entre componentes que garantiza un acoplamiento laxo, aislamiento y transparencia de ubicación.</li>
</ul>
<p><strong>La programación reactiva se centra en trabajar con flujos asíncronos de fuentes de datos finitas o infinitas donde podemos observarlos.</strong></p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*KsJp6dTuXR-nPzaJXlKHiQ.png">
</p>
<p>La programación reactiva es un subconjunto de la programación asíncrona. Permite descomponer el problema en múltiples pasos discretos en los que cada uno se puede ejecutar de manera <strong>asíncrona y no bloqueante</strong>, para luego componerlos y producir el flujo de trabajo final.</p>
<p>La programación reactiva ofrece varias ventajas, <strong>incluida una mejor utilización de los recursos informáticos en sistemas multinúcleo y multiprocesador</strong>, y un rendimiento mejorado al reducir los puntos de serialización. Otro beneficio importante es el aumento de la productividad del desarrollador. Los paradigmas de programación tradicionales han tenido dificultades para proporcionar una forma sencilla y mantenible de manejar la computación asíncrona y no bloqueante y las operaciones de entrada y salida. La programación reactiva resuelve la mayoría de estos desafíos al eliminar la necesidad de coordinación explícita entre los componentes activos.</p>
<p>Para aprovechar la ejecución asíncrona, es necesario incluir el control de retroalimentación para evitar la sobreutilización o el consumo desmedido de recursos. Por ejemplo, en las entradas y salidas con bases de datos que utilizan JDBC, bloqueamos el hilo hasta que recibimos una respuesta. Este es un ejemplo sencillo de las cosas que vamos a resolver para mejorar la productividad de nuestros servicios.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*ERXJmA1XHPcCccFJPDlLyg.png">
</p>
<p>Aquí es donde <a href="https://ktor.io/" target="_blank" rel="noopener noreferrer">Ktor</a> y <a href="https://kotlinlang.org/" target="_blank" rel="noopener noreferrer">Kotlin</a> forman un equipo invencible. Ktor nos ofrece la posibilidad de crear servicios asíncronos (primera condición), y Kotlin ofrece <a href="https://kotlinlang.org/docs/coroutines-overview.html" target="_blank" rel="noopener noreferrer">corrutinas</a> y <em>Flows</em> (flujos) para procesar colecciones de manera asíncrona y reactiva.</p>
<h2>Creando un nuevo proyecto con Kotlin y Ktor</h2>
<p><strong>Kotlin</strong> es un lenguaje de programación de tipo estático que tiene varias ventajas sobre otros lenguajes de programación. Algunas de las ventajas de Kotlin son: interoperabilidad con Java, sintaxis concisa, seguridad ante valores nulos, soporte para programación funcional, funciones de extensión y <em>coroutines</em>, lo que facilita la escritura de código asíncrono y no bloqueante. Las <em>coroutines</em> son livianas y eficientes y se pueden utilizar para simplificar código asíncrono complejo. El soporte de Kotlin es una excelente elección para construir aplicaciones modernas.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*BCwpKaSY_hTRJMvTddpMAw.png">
</p>
<p><strong>Ktor</strong> es un <em>framework</em> ligero basado en Kotlin utilizado para crear aplicaciones y servicios web en el lado del servidor. Proporciona una API simple y flexible para construir aplicaciones asíncronas, basadas en eventos y no bloqueantes.</p>
<p>Con Ktor, los desarrolladores pueden crear fácilmente APIs RESTful, aplicaciones web y microservicios. Es un <em>framework</em> de código abierto que se puede utilizar para construir aplicaciones tanto web como móviles. Ktor se puede utilizar con <em>coroutines</em> y <em>flows</em> de Kotlin para escribir código asíncrono/reactivo de manera más concisa y legible.</p>
<p>Ktor utiliza <em>plugins</em> para ampliar su funcionalidad según las necesidades del proyecto. Algunos de ellos están incluidos por defecto, otros deben ser instalados. En ambos casos, debemos configurarlos para poder utilizarlos.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*4aBfRqsFco67-aiA1EFFmg.png">
</p>
<p>Podemos crear un proyecto Ktor desde el generador web: <a href="https://start.ktor.io/" target="_blank" rel="noopener noreferrer">https://start.ktor.io/</a> (abrir el archivo zip) o desde el complemento <a href="https://plugins.jetbrains.com/plugin/16008-ktor" target="_blank" rel="noopener noreferrer">IntelliJ IDEA</a>.</p>
<p>En nuestro caso, como estamos experimentando con numerosos torneos de tenis, <em>vamos a implementar una API REST reactiva para averiguar las raquetas más utilizadas en un torneo de tenis</em>.</p>
<p>Primero debemos crear nuestro proyecto y agregar los siguientes <em>plugins</em>: <strong>enrutamiento y serialización de Kotlin</strong>. El primero nos permitirá crear las rutas o <em>endpoints</em> para gestionar las raquetas. El segundo nos ofrece la capacidad de intercambiar información en formato JSON.</p>
<p>Es posible que necesitemos otros <em>plugins</em> en el futuro, pero no importa, los agregaremos manualmente más adelante.</p>
<p>En &quot;Ajustar configuración del proyecto&quot;, seleccionamos <strong>&quot;configuración en archivo HOCON&quot;</strong>.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*G8VBnzaJMzSEyhtWW_l6vw.png">
</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*-KUwnLGgUIXZWK6UyDHMmg.png">
</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*5V4gTZMg381CatzYGDs8Cw.png">
</p>
<h3>Analizando el código inicial</h3>
<p>Tenemos la siguiente estructura:</p>
<p><em>Application</em>: Contiene el código que inicia nuestro servicio y donde se indican los <em>plugins</em> a configurar.
<em>plugins/Routing</em>: Define el enrutamiento de nuestra aplicación basado en...
<em>plugins/Serialization</em>: Configura la serialización de nuestra aplicación basada en JSON.
<em>resources/application.conf</em>: Configura la aplicación en función de las variables de entorno.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:640/format:webp/1*mVhfQodrH_jzvcacoiWE7g.png">
</p>
<h3>Configurar un Plugin</h3>
<p>Puedes configurar un <em>plugin</em> utilizando funciones de extensión. Por ejemplo, &quot;configureRouting&quot; en Application.kt es una función de extensión que define el enrutamiento. Esta función se declara en un paquete de <em>plugin</em> separado (el archivo Routing.kt).</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">fun</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Application</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">configureRouting</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">   routing</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"/"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respondText</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"¡Hola Mundo!"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>Ejecutar el Servicio</h3>
<p>Puedes ejecutar el método principal y luego verificar tu API en http://0.0.0.0:8080. Verás el famoso mensaje: &quot;¡Hola Mundo!&quot;</p>
<h2>Codificando nuestro servicio</h2>
<h3>Configurar nuestro servicio</h3>
<p>El primer paso es configurar nuestro servicio y agregar algunos puntos adicionales a nuestro archivo application.conf para optimizar el desarrollo. Esta es la primera versión y ampliaremos este archivo en tutoriales posteriores.</p>
<h3>Definir nuestro modelo</h3>
<p>El primer paso es definir nuestro modelo. En este caso, es la raqueta (Racket). Lo haremos dentro de una carpeta/paquete llamada &quot;racket&quot;. Tendrá un id, marca, modelo, precio, número de jugadores de tenis que la utilizan, una imagen, y la fecha y hora de creación y última modificación. Además, usaremos una constante de id para identificar las raquetas nuevas o existentes.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">data</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Long</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> NEW_RACKET,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> brand: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> model: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> price: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Double</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> numberTenisPlayers: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Int</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> image: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> DEFAULT_IMAGE,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> createdAt: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">LocalDateTime</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> LocalDateTime.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> updatedAt: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">LocalDateTime</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> LocalDateTime.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(),</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> isDeleted: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Boolean</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> false</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    companion</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> object</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> NEW_RACKET </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> -</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1L</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        const</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> DEFAULT_IMAGE </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "https://i.imgur.com/AsZ2xYS.jpg"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>Desarrollando nuestro repositorio</h2>
<p>El patrón de repositorio es un patrón de diseño que aísla la capa de datos del resto de la aplicación. El patrón de repositorio tiene dos propósitos: primero, es una abstracción de la capa de datos, y segundo, es una forma de centralizar el manejo de los objetos del dominio. La idea es tener una forma abstracta y genérica para que la aplicación pueda trabajar con la capa de datos sin importar si la implementación se dirige a una base de datos local, un archivo o una colección en memoria.</p>
<p>Los métodos se basan en las operaciones CRUD: Crear (Create), Leer (Read), Actualizar (Update) y Eliminar (Delete). En la primera versión, utilizamos un mapa como repositorio en memoria (en partes futuras utilizaremos una base de datos reactiva). Usamos funciones suspendidas (<em>suspended functions</em>), <em>flows</em> y tipos nulos (<em>nullable types</em>) para realizar el uso reactivo y asíncrono del repositorio. Utilizaremos interfaces para poder realizar la inyección de dependencias más adelante y cumplir con los principios SOLID. También utilizamos <a href="https://github.com/oshai/kotlin-logging" target="_blank" rel="noopener noreferrer">Kotlin Logging</a> para mostrar los mensajes. Además, utilizamos un <a href="https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html" target="_blank" rel="noopener noreferrer">nuevo contexto y despachador (<em>dispatcher</em>)</a> para ejecutar los métodos en un hilo (<em>thread</em>) especial y no suspender el hilo de la solicitud (<em>request petition</em>).</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> RacketsRepositoryImpl</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> : </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">RacketsRepository</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> rackets </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> racketsDemoData</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> findAll</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Flow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> withContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Dispatchers.IO) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"findAll"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">@withContext rackets.values.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">().</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">asFlow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> findById</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(id: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Long</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> withContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Dispatchers.IO) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"findById: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$id</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">@withContext rackets[id]</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> findAllPageable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(page: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, perPage: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Int</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Flow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> withContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Dispatchers.IO) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"findAllPageable: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$page</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">, </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$perPage</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> myLimit </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (perPage </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">></span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 100</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">100L</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> else</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> perPage.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toLong</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> myOffset </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (page </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">*</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> perPage).</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toLong</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">@withContext rackets.values.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">().</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">subList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(myOffset.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toInt</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(), myLimit.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toInt</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()).</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">asFlow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> findByBrand</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(brand: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Flow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> withContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Dispatchers.IO) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"findByBrand: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$brand</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">@withContext rackets.values</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">filter</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { it.brand.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">contains</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(brand, </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">asFlow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> save</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entity: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> withContext</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(Dispatchers.IO) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"save: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$entity</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (entity.id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> Racket.NEW_RACQUET) {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">           create</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entity)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       } </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">           update</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entity)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> update</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entity: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"update: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$entity</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       rackets[entity.id] </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> entity.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">copy</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(updatedAt </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> LocalDateTime.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">())</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> entity</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> create</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entity: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"create: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$entity</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       </span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> rackets.keys.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">maxOrNull</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">plus</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) ?: </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> newEntity </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> entity.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">copy</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id, createdAt </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> LocalDateTime.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(), updatedAt </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> LocalDateTime.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">now</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">())</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       rackets[id] </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> newEntity</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> newEntity</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> delete</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entity: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"delete: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$entity</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       </span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">remove</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entity.id)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> deleteAll</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"deleteAll"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">clear</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> saveAll</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(entities: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Iterable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>): </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Flow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"saveAll: </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$entities</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       </span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       entities.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">forEach</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">save</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(it) }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">       return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> entities.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">asFlow</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>Configurando nuestras rutas y <em>endpoints</em></h3>
<p>El siguiente paso es definir nuestras rutas para nuestros <em>endpoints</em>; para ello, debemos conocer perfectamente los verbos <a href="https://miro.medium.com/v2/resize:fit:720/format:webp/1*ontd8poQrEWLIsMzy0QYpQ.png" target="_blank" rel="noopener noreferrer">HTTP</a>, los códigos de estado y respuesta y las <a href="https://ktor.io/docs/requests.html" target="_blank" rel="noopener noreferrer">solicitudes (<em>Requests</em>)</a> y <a href="https://ktor.io/docs/responses.html" target="_blank" rel="noopener noreferrer">respuestas (<em>Responses</em>)</a> utilizando el DSL de Ktor. Podemos utilizar la siguiente tabla como referencia:</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*ontd8poQrEWLIsMzy0QYpQ.png">
</p>
<p>Utilizamos una función de extensión llamada <strong>&quot;racketsRoutes()&quot;</strong> para definirlo y luego usamos esta función en nuestro <em>plugin</em> de <strong>enrutamiento (Routing)</strong>.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">fun</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Application</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">configureRouting</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    routing</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"/"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">            call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respondText</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Hello World!"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Add our routes</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    racketsRoutes</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="hint-container warning">
<p class="hint-container-title">Aviso</p>
<p>Recuerda, esta no es la versión final y todavía tenemos que mejorar muchas cosas, pero es un buen punto de partida para verificar nuestro servicio y cómo funciona Ktor. De hecho aún no está programado decentemente, pero es un buen punto de partida para verificar nuestro servicio y cómo funciona Ktor.</p>
</div>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Define endpoint</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">private</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> const</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> ENDPOINT </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "api/rackets"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">fun</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Application</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">racketsRoutes</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">   // Repository</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">   val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> rackets: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">RacketsRepository</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> RacketsRepositoryImpl</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">   // Define routing based on endpoint</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">   routing</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">       route</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"/</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">           // Get all rackets --> GET /api/racquets</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">           get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">info</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Get all rackets"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">               // QueryParams ??</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> page </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.request.queryParameters[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"page"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toIntOrNull</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> perPage </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.request.queryParameters[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"perPage"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toIntOrNull</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() ?: </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">10</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (page </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> &#x26;&#x26;</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> page </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">></span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"GET ALL /</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">?page=</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$page</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">&#x26;perPage=</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$perPage</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">findAllPageable</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(page </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">-</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, perPage).</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                       .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.OK, </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               } </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"GET ALL /</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">findAll</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">().</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                       .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.OK, </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">           // Get one racket by id --> GET /api/rackets/{id}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">           get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"{id}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"GET BY ID /</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/{id}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.parameters[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"id"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toLongOrNull</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               id?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">let</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">findById</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(it)?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.OK, </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                       ?: call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.NotFound, </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Racket not found with ID </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$id</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               } ?: call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.BadRequest, </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"ID is not a number"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">           // Get one rackets by brand --> GET /api/rackets/brand/{brand}</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">           get</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"brand/{brand}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"GET BY BRAND /</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/brand/{brand}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> brand </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.parameters[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"brand"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               brand?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">let</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">findByBrand</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(it).</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                       .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.OK, </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               } ?: call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.BadRequest, </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Brand is not a string"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">           // Create a new racket --> POST /api/rackets</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">           post</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"POST /</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> racket </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">receive</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">save</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(racket)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.Created, </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">           // Update a racket by id --> PUT /api/rackets/{id}</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">           put</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"{id}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"PUT /</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/{id}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.parameters[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"id"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toLongOrNull</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               id?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">let</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">                   val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> racket </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">receive</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Racket</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">>()</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">                   // exists?</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">findById</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(it)?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">let</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                       rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">save</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(racket)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                           .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.OK, </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   } ?: call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.NotFound, </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Racket not found with ID </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$id</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               } ?: call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.BadRequest, </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"ID is not a number"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">           // Delete a racket by id --> DELETE /api/racquets/{id}</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">           delete</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"{id}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               logger.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">debug</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"DELETE /</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$ENDPOINT</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">/{id}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">               val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> call.parameters[</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"id"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toLongOrNull</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               id?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">let</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">                   // exists?</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">findById</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(it)?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">let</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { racquet </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">-></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                       rackets.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">delete</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(racquet)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                           .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">run</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.NoContent) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">                   } ?: call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.NotFound, </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Racket not found with ID </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$id</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">               } ?: call.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">respond</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(HttpStatusCode.BadRequest, </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"ID is not a number"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">           }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">       }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">   }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2>Probando en Postman</h2>
<p>Finalmente, podemos probar nuestra API REST en <a href="https://www.postman.com/" target="_blank" rel="noopener noreferrer">Postman</a> o cualquier otro cliente REST. Postman es un cliente famoso para probar API REST. Puedes configurar nuestra solicitud y probar las respuestas.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*MBybxX-nRcHouQWwjv2YlQ.png">
</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*Fje9jv8oeYOi7uCAFQStPA.png">
</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*z9v0iFFz1h5774Lrjau8hw.png">
</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*cGxtQGcE1j3I5QogUkkqcg.png">
</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*ZExKX_rc9GGO9xRjx2I_rw.png">
</p>
<h2>Conclusión</h2>
<p>Hemos comenzado una serie de temas emocionantes para captar las ideas de la programación reactiva y generar una API REST.</p>
<p>Ahora tenemos un buen punto de partida para comenzar a trabajar. Hemos mostrado cómo funciona Ktor para desarrollar una API REST reactiva, pero aún tenemos mucho que mejorar en nuestro código. Pero no todo se puede hacer en el primer paso. Poco a poco iremos aprendiendo lo que necesitamos en los siguientes tutoriales.</p>
<p>Nos hemos centrado en crear nuestros primeros <em>endpoints</em> y probarlos en Postman. Pero todavía tenemos mucho por hacer en futuros tutoriales:</p>
<ul>
<li>Validar solicitudes.</li>
<li>Manejo de errores y excepciones.</li>
<li><em>Railway Oriented Programming</em>.</li>
<li>Uso de un método de caché.</li>
<li>Uso de una base de datos reactiva.</li>
<li>Subir archivos.</li>
<li>Usar <em>websockets</em> para implementar notificaciones en tiempo real.</li>
<li>Usar Koin para inyectar nuestras dependencias.</li>
<li>Conexiones seguras con SSL/TSL.</li>
<li>Autenticación y autorización con JWT.</li>
<li>Pruebas de nuestros <em>endpoints</em>.</li>
</ul>
<p>Tienes el código de este proyecto en <a href="https://github.com/joseluisgs/ktor-reactive-rest-hyperskill" target="_blank" rel="noopener noreferrer">GitHub</a>. El código de esta parte está en <a href="https://github.com/joseluisgs/ktor-reactive-rest-hyperskill/releases" target="_blank" rel="noopener noreferrer">Releases</a>. Por favor, no olvides darle una estrella o seguirme para estar al tanto de nuevos tutoriales y noticias.</p>
<p><strong>Puedes seguirlo <em>commit</em> por <em>commit</em> y utilizar el archivo de respaldo de Postman para probarlo.</strong></p>
<p>Además, en <a href="https://hyperskill.org" target="_blank" rel="noopener noreferrer">Hyperskill</a>, puedes profundizar y aprender todos los conceptos y más a través de diferentes temas y tareas que te ayuden a mejorar como desarrollador en tecnologías Kotlin.</p>
<p>Las siguientes trayectorias ofrecidas por JetBrains Academy en Hyperskill pueden ser un punto de partida perfecto. Encontrarás toda la información y explicación de los conceptos y técnicas mostradas en estos artículos. ¡No te los pierdas!</p>
<ul>
<li><a href="https://hyperskill.org/tracks/3?category=4&amp;utm_source=medium_hs&amp;utm_medium=social&amp;utm_campaign=ktor&amp;utm_term=16.05.2023" target="_blank" rel="noopener noreferrer">Desarrollador Kotlin</a></li>
<li><a href="https://hyperskill.org/tracks/45?category=20&amp;utm_source=medium_hs&amp;utm_medium=social&amp;utm_campaign=ktor&amp;utm_term=16.05.2023" target="_blank" rel="noopener noreferrer">Desarrollador Backend Kotlin (Ktor)</a></li>
</ul>
<p>Con estas trayectorias, adquirirás experiencia práctica trabajando con herramientas modernas y aprenderás cómo desarrollar aplicaciones del lado del servidor, mantener los datos persistentes en tus bases de datos y probar la funcionalidad de tus aplicaciones utilizando herramientas modernas.</p>
<p>Déjanos saber en los comentarios si tienes alguna pregunta o comentario sobre este blog. También puedes seguirnos en las redes sociales para estar al tanto de nuestros últimos artículos y proyectos. Estamos en <a href="https://www.reddit.com/r/Hyperskill/" target="_blank" rel="noopener noreferrer">Reddit</a>, <a href="https://twitter.com/yourhyperskill" target="_blank" rel="noopener noreferrer">Twitter</a>, <a href="https://www.linkedin.com/company/hyperskill" target="_blank" rel="noopener noreferrer">LinkedIn</a> y <a href="https://www.facebook.com/myhyperskill" target="_blank" rel="noopener noreferrer">Facebook</a>.</p>
]]></content:encoded>
      <enclosure url="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*6sqcDNSinKG2uQb7UrJB7A.png" type="image/png"/>
    </item>
    <item>
      <title>¿Cómo puedo ayudar en 2023?</title>
      <link>https://joseluisgs.dev/posts/2023/2023-01-01-como-puedo-ayudar.html</link>
      <guid>https://joseluisgs.dev/posts/2023/2023-01-01-como-puedo-ayudar.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">¿Cómo puedo ayudar en 2023?</source>
      <description>Algunas reflexiones sobre el nuevo año y los propósitos que me he marcado para el 2023</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Sun, 01 Jan 2023 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Año que acaba y año que comienza. Es tiempo de hacer balance y de pensar en el futuro. En este caso, en el futuro inmediato. El 2023 ya está aquí. No soy muy amante de los propósitos de año nuevo, pero este año me he marcado cambiar algunas cosas con respecto al año pasado, pues hay situaciones que no han terminado de encajar y me gustaría poner de mi parte para mejorarlas. Mi primer <em>deploy</em> de 2023.</p>
<!-- more -->
<h2>Resumen profesional de 2022</h2>
<p>Este 2022 no ha sido un año fácil. Con él terminé mi primer curso académico en mi nuevo destino. Nueva ciudad, nuevo centro, nuevo contexto, nuevos compañeros/as, alumnado y dinámicas que aprender. Y es que, aunque digan que todos los centros se parecen, cada uno tiene sus particularidades. Te pueden gustar más o menos, pero son las que hay y te debes adaptar en parte a ellas.</p>
<p>Por un lado, yo venía de otro instituto donde los compañeros/as estaban muy unidos a nivel docente, compartiendo e implicándose la mayoría en proyectos educativos comunes. Esto hacía que determinadas estrategias de cómo entiendo el proceso de enseñanza-aprendizaje las tuviera muy asimiladas y formaban parte de mi día a día. Aquí las cosas son un poco distintas, pues al haber tantos ciclos, en donde yo me muevo, DAM, seguimos trabajando duramente entre todos/as para asentarlo y encontrar un equilibrio, que a veces se dificulta al no tener una plantilla fija. Nada del otro mundo.</p>
<p>El instituto es muy bueno, tiene un gran equipo directivo, muy buenas ideas, muy tentadoras y atrayentes, de todas las familias profesionales que lo componen.</p>
<p>A nivel personal, intenté aportar con mi trabajo, contactos y alianzas lo que me había marcado como objetivo en el 2021/2022. Algunas cosas se han conseguido, otras pues han chocado con la realidad, ya que quizás no era el momento y que yo, posiblemente, no analicé la situación correctamente y el impacto que pueden suponer estos cambios, y no se han podido llevar a cabo. Pero bueno, es parte del proceso. Y que conste que he encontrado un centro donde el equipo directivo ha confiado en mí desde el principio y parte de mis compañeros/as de departamento me han respaldado y ayudado en todo lo que he necesitado y eso no tiene precio.</p>
<p>Personalmente y profesionalmente seguí apostando por ofrecer lo mejor al alumnado. Fui elegido <a href="https://www.jetbrains.com/es-es/company/partners/kotlin/" target="_blank" rel="noopener noreferrer">Kotlin Trainer Certified by JetBrains</a> y con ello <a href="https://joseluisgs.dev/blogs/2022/2022-06-25-decepciones-superaciones.html#nuevos-reconocimientos-y-un-paso-mas" target="_blank" rel="noopener noreferrer">poder certificar unos mínimos profesionales para mi alumnado</a>. Sigo como GitHub Campus Advisor, Embajador de GitKraken y mi centro es Campus de JetBrains, GitHub, Postman o Canvas, entre otras cosas. El proceso de certificación de JetBrains fue duro, pero enriquecedor, donde aprendí mucho de cada una de las etapas que tuve que superar. Y es que no es lo mismo ser un buen programador que ser un buen formador en una tecnología/lenguaje al nivel que exigen sus propios creadores. Y en esta certificación has de combinar ambas cosas.</p>
<p>Por otro lado, he conocido a gente maravillosa que me ha aportado profesionalmente y personalmente mucho. Aprender y colaborar con los demás es lo que siempre más me ha motivado. <a href="https://devexperto.com/acerca-de/" target="_blank" rel="noopener noreferrer">Antonio Leiva</a> se ha convertido en uno de mis pocos amigos en mi nuevo contexto y, aparte, en un pilar de colaboración y aprendizaje. Sus formaciones son exquisitas, y está trabajando junto a su equipo para ofrecer lo mejor. Yo mismo <a href="https://joseluisgs.dev/blogs/2022/2022-06-27-kotlin-expert.html" target="_blank" rel="noopener noreferrer">me refuerzo en algunas de ellas</a>. Te invito a que veas lo que te ofrece en su web: <a href="https://devexperto.com/" target="_blank" rel="noopener noreferrer">Devexperto</a>.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://scontent-mad1-1.xx.fbcdn.net/v/t39.30808-6/288647726_3240026732991763_9124814251306655995_n.png?_nc_cat=100&ccb=1-7&_nc_sid=e3f864&_nc_ohc=gFsIye81VMsAX8G8W_R&tn=Z5Tl5uC_NsUZd0oi&_nc_ht=scontent-mad1-1.xx&oh=00_AfBwGFOEGOCb3MyUdShpr9FJR-2kUqIy3dmP55oV8qlTgA&oe=63B49D48" alt="Imagen">
</p>
<p>Por otro lado, inicié mi etapa ayudando en algunos temas de <a href="https://www.jetbrains.com/es-es/academy/" target="_blank" rel="noopener noreferrer">JetBrains Academy</a>. Primeramente, escribiendo algunos temas para los <em>tracks</em> de Kotlin, donde además asenté mis conocimientos de nuevo con su propio título, desde otro punto de vista de solo estudiante. Posteriormente, ayudando en la corrección/revisión de los mismos. Y es que la calidad de los contenidos de esta plataforma es muy buena, y es un placer poder colaborar en ella y aprender de la gente de su equipo, que tiene unos conocimientos y unas ideas muy interesantes para transmitir el aprendizaje de un lenguaje de desarrollo por una plataforma virtual. Esto ha hecho que aprenda lo que en otros países, centros de formación valoran en el aprendizaje de un lenguaje y cómo enfocan su aprendizaje. Al día de hoy soy parte responsable y experto en contenidos de Kotlin en esta plataforma. Un honor que confíen en mí para ello.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://blog.jetbrains.com/wp-content/uploads/2021/08/Twitter_post_1200x675.png" alt="Imagen">
</p>
<p>También colaboré asistiendo a algunas sesiones de trabajo del grupo de Vue.js Madrid, aunque han sido geniales, no hubo muchas. Espero que se retomen.</p>
<p>He ayudado a formar a otros compañeros/as y ha sido muy agradable. He aprendido mucho de ello y espero seguir haciéndolo mejor en las siguientes oportunidades, si las hay.</p>
<p>Sinceramente, me siento muy afortunado de poder conocer a gente tan maravillosa y poder colaborar con ellos/as. Y es que la colaboración es la clave del éxito. Y es que no hay nada mejor que poder aprender de los demás y poder aportar lo que sabes a los demás. Y si encima te encuentras con buenas personas como Antonio o Zoya, pues mejor que mejor. Han sido elementos fundamentales fuera del centro que <em>me han dado el equilibrio y los retos profesionales que necesito</em>.</p>
<p>Pero no todo ha sido agradable. Han existido situaciones que incluso me han hecho plantearme buscar otro lugar de trabajo y desarrollar mi carrera profesional en otro sitio. Pero bueno, es parte del proceso: no todo es color de rosa.</p>
<h2>Retos para 2023</h2>
<p>Pero, como he dicho, no todo ha sido perfecto. El 2022 ha sido un año complicado, que ha hecho que me replantee muchas cosas y extraiga conclusiones para aplicar directamente.</p>
<p>Primero y más importante, es <strong>encontrar motivación</strong> y sentirme bien en mi lugar de trabajo. Mi alumnado es bueno, los compañeros/as estupendos y el centro privilegiado. Pero muchas veces siento que no es mi lugar. Por supuesto, ese problema está en mí. Con las cosas que veo y no comparto, me cuesta ser cómplice o parte de ellas. No es culpa del centro, sino más del sistema. Creo que hasta que encuentre mi lugar voy a mantenerme al margen haciendo mi camino. Si me piden ayuda, allí estaré en lo que crea que pueda aportar, pero no lanzaré propuestas como el año pasado, salvo que tenga el consenso de la mayoría, para que no se vean como imposiciones o caprichos. Creo que desperdicié muchas energías en cosas que no han aportado nada. Y si este no es mi lugar, pues deberé buscar otro. No me voy a quedar en un sitio que no me hace feliz o, sobre todo, donde pueda desarrollarme profesionalmente.</p>
<p>Por otro lado, debo desprenderme de formas de trabajar anteriores. Estoy muy agradecido al CIFP Virgen de Gracia, pero aquí no se pueden hacer las cosas iguales. No se trata de que en un sitio sea mejor o peor, sino diferente. El contexto es distinto, lo que implica medidas distintas. Pero a veces parece que sigo de &quot;duelo&quot;. Esto implicará también desprenderme de antiguas obligaciones, salvo las que me unen con su directora. Pero bastante tengo con mi día a día para mirar de reojo a otros departamentos. No es algo personal, pues los quiero y admiro un montón, pero es tiempo y recursos que a veces me cuesta dar y que interfieren en mi día a día en mi contexto actual. Además, ahora hay líneas de trabajo que nos separan y no nos permiten colaborar, por lo que tampoco podemos ponernos trabas a nosotros mismos para avanzar.</p>
<p>Me gustaría involucrarme en algún proyecto de innovación y colaborar en algo que en parte llene. Esa parte mía acostumbrada a investigar o buscar nuevas formas es como una llama que arde. Pero si eso implica tirar de personas, no sé si ahora estaré preparado tras lo sucedido este año. Pero como siempre aceptaré el reto. Me atrae la idea de colaborar con otros departamentos y aprender de su forma de trabajar en otras especialidades.</p>
<p>Mejorar como docente en la comunicación de contenidos es esencial. Al día de hoy, sé que mi alumnado es lo más importante. Voy a seguir intentando darles un valor añadido a los contenidos del día a día para su futura etapa profesional e intentar poner de mi parte para que solventen sus problemas. De hecho, voy a aplicar la máxima del Dr. Max Goodwin en New Amsterdam (una de las series con la que más estoy aprendiendo en distintos sentidos) y contestar siempre con <strong>&quot;¿cómo puedo ayudar?&quot;</strong> a cualquier persona que me rodee, sobre todo dentro del aula.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/ZBQeJwn.jpg" alt="Imagen">
</p>
<p>Me encantaría colaborar más con Antonio Leiva; si lo lees, ya sabes que me encantaría poder colaborar contigo en algún proyecto. Y si no, pues espero que algún día podamos hacerlo. Y es que me encanta tu forma de trabajar no por lo que se ve desde fuera, sino en tu día a día. Ya lo sabes. Aquí me tienes. Eso no quita de otras cosas que ya compartimos, jejeje.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://computerworldmexico.com.mx/wp-content/uploads/2020/07/educacion-digital-IA.jpg" alt="Imagen">
</p>
<p>Sobre el tema de proyectos de fin de grado y tutorías. Al día de hoy, comuniqué oficialmente que prefería no involucrarme y que me limitaría a lo que <em>legalmente fuese necesario</em>. No creo que pueda aportar nada en este campo, y tras algunas circunstancias que ya he comentado en entradas anteriores o en esta propia entrada, prefiero no hacerlo. Pero si alguien quiere que le ayude, allí estaré. Pero <strong>al día de hoy no saldrá de mí ser el tutor ni tribunal de nadie</strong>. Prefiero dedicar mi tiempo a otros quehaceres para el centro o la comunidad educativa y que repercutan directa o indirectamente en el alumnado, el propio centro o la comunidad en general. Sobre ser tutor de 1.º o de DAM, no sé si seguiré y si lo hago es por puntos para en el futuro, si es necesario para un futuro concurso de traslados, por si se necesitaran, nunca se sabe, ni está planificado.</p>
<p><strong>Quiero darle sentido al término &quot;servir como docente&quot;: no me refiero a ser utilizado; me refiero a la actitud o calidad de servicio.</strong></p>
<p>Si sigo en DAM, cosa que también dudo ahora, me gustaría replantear su enseñanza. Me gustaría que distintos especialistas que estamos en el sector debatiésemos sobre el contexto actual, el tiempo que tenemos, las competencias demandadas por el sector, estrategias metodológicas consensuadas, pros y contras de tecnologías, o implicaciones e intereses de las empresas, etc. He intentado hablar con el Ministerio para crear una <em>Skill</em> de DAM y me gustaría retomar este tema con otras personas. También creo que deberíamos luchar por un reconocimiento de Unidades de Competencia de algunos módulos y contenidos que se dan en el ciclo. Pero bueno, esto da para otra entrada. Lo apunto.</p>
<p>Me gustaría que entre el alumnado, centro y profesores implicados, empezando por mí, hiciéramos un <strong>club de desarrollo de software</strong> para plasmar algunas ideas suyas, trabajar en proyectos y sobre todo dar rienda suelta a su creatividad e ideas. Yo me apunto a ello y con ello traer a distintos profesionales para que den su visión y aprendan de su experiencia.</p>
<p>Voy a hacer un <strong>libro gratis (o libros)</strong> siguiendo los recursos, contenidos, prácticas que ya tengo disponibles en mi <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">GitHub</a> para el aprendizaje de distintos módulos de DAM/DAW usando tecnologías como Kotlin, Vue.js o GitHub, siguiendo lo aprendido a lo largo de este tiempo. Y de paso mostrar dichos contenidos y prácticas en mi canal de <a href="https://www.youtube.com/@joseluisgs" target="_blank" rel="noopener noreferrer">YouTube</a>. Además potenciaré mi <a href="https://joseluisgs.dev/" target="_blank" rel="noopener noreferrer">web</a> siguiendo ese camino. Quien quiera colaborar es bienvenido/a.</p>
<h2>A nivel personal</h2>
<p>No de lo profesional vivo. Pero claro, si falla esa pata que es muy importante para desarrollarse interiormente pues uno siente un vacío importante. Siempre lo digo y siempre me pasa. Necesito separar e intentar que lo profesional no afecte a lo personal y que no todos los compañeros/as tienen que ser amigos/as como en el pasado y limitarme en parte a cosas profesionales.</p>
<p>Leganés y Madrid es mi lugar (por ahora). Espero tener algún amigo más para salir y compartir. Tengo una buena base de gente del club de tenis. Pero espero construir un grupo de amigos más amplio de tenis y de otras inquietudes. Que mola mucho echarse unas risas, unas cervezas y hablar de cualquier cosa pasando un buen rato. Debo aprender a sacar lo mejor o lo que más me aporte del lugar donde vivo y recursos y sitios hay para ello.</p>
<p>Espero avanzar más con la guitarra y volver a disfrutar de ello. Otra cosa con la que se me quitan las ganas o con la que la motivación no ha sido la adecuada. Pero espero que este año sea diferente y adquiera nuevas rutinas.</p>
<p>Quiero conocer más sitios cercanos de donde actualmente vivo.</p>
<p>Y por supuesto, y lo más importante, <strong>espero devolver todo el apoyo, cariño y amor a mi gente y sobre todo a los que me aguantan, escuchan y apoyan todos los días, con los que siempre tendré una deuda pendiente y que son mi verdadera motivación para seguir adelante y mejorar día a día</strong>.</p>
<div class="hint-container tip">
<p class="hint-container-title">Cosas a mejorar e intentar este año</p>
<ul>
<li>Intenta y, cuando todo falle, vuelve a intentar, pero analiza tus limitaciones: no puedes hacerlo todo solo, quizás solo una parte.</li>
<li>Descubre los talentos de tu alumnado y gente que te rodee e invítalos a dar lo mejor de sí. No todos pueden hacer todo, pero pueden ser lo mejor en algo.</li>
<li>Asimila que no podrás enseñar ni ayudar a todo el mundo y que la gente es mayor para tomar sus propias decisiones. Y si no quieren estar o no pueden superar los objetivos concretos, no va a ser por tu culpa.</li>
<li>Tolerancia cero a la mediocridad. No te dejes engañar por la apariencia ni perdones a los que engañan o hagan trampas. Si no tienen respeto por lo que hacen, no merecen tu respeto.</li>
<li>El que da todo lo que tiene nunca podrá ser reprochado por lo que hace.</li>
<li>Siempre hay una manera mejor y más fácil de hacer las cosas. Que el confort y la rutina no te cieguen, pero entiende que a otros les dé vértigo el cambio.</li>
<li>Facilita, empodera y apoya.</li>
<li>Siempre rodéate de las personas más inteligentes para aprender. Si tú eres el más inteligente (en mi caso lo dudo), quizás debas cambiar algo.</li>
<li>Si no crees en algo o no compartes una idea o visión, no te quedes callado. Y si no hay acuerdo, no te involucres.</li>
<li>No te quedes nunca con la duda.</li>
<li>Hay batallas que no merece la pena pelear. Asúmelas y deja que la vida siga su curso.</li>
<li>El cambio no es malo. Lo malo es no querer cambiar.</li>
<li>Si no es tu lugar, tu centro o titulación, recuerda el punto anterior.</li>
<li>Sigue caminando y no te pares. Si te paras, te quedas atrás.</li>
</ul>
</div>
]]></content:encoded>
      <enclosure url="https://lovingonme.com/2017/wp-content/uploads/2015/10/How-Can-I-Help.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Soy un Vue Lover</title>
      <link>https://joseluisgs.dev/posts/2021/2021-12-08-soy-un-vue-lover.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-12-08-soy-un-vue-lover.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Soy un Vue Lover</source>
      <description>Libertad y potencia en mi Framework preferido para Front-end</description>
      <category>Blog</category>
      <pubDate>Wed, 08 Dec 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Confieso, soy un Vue Lover y estoy orgulloso de serlo. Hace poco os hablé de cómo <a href="/posts/2021/2021-11-28-kotlin-en-mi-equipo.html" target="_blank">Kotlin siempre estaba en mi equipo</a>. Hoy me toca una de las entradas que más deseaba escribir. Vue.js es imprescindible para mí. Aunque lo he ido adelantando a lo largo de esta web y distintas entradas y proyectos. Creo que no quedan dudas de que me encanta Vue.js y poco a poco te voy a mostrar a qué se debe que sea parte fundamental de mi <a href="/posts/2021/2021-11-27-mi-stack.html" target="_blank">stack como desarrollador y profesor</a>.</p>
<!-- more -->
<h2>¿Qué es Vue.js?</h2>
<p><a href="https://vuejs.org/" target="_blank" rel="noopener noreferrer">Vue.js</a> es un framework progresivo de JavaScript de <a href="https://github.com/vuejs/vue" target="_blank" rel="noopener noreferrer">código abierto</a> para la construcción de aplicaciones web. Fue creado por <a href="https://evanyou.me/" target="_blank" rel="noopener noreferrer">Evan You</a>, y es mantenido por él y por el resto del equipo de Vue Core. Vue.js te permite trabajar con plantillas, usar reactividad en tus aplicaciones web, enrutamiento y hasta stores de datos. No voy a explicarte todo lo que se puede hacer de Vue.js, sino por qué me aporta mucho más que el resto de frameworks.</p>
<p>Si quieres conocer un poco más de Vue.js debes mirar este documental.</p>
<p style="text-align:center;">
<iframe width="560" height="315" src="https://www.youtube.com/embed/OrxmtDw4pVI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</p>
<h2>¿Por qué Vue.js?</h2>
<p><em>Esta es la gran pregunta</em>. Siempre me he decantado más por el Back-end que por el Front-end. Si trabajaba en clientes, generalmente era en móvil. Veía mucho código en JS desorganizado, usando jQuery, que lo alejaba de la lógica de cómo hacer aplicaciones grandes donde se procesan datos.</p>
<p>A la hora de querer profundizar en frameworks del lado del cliente tuve la duda entre elegir Angular, React o Vue. Creo que casi todo el mundo ha pasado por eso. Debo recordar que desarrollo por gusto y particularmente, y no me muevo por intereses empresariales. Además, mi trabajo principal es la docencia, formación e investigación.</p>
<p>Angular lo rechacé no por otra cosa, sino por los &quot;bailes&quot; que hace Google. Lo he sufrido en móvil. React no me gusta cómo trabaja y de nuevo me veo a expensas de los intereses de Facebook. Vue.js me parecía una alternativa real, potente y en medio de ambas. Capaz de coger lo mejor de los dos. Es un proyecto libre y escuchan a la comunidad y están atentos a sus necesidades.</p>
<p>Para mí, Vue.js, se adapta muy bien a mi forma de desarrollar software. Desarrollo basado en la descomposición de componentes que interactúan entre ellos. Puedo usar JavaScript o TypeScript cuando lo deseo y sobre todo me ha encantado su ecosistema.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://wallpapercave.com/wp/wp7134662.jpg" alt="Imagen">
</p>
<h2>Ecosistema de Vue.js</h2>
<p>Aquí es donde viene el punto fuerte. Vue.js no solo te permite hacer SPAs (Single Page Apps), tiene un ecosistema y una comunidad que lo hace bastante interesante y atrayente para mí. No solo se trata de hacer componentes que se comunican con eventos y propiedades, sino de ofrecer una serie de herramientas, utilidades y extras que te hacen más fácil el trabajo.</p>
<h3>Vue CLI y Vite</h3>
<p>Estos CLI te ofrecen toda la potencia para configurar y comenzar tus proyectos en Vue.js.</p>
<p><a href="https://cli.vuejs.org/" target="_blank" rel="noopener noreferrer">Vue CLI</a> te ofrece todas las opciones para hacer tu proyecto una realidad a nivel de configuración. <a href="https://vitejs.dev/" target="_blank" rel="noopener noreferrer">Vite</a> es toda una revolución. De nuevo Evan lo ha vuelto a hacer ofreciéndonos una herramienta que realmente potencia tu desarrollo.</p>
<h3>DevTools</h3>
<p><a href="https://devtools.vuejs.org/" target="_blank" rel="noopener noreferrer">Vue DevTools</a> es una extensión para Chrome que te ofrece la posibilidad de poder analizar y depurar tu aplicación Vue. Con ella puedes analizar los componentes, sus eventos y propiedades. También puedes analizar el enrutamiento y los estados de tu store.</p>
<h3>Vue Router</h3>
<p>El sistema de <a href="https://router.vuejs.org/" target="_blank" rel="noopener noreferrer">enrutamiento de Vue</a>, es uno de los que más me gustan. Eficaz y muy sencillo. Es modular, con rutas dinámicas, query, efectos de transición y mucho más. Sin duda no parecerá que estés en una SPA cuando navegues en tu navegador.</p>
<h3>Vuex</h3>
<p>El sistema de <a href="https://vuex.vuejs.org/" target="_blank" rel="noopener noreferrer">estado de Vue</a> es realmente interesante. Se podría decir que se basa en el patrón Flux o Redux. Nos permite manejar el estado entre confirmaciones y mutaciones. Además, es compatible con las DevTools.</p>
<h2>Mis preferencias en Vue.js</h2>
<p>No solo de los proyectos oficiales vive el desarrollador de Vue.js. Y entre la multitud de proyectos de su ecosistema realizados por la comunidad me gusta destacar los que más uso.</p>
<h3>Nuxt.js</h3>
<p><a href="https://nuxtjs.org/" target="_blank" rel="noopener noreferrer">Nuxt.js</a> es un framework que se utiliza para el desarrollo de aplicaciones web. Podemos utilizar Nuxt.js para crear aplicaciones estáticas (static pages), de una sola página (SPA) o de servidor (SSR). Nuxt.js es un framework que trabaja sobre Vue.js; esto quiere decir que tenemos todo lo bueno de Vue.js, pero contando ya con una organización y configuración establecidas desde el principio, que ayudan al desarrollador a enfocarse al 100% en el desarrollo.</p>
<h3>VuePress</h3>
<p>Es uno de mis proyectos preferidos. Esta web está desarrollada con <a href="https://vuepress.vuejs.org/" target="_blank" rel="noopener noreferrer">VuePress</a>. Nació porque Evan y su equipo necesitaban un sistema para documentar los proyectos y hacer blogs, sin la necesidad de montar grandes aplicaciones como puede pasar en Nuxt.js para hacer SSR. Algo que te permita escribir documentación con Markdown. Además, se adapta muy bien a mi filosofía de trabajo jamstack.</p>
<h3>Pinia</h3>
<p>No digas que es una simple store, es la STORE. <a href="https://pinia.esm.dev/" target="_blank" rel="noopener noreferrer">Pinia</a> es uno de los proyectos que más feliz me ha hecho y más tiempo y dolores de cabeza me ha quitado. Uno de los problemas que le veo a Vuex es su rigidez y pasos extras para trabajar con tipos incluso en Vue3. Pinia, nos ofrece una visión de cómo debe ser una store moderna, efectiva, intuitiva, extensible y muy ligera. Es compatible con DevTools. He felicitado a <a href="https://twitter.com/posva" target="_blank" rel="noopener noreferrer">Eduardo</a> por su excelente trabajo y recomiendo utilizar Pinia en todos los proyectos que puedas. De hecho marcará los pasos del próximo Vuex 5.</p>
<h3>Oruga</h3>
<p>¿Componentes para Vue a los que les puedes aplicar cualquier estilo CSS? Eso es <a href="https://oruga.io/" target="_blank" rel="noopener noreferrer">Oruga</a>. Me encanta <a href="https://bulma.io/" target="_blank" rel="noopener noreferrer">Bulma</a> y me encanta <a href="https://buefy.org/" target="_blank" rel="noopener noreferrer">Buefy</a>. Muchos de mis proyectos parten de esa base. Pero es verdad que a veces buscas algo más, o simplemente quieres compatibilidad con Vue3. Oruga está creado por el equipo de Buefy, con la idea de no depender de un CSS, sino de usar lo que consideres más oportuno en tu proyecto. <a href="https://twitter.com/walter_tommasi" target="_blank" rel="noopener noreferrer">Walter</a> ha hecho un trabajo fantástico con sus colaboradores. En su página web tienes un ejemplo de cómo usar <a href="https://oruga.io/documentation/#base-style" target="_blank" rel="noopener noreferrer">distintos estilos para los mismos componentes</a>.</p>
<h3>Quasar</h3>
<p>Uso <a href="https://quasar.dev/" target="_blank" rel="noopener noreferrer">Quasar</a> cuando quiero una aplicación para todo. Es verdad que puedo usar Electron para Vue para mis aplicaciones de escritorio, o tecnologías como <a href="https://docs.nativescript.org/tutorial/vue.html" target="_blank" rel="noopener noreferrer">NativeScript</a> o <a href="https://ionicframework.com/docs/vue/overview" target="_blank" rel="noopener noreferrer">Ionic</a> para crear aplicaciones móviles. Quasar me ayuda a crear todo lo que quiera fácilmente. Con Quasar puedes hacer casi de todo:</p>
<ul>
<li>SPAs (Single Page App)</li>
<li>SSR (Server-side Rendered App) (con cliente PWA)</li>
<li>PWAs (Progressive Web App)</li>
<li>BEX (Extensiones para Explorador)</li>
<li>Mobile Apps (Android, iOS, …) with Cordova or Capacitor</li>
<li>Multi-platform Desktop Apps (usando Electron)</li>
</ul>
<h3>Otros</h3>
<p>Seguro que me dejo muchos de los increíbles proyectos como <a href="https://vee-validate.logaretm.com/v4/" target="_blank" rel="noopener noreferrer">VeeValidate</a>, <a href="https://vueuse.org/" target="_blank" rel="noopener noreferrer">VueUse</a> o <a href="https://sli.dev/" target="_blank" rel="noopener noreferrer">Slidev</a>, pero te invito a que mires <a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener noreferrer">VueAwesome</a> y conozcas los proyectos alrededor de Vue.js.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://www.dreams.es/uploads/2020/vue-components.png" alt="Imagen">
</p>
<h3>Testing</h3>
<p>Para testear nada mejor que <a href="https://www.cypress.io/blog/2021/04/06/getting-start-with-cypress-component-testing-vue-2-3/" target="_blank" rel="noopener noreferrer">Cypress</a> y/o Jest usando <a href="https://vue-test-utils.vuejs.org/" target="_blank" rel="noopener noreferrer">Vue Test Utils</a>. En esta entrada te explico cómo testar componentes y aplicaciones Vue.js.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://www.monterail.com/hubfs/vue-optimized.png" alt="Imagen">
</p>
<h2>Aprende Vue.js</h2>
<p>Te recomiendo algunos de mis recursos favoritos para aprender Vue.js:</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://applover.com/wp-content/uploads/2020/11/vue_1-1.svg" alt="Imagen">
</p>
<p><strong>Recursos y grupos de trabajo</strong></p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://vuejs.org/" target="_blank" rel="noopener noreferrer">Documentación</a>.</p>
<p><span class="iconify" data-icon="akar-icons:youtube-fill"></span> <a href="https://escuelavue.es/" target="_blank" rel="noopener noreferrer">Escuela Vue</a>. Sin duda mi lugar favorito. Aprenderás rápido y con calidad.</p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://www.newline.co/30-days-of-vue" target="_blank" rel="noopener noreferrer">30 days of Vue.js</a>.</p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://www.meetup.com/es-ES/VueJS-Madrid/" target="_blank" rel="noopener noreferrer">Comunidad Vue.js Madrid</a>. Grandes personas y profesionales donde intercambiar y aprender sobre Vue.js, ¿dónde mejor?</p>
<p>¿Alguno más? Compártelo en comentarios en las redes sociales.</p>
<h2>Conclusión</h2>
<p>Vue.js es un gran framework para crear aplicaciones web. Pero no es todo. Hay mucho más que puedes hacer con Vue.js como has visto. Su curva de aprendizaje es sencilla y se adapta a tu forma de desarrollar ofreciéndote toda la libertad para hacer las cosas. <em><strong>El límite está en tu mente y Vue.js te ayudará a alcanzarlo</strong></em>.</p>
<p>En mi <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">repositorio</a> tienes ya algunos proyectos tipo. Si necesitas algo más no dudes en contactar conmigo.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://wallpaperaccess.com/full/4584357.jpg" alt="Imagen">
</p>]]></content:encoded>
      <enclosure url="https://media.publit.io/file/n0pLqGsm.png" type="image/png"/>
    </item>
    <item>
      <title>Kotlin siempre en mi equipo</title>
      <link>https://joseluisgs.dev/posts/2021/2021-11-28-kotlin-en-mi-equipo.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-11-28-kotlin-en-mi-equipo.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Kotlin siempre en mi equipo</source>
      <description>Uno de mis lenguajes multiusos preferidos</description>
      <category>Blog</category>
      <pubDate>Sun, 28 Nov 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Hace poquito te hablé de que había <a href="/posts/2021/2021-11-17-he-vuelto-a-java.html" target="_blank">vuelto a Java</a> y de que lo seguía odiando. En esta entrada entenderás parte del porqué. Quiero presentarte a Kotlin, uno de los lenguajes que más uso y que es pilar de mi <a href="/posts/2021/2021-11-27-mi-stack.html" target="_blank">stack de desarrollo</a>. Es elegante y es potente. Me facilita la vida tanto a nivel docente como a nivel de desarrollador. Quiero que conozcas a uno de los lenguajes más completos que existen en el mercado y, después de conocerlo, quizás te pueda servir para tu desarrollo dada su versatilidad.</p>
<!-- more -->
<h2>Un poco de historia</h2>
<p>Kotlin es un lenguaje de programación de tipado estático, multiparadigma y multiplataforma que se ejecuta más ágil sobre la máquina virtual de Java y que también puede ser compilado a código fuente de JavaScript. <em>Solamente con esto ya deberías prestarle toda la atención como desarrollador</em>. Te facilita que con un solo lenguaje te muevas por el mundo de la JVM, la web y Node.js y dispositivos móviles, así como distintos sistemas operativos.</p>
<p>Piensa que es un lenguaje muy joven y que ha bebido de las mejores cosas que existen en los lenguajes actuales y que evoluciona rápidamente. Imagina que debes diseñar Java en el 2016. Ten por seguro que no lo harías como hace 30 años, pensando en la cantidad de programadores C/C++ que había y en hacer algo similar para ellos. Kotlin es una visión moderna de cómo debería ser un lenguaje de programación pensado para programar en los tiempos actuales.</p>
<p>Kotlin cubre todos los paradigmas, por lo que es ideal para enseñar: programación estructurada, modular, orientada a objetos y funcional. Además, como he dicho, es 100% interoperable con Java, por lo que puede usar todas sus clases y paquetes, lo que te da un abanico de librerías listas para usar y las de NPM, por ejemplo, si lo usas como lenguaje a compilar a JavaScript. Es <em>Null-safe</em>, lo que significa que no hay ningún problema con el manejo de <em>null</em>.</p>
<p>Desde mi punto de vista, el único problema de Kotlin es que ha estado muy ligado a lenguaje solo para Android. Esta es una de sus características, pero no la única, y aunque importante, hay mucho más que lo hacen un lenguaje potente, elegante y muy efectivo para desarrollar todo tipo de proyectos. Poco a poco te iré desgranando sus ventajas como lenguaje y como herramienta docente y quizás pueda convencerte de que lo uses.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://wallpaperaccess.com/full/3098934.png" alt="Imagen">
</p>
<h2>Kotlin a nivel docente</h2>
<p>¿Por qué aprender a programar con Kotlin o por qué enseñarlo?</p>
<p>Después hablaré de todas sus bondades técnicas, pero como docente que soy, y profesor en desarrollo de <em>software</em>, te voy a contar mi punto de vista de por qué Kotlin es un lenguaje muy bueno para aprender/enseñar programación.</p>
<h3>Aprendizaje gradual</h3>
<p>Kotlin te permite moverte siguiendo los distintos paradigmas existentes. Puedes usarlo como lenguaje para programación estructurada y modular, exprimir la programación orientada a objetos y asentar las bases de programación funcional.</p>
<p>Por ejemplo, cuando comenzamos un curso de programación usando Java, un simple &quot;¡Hola, mundo!&quot; tiene una carga conceptual enorme. ¿Qué hacemos? Podemos optar por pedirle al alumnado un acto de fe, obviar algunos conceptos o intentar explicarlos hasta &quot;donde se pueda&quot;. Esto puede dar problemas como que el alumnado no entienda el porqué de algunos conceptos, o que no entienda qué es lo que hace el código, o que utilice por error o por defecto algunos elementos y adquiera vicios inapropiados, como el uso de <em>static</em> en todos lados.</p>
<p>Analicemos un &quot;¡Hola, mundo!&quot; en Java:</p>
<div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> HolaMundo</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> static</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">[] </span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">args</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    System</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">out</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Hola mundo!"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Como ves, nos aparecen varios conceptos como <em>public</em> (visibilidad), <em>class</em> (todo debe ser una clase sin haber comenzado los objetos), <em>static</em> (métodos de clases, sin ver OO), <em>void</em> (no devuelve nada), <em>main</em> (es el punto de entrada de la aplicación), <em>String[] args</em> (es un <em>array</em> de cadenas de texto), <em>System</em> (es una clase de Java que nos permite escribir en pantalla), <em>out</em> (indica la salida) <em>println</em> (es un método de la clase <em>System</em> que imprime en pantalla).</p>
<p>No voy a entrar si queremos hacer el típico programa que pida un dato o inicie un diálogo por consola y aparezca la clase <em>Scanner</em> y con ello la locura de por qué se hace así. Demasiada carga para tan poca cosa.</p>
<p>Repitamos el mismo ejemplo con Kotlin:</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> main</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">  println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"Hola mundo!"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Kotlin está muy bien diseñado y te permite una curva de aprendizaje gradual, donde utilizas lo que necesitas cuando lo necesitas, evitando escribir o utilizar conceptos que no necesitas y pueden ser más complicados de entender cuando uno comienza a programar y usando la sintaxis justa en el momento justo, y no siendo tan verboso como Java.</p>
<p>A partir de aquí puedes ir aprendiendo y dominando nuevos conceptos desde tipos, funciones, clases, objetos, colecciones, funciones lambda, etc.</p>
<h3>Sigues usando el ecosistema de Java</h3>
<p>Todas tus clases, librerías y programas existentes en Java son 100% compatibles con Kotlin. Es decir, si tu programa necesita una clase de Java, puedes usarla en Kotlin. Puedes hacer uso de Maven para instalar librerías de Java en tu proyecto e incluso de Maven/Gradle para gestionarlo.</p>
<h3>Di &quot;hola&quot; a JS</h3>
<p>Además, Kotlin te permite ser compilado a JavaScript. Si necesitas aplicaciones que hagan uso de JavaScript, puedes usar Kotlin y luego convertirlo a JavaScript. Además, te permite no solo aplicaciones web, sino en Node.js, o usar <em>frameworks</em> como Express, React, etc. Tienes NPM a tu disposición para cualquier librería.</p>
<h3>Lenguaje puente</h3>
<p>Kotlin te permite saltar de Java a JavaScript, con una sintaxis a medio camino entre el primero y TypeScript. Aprendiendo Kotlin podrás migrar fácilmente a estos dos últimos.</p>
<div class="hint-container tip">
<p class="hint-container-title"><span class="iconify" data-icon="cib:kotlin"></span> Kotlin y docentes</p>
<ul>
<li>Fácil de aprender.</li>
<li>Multiparadigma y multiplataforma.</li>
<li>JVM, Node.js, web, Maven Central y NPM.</li>
<li>Con Kotlin puedes aprender otros lenguajes facilitando el salto.</li>
</ul>
</div>
<h2>Elementos de Kotlin</h2>
<p>A continuación voy a resumirte elementos de Kotlin que pueden interesarte.</p>
<h3>Programación estructurada y modular</h3>
<ul>
<li>Kotlin es un lenguaje orientado a objetos con tipado estático. Aun así, define unos <a href="https://kotlinlang.org/docs/basic-types.html" target="_blank" rel="noopener noreferrer">tipos como primitivos</a> para facilitarnos su uso. También tenemos cadenas y <em>arrays</em>, ideales para comenzar a programar. De hecho, tiene los <a href="https://kotlinlang.org/docs/basic-types.html#string-templates" target="_blank" rel="noopener noreferrer"><em>String Templates</em></a>, que vienen fantásticos para no perderte concatenando elementos.</li>
<li>El <a href="https://kotlinlang.org/docs/control-flow.html" target="_blank" rel="noopener noreferrer">control de flujo</a> se puede realizar con elementos conocidos como condicionales, bucles definidos e indefinidos. Destacamos el <em>when</em>, que es un condicional múltiple supervitaminado, y el <em>for</em>, que al moverse con rangos nos facilita no usar comparaciones tediosas que pueden provocar errores.</li>
<li>Las <a href="https://kotlinlang.org/docs/functions.html" target="_blank" rel="noopener noreferrer">funciones</a> son otro punto a favor. Podemos definir funciones con un número variable de parámetros. Además, podemos definir funciones en línea, de extensión (de lo mejor que podrás encontrarte) y sobrecarga de operadores de la manera más sencilla.</li>
<li>Seguridad ante nulos: el <em>null</em> es un problema y desde su concepción se ha tratado de evitar de distintas maneras. Es aquí donde Kotlin tiene uno de sus puntos fuertes gracias a su facilidad para detectarlos, protegerse ante ellos y hacer instrucciones que realicen alguna acción en caso de no serlo, sin recurrir a complicadas comparaciones u objetos <em>Optionals</em>.</li>
</ul>
<h3>Programación orientada a objetos</h3>
<ul>
<li>Kotlin trabaja con clases y objetos: <a href="https://kotlinlang.org/docs/classes.html" target="_blank" rel="noopener noreferrer">Clases</a>. Podemos tener constructores en la misma definición de clase e inicializadores. Para instanciar no necesitamos <em>new</em>.</li>
<li>La <a href="https://kotlinlang.org/docs/inheritance.html" target="_blank" rel="noopener noreferrer">herencia</a> es simple para clases, pero múltiple para interfaces. Los métodos y variables de clase se incluyen en los objetos de tipo &quot;<em>companion</em>&quot;, lo que los dota de esa característica especial que tiene un elemento de clase.</li>
<li>Las <a href="https://kotlinlang.org/docs/properties.html" target="_blank" rel="noopener noreferrer">propiedades</a> y los <em>getter&amp;setter</em> ya van de serie y depende de su visibilidad y si se definen como <em>var</em> o <em>val</em> (variables o constantes). De esta manera podemos implementar el patrón <em>Builder</em> de una manera muy simple.</li>
<li>Clases específicas: gracias a notaciones como <a href="https://kotlinlang.org/docs/data-classes.html" target="_blank" rel="noopener noreferrer"><em>Data</em></a> podemos crear clases para solo almacenar datos (típicas POJO en Java) en una sola línea. Con <a href="https://kotlinlang.org/docs/object-declarations.html" target="_blank" rel="noopener noreferrer"><em>Object</em></a> podemos crear clases siguiendo el patrón <em>Singleton</em>. Con todo lo indicado quizás no necesitemos recurrir a Lombok más.</li>
</ul>
<p><strong>Kotlin</strong></p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">data</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> name: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> surname: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p><strong>Java</strong></p>
<div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  private</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  private</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> surname</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  private</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> id</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> getName</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> name;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> setName</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">name</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> name;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> getSurname</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> surname;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> setSurname</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> surname</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">surname</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> surname;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> getId</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> void</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> setId</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> id</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">    this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">id</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  @</span><span style="--shiki-light:#A626A4;--shiki-dark:#E5C07B">Override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> boolean</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> equals</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">Object</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic"> o</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> ==</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> o) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> true</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (o </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> ||</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> getClass</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> o</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getClass</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> false</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">    Person</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> person</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (Person) o;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (name </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ?</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> !</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">equals</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">name</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> !=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> false</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (surname </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ?</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> !</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">surname</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">equals</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">surname</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">surname</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> !=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">      return</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> false</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ?</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> id</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">equals</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">id</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> person</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">id</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> ==</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  @</span><span style="--shiki-light:#A626A4;--shiki-dark:#E5C07B">Override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> public</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> int</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> hashCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    int</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> result</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> name </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ?</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">hashCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    result </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 31</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> *</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> result </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">+</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (surname </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ?</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> surname</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">hashCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    result </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 31</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> *</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> result </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">+</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (id </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">!=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> ?</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B"> id</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">hashCode</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">:</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> 0</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> result;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  @</span><span style="--shiki-light:#A626A4;--shiki-dark:#E5C07B">Override</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> public</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> String</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> toString</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    return</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "Person{"</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">    "name='"</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> name </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">+</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ''' +</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">    ", surname='" + surname + ''' +</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">    "</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, id</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">'" + id + '''</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">    '}'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul>
<li><a href="https://kotlinlang.org/docs/generics.html" target="_blank" rel="noopener noreferrer">Genéricos</a>: con ellos podemos dotar de toda la potencia de la programación genérica, pensando en qué queremos hacer y no con qué tipo.</li>
</ul>
<h3>Colecciones</h3>
<p>Vale, podría ir en el apartado anterior. Aparte de poder usar todas las de Java, Kotlin tiene una serie de colecciones que puedes usar con una característica muy importante: poder trabajar con la mutabilidad e inmutabilidad.</p>
<p>De esta manera podemos trabajar, por ejemplo, con listas constantes de solo lectura. Una gran ventaja para no meter la pata. Es decir, una lista de solo lectura, que sería un dolor de cabeza en Java, aquí la tienes de serie. Por supuesto todas trabajan con estructuras funcionales si se da el caso, sin recurrir a los <em>streams</em> como parche o transformando los objetos.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://kotlinlang.org/docs/images/collections-diagram.png" alt="Imagen">
</p>
<h3>Programación funcional</h3>
<p>Kotlin es un lenguaje que se mueve muy bien con la programación funcional y las <a href="https://kotlinlang.org/docs/lambdas.html" target="_blank" rel="noopener noreferrer">lambdas</a>, pues fue concebido para ello y no es un añadido como en otros lenguajes. Por lo que todo resulta muy natural y sencillo, pues bebe de su &quot;primo&quot; Scala.</p>
<p>A partir de aquí tenemos una serie de operaciones usando este paradigma para el manejo de <a href="https://kotlinlang.org/docs/collection-operations.html" target="_blank" rel="noopener noreferrer">colecciones</a>. Operaciones para realizar transformaciones, filtrado, adiciones y sustracciones, agrupamiento, recolección de datos, ordenamiento o funciones de agregación. Lo que necesites y más.</p>
<h3>Programación concurrente</h3>
<p>Kotlin puede hacer uso de hilos, <em>callbacks</em> y <em>futures</em> (promesas de Java) para hacer uso de la programación concurrente y mecanismos de sincronización conocidos (semáforos, monitores, <em>synchronized</em>, etc.). Pero su punto fuerte es el uso de las <a href="https://kotlinlang.org/docs/coroutines-guide.html" target="_blank" rel="noopener noreferrer">corrutinas</a>. Una corrutina (definida también como un hilo liviano y optimizado) es un conjunto de sentencias que realizan una tarea específica, con la capacidad de suspender o reanudar su ejecución sin bloquear un hilo. Esto permite que tengas diferentes corrutinas cooperando entre ellas, suspendiéndose y reanudándose en puntos especificados por ti o por Kotlin. Con ellas podemos ejecutar código en paralelo y/o asíncrono de una forma muy sencilla y óptima. No significa que exista un hilo por cada corrutina; al contrario: puedes ejecutar varias en uno solo.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">suspend</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> fetchTwoDocs</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">  coroutineScope</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> deferredOne </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> async</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">fetchDoc</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> deferredTwo </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> async</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">fetchDoc</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">2</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    deferredOne.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">await</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    deferredTwo.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">await</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Con ellas podrás crear tu propio procesamiento concurrente optimizando los recursos del sistema. Este comportamiento de las corrutinas en Kotlin te permite:</p>
<ul>
<li>Reducir recursos del sistema al evitar la creación de grandes cantidades de hilos.</li>
<li>Facilitar el retorno de datos de una tarea asíncrona.</li>
<li>Facilitar el intercambio de datos entre tareas asíncronas.</li>
</ul>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/max/2000/1*I3OMp4jIytzR7GKpRkEiAA.png" alt="Imagen">
</p>
<h3>Programación de servicios</h3>
<p>Kotlin te permite trabajar con el <a href="https://kotlinlang.org/docs/server-overview.html#deploying-kotlin-server-side-applications" target="_blank" rel="noopener noreferrer"><em>Back-end</em></a> de tu aplicación de manera muy efectiva. Puedes usar <a href="https://spring.io/guides/tutorials/spring-boot-kotlin/" target="_blank" rel="noopener noreferrer">Spring</a> sin ningún problema y con una gran cantidad de funcionalidades. Si tu excusa para no usar Kotlin en el <em>back</em> era Spring, ya se te ha acabado. Con ello podrás hacer, por ejemplo, tu API REST.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://raw.githubusercontent.com/spring-guides/tut-spring-boot-kotlin/master/images/initializr.png" alt="Imagen">
</p>
<p>Pero quizás quieras crear servicios web de una manera asíncrona y sencilla. Kotlin pone a tu disposición <a href="https://ktor.io/" target="_blank" rel="noopener noreferrer">Ktor</a> para ello. No necesitas Spring o Express para hacer una API REST o GraphQL de manera rápida.</p>
<p>Por otro lado, puedes usar <a href="https://www.baeldung.com/kotlin/jpa" target="_blank" rel="noopener noreferrer">JPA</a> sobre Hibernate o Spring Data para trabajar en tu acceso a datos a cualquier tipo de base de datos. También podemos usar JAXB para XML, o GSON o Jackson para JSON, Java NIO2 o las propias funciones de Kotlin para ficheros.</p>
<p>¿Quieres más? Necesitas moverte con <a href="https://www.baeldung.com/kotlin/javascript" target="_blank" rel="noopener noreferrer">Node.js</a>. ¿Un <em>back</em> sobre Express?, ¿librerías de NPM? Con Kotlin puedes hacerlo. Puedes crear tus aplicaciones en Node.js.</p>
<h3>Desarrollo de clientes</h3>
<p>Con Kotlin puedes crear el cliente que quieras o como quieras. JVM: puedes usar JavaFX o cualquier paquete de Java para implementar tu cliente. JavaScript/Web: puedes usar Kotlin para crear tu cliente o aplicación web como lenguaje tipado. Puedes trabajar en <a href="https://kotlinlang.org/docs/js-project-setup.html" target="_blank" rel="noopener noreferrer">Vanilla JavaScript</a> o con <em>frameworks</em> tan conocidos como <a href="https://kotlinlang.org/docs/js-get-started.html" target="_blank" rel="noopener noreferrer">React</a>.</p>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://i1.wp.com/hipertextual.com/wp-content/uploads/2019/06/hipertextual-kotlin-lenguaje-oficial-android-que-quiere-comerse-java-2019922347.jpg" alt="Imagen">
</p>
<p>Por supuesto puedes trabajar para móviles: todos conocemos su potencial sobre <a href="https://developer.android.com/kotlin" target="_blank" rel="noopener noreferrer">Android</a>. Pero además nos permite realizar desarrollos para <a href="https://kotlinlang.org/lp/mobile/" target="_blank" rel="noopener noreferrer">iOS</a>.</p>
<p>Pero el gran paso para poder hacerlo todo y en todas las plataformas con Kotlin es <a href="https://www.jetbrains.com/lp/compose-mpp/" target="_blank" rel="noopener noreferrer">Compose</a>, o cómo crear interfaces de usuario de manera declarativa y con ello poder crear nuestras aplicaciones para Windows, Mac, Linux, Android, iOS, web, etc.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://raw.githubusercontent.com/JetBrains/compose-jb/master/artwork/readme/apps.png" alt="Imagen">
</p>
<h3>Testing</h3>
<p>Obviamente para nuestros <a href="https://kotlinlang.org/docs/mpp-run-tests.html" target="_blank" rel="noopener noreferrer">tests</a> podemos hacer uso de JUnit o Mockito. Pero además tenemos nuestras <a href="https://kotlinlang.org/api/latest/kotlin.test/" target="_blank" rel="noopener noreferrer">librerías propias</a> para ello.</p>
<h3>Desarrollo nativo</h3>
<p>Si tu problema es el escepticismo del rendimiento sobre JVM, también puedes <a href="https://kotlinlang.org/docs/native-get-started.html#count-the-unique-letters-in-your-name" target="_blank" rel="noopener noreferrer">desarrollar nativamente</a> con interoperabilidad con <a href="https://kotlinlang.org/docs/native-c-interop.html" target="_blank" rel="noopener noreferrer">C</a> u <a href="https://kotlinlang.org/docs/native-objc-interop.html" target="_blank" rel="noopener noreferrer">Objective C</a>.</p>
<h3>Ciencia de los datos</h3>
<p>Kotlin puede ser una gran alternativa para la <a href="https://kotlinlang.org/docs/data-science-overview.html" target="_blank" rel="noopener noreferrer">ciencia de los datos</a>. Puedes usar sus librerías e integración, por ejemplo, con terceros.</p>
<h3>DSL y Kotlin</h3>
<p>Kotlin puede ser usado como <a href="https://www.jetbrains.com/es-es/mps/concepts/domain-specific-languages/" target="_blank" rel="noopener noreferrer">lenguaje de dominio específico</a> para <a href="https://proandroiddev.com/writing-dsls-in-kotlin-part-1-7f5d2193f277" target="_blank" rel="noopener noreferrer">resolver determinadas tareas</a> de una <a href="https://medium.com/kotlin-en-android/kotlin-dsl-introduccion-f112557f5662" target="_blank" rel="noopener noreferrer">manera rápida y muy legible</a>.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/max/7680/1*noONs72PvMEkwVfQbbG75Q.png" alt="Imagen">
</p>
<h3>Patrones de diseño</h3>
<p>Kotlin puede ser usado sobre <a href="https://github.com/dbacinski/Design-Patterns-In-Kotlin" target="_blank" rel="noopener noreferrer">patrones de diseño</a> y aprovechar su sintaxis para hacerlos más asequibles y fáciles de implementar: ya hemos hablado de cómo conseguir por ejemplo <em>Singleton</em> con la notación <em>object</em>. A continuación mostramos un simple ejemplo para el patrón <em>Builder</em>.</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> FoodOrder</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> private</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> constructor</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> bread: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">?,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> condiments: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">?,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> meat: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">?,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> fish: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">?) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  data</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> class</span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B"> Builder</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> bread: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> condiments: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> meat: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    var</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> fish: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">      fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> bread</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(bread: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> apply</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.bread </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> bread }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">      fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> condiments</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(condiments: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> apply</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.condiments </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> condiments }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">      fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> meat</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(meat: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> apply</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.meat </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> meat }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">      fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> fish</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(fish: </span><span style="--shiki-light:#C18401;--shiki-dark:#E5C07B">String</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> apply</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">this</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.fish </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> fish }</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">      fun</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> build</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> FoodOrder</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(bread, condiments, meat, fish)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">}</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>Shell y Scripting</h3>
<p>Quizás te guste Node.js y su consola. Si ejecutamos el compilador sin parámetros, entraremos en la <a href="https://kotlinlang.org/docs/command-line.html#run-the-repl" target="_blank" rel="noopener noreferrer">consola de Kotlin</a>, donde podemos ejecutar código de Kotlin y ver sus resultados.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://kotlinlang.org/docs/images/kotlin-shell.png" alt="Imagen">
</p>
<p>Además podemos usar Kotlin como lenguaje de <em>scripting</em> con ficheros <code>.kts</code>. Un ejemplo:</p>
<p>Ejemplo de <em>script</em> llamado:</p>
<div class="language-kotlin line-numbers-mode" data-highlighter="shiki" data-ext="kotlin" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-kotlin"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">import</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> java.io.File</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Toma el path pasado en -d, o el path actual</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> path </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> if</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (args.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">contains</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"-d"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)) args[</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">1</span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2"> +</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> args.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">indexOf</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"-d"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)]</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">else</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> "."</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">val</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> folders </span><span style="--shiki-light:#383A42;--shiki-dark:#56B6C2">=</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> File</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(path).</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">listFiles</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { file </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">-></span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> file.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">isDirectory</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">() }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">folders?.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">forEach</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { folder </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">-></span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> println</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(folder) }</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Podemos llamarlo como:</p>
<div class="language-shell line-numbers-mode" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">$</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> kotlinc</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -script</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> list_folders.kts</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> -d</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> &#x3C;</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">path_folde</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">r></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h2><span class="iconify" data-icon="cib:kotlin"></span> Kotlin vs <span class="iconify" data-icon="cib:java"></span> Java</h2>
<p>No se trata de una guerra, sino de que cada cual elija según sus intereses programando.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://www.spaceotechnologies.com/wp-content/uploads/2019/09/Java-Vs-Kotlin.png" alt="Imagen">
</p>
<h2>Aprende Kotlin</h2>
<p><strong>Cursos y documentación</strong></p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://kotlinlang.org/docs/home.html" target="_blank" rel="noopener noreferrer">Documentación</a></p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://play.kotlinlang.org/byExample/overview" target="_blank" rel="noopener noreferrer">Ejemplos</a></p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://play.kotlinlang.org/koans/overview" target="_blank" rel="noopener noreferrer">Koans</a></p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://www.coursera.org/learn/kotlin-for-java-developers" target="_blank" rel="noopener noreferrer">JetBrains Coursera</a>.</p>
<p><span class="iconify" data-icon="bx:bxs-file-doc"></span> <a href="https://hyperskill.org/tracks" target="_blank" rel="noopener noreferrer">JetBrains Academy</a>.</p>
<p><strong>En YouTube</strong>, mis preferidos y siempre recomendados son:</p>
<p><span class="iconify" data-icon="akar-icons:youtube-fill"></span> Antonio Leiva <a href="https://www.youtube.com/c/DevExperto" target="_blank" rel="noopener noreferrer">DevExperto</a>.</p>
<p><span class="iconify" data-icon="akar-icons:youtube-fill"></span> Brais Moure <a href="https://www.youtube.com/c/MouredevApps" target="_blank" rel="noopener noreferrer">MoureDev</a>.</p>
<h2>Conclusión</h2>
<p>Kotlin ha supuesto un gran avance en el ecosistema de la JVM; de hecho, muchos de los avances de Java desde la versión 8 vienen de &quot;inspirarse&quot; en Kotlin. No voy a negar que coincido con las palabras de un desarrollador del ecosistema de Spring: <em>&quot;Lo mejor que le puede pasar a un desarrollador de Java es codificar en Kotlin&quot;</em>. También espero haber conseguido que dejes de ver a Kotlin como el lenguaje que solo sirve para programar en Android y lo tengas en cuenta para más tipos de desarrollos.</p>
<p>Uno de los objetivos que tengo pendientes, y quiero realizar, es mostrar cómo muchas partes del temario de DAM y DAW se pueden desarrollar con Kotlin. Para ello realizaré apuntes y prácticas tipo. Los compartiré en mi repositorio y web. De hecho, como ves, cubre todo el temario de ambos ciclos y más. Deseo que tú, como alumno o docente, te animes a usar Kotlin, no solo para Android, y con esta entrada hayas descubierto cómo este lenguaje te puede aportar experiencias muy positivas para tus desarrollos. Y si podemos compartirlo, mucho mejor.</p>
<p>En mi <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">repositorio</a> tienes ya algunos proyectos tipo. Si necesitas algo más, no dudes en contactar conmigo.</p>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://www.adesso-mobile.de/wp-content/uploads/2021/02/kotlin-einführung.jpg" alt="Imagen">
</p>
]]></content:encoded>
      <enclosure url="https://content.techgig.com/photo/82324241/5-reasons-why-you-should-learn-kotlin-in-2021.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Mi Stack como programador</title>
      <link>https://joseluisgs.dev/posts/2021/2021-11-27-mi-stack.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-11-27-mi-stack.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Mi Stack como programador</source>
      <description>O como paso de algunas modas y soy feliz</description>
      <category>Blog</category>
      <pubDate>Sat, 27 Nov 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Cuando uno lleva ya algún tiempo en el mundo del desarrollo, ya sea como programador o simplemente enseñando cómo serlo, va viendo un montón de lenguajes y tecnologías, así como terminología que va yendo y viniendo como las olas en el mar. En este <em>post</em>, te comento mis motivos de cómo a nivel personal mi experiencia como desarrollador es más positiva con algunas tecnologías que con otras.</p>
<!-- more -->
<h2>El mundo del desarrollo de software y sus cambios</h2>
<p>El mundo del desarrollo del <em>software</em> es un mundo cambiante, algo volátil y a la vez muy conservador. Esto quiere decir que todos los días aparecen librerías, <em>frameworks</em> o versiones de lenguajes, pero que estos se establezcan como referentes es complicado. A la vez, aparecen términos que se ponen de moda y muchos los abrazan como nuevas religiones (incluso creando fanatismo por ellos) o se rescatan antiguas canciones, como los grandes éxitos de los años pasados que vuelven a estar de moda, o simplemente se les maquilla un poco y se les da una nueva terminología.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://i.ytimg.com/vi/Pn5znSOGHcs/maxresdefault.jpg" alt="Imagen">
</p>
<h2>Lo que busco al programar</h2>
<p>Debo identificar parte de mis tres facetas: el profesor, el desarrollador profesional y el desarrollador a nivel personal. Algunas poco a poco se van entremezclando.</p>
<p>En esta misma web, en el apartado de Tecnologías, encontrarás una lista de las tecnologías que he utilizado con más frecuencia.</p>
<p>A nivel docente, uso lo que creo más adecuado dependiendo del marco empresarial donde mi alumnado se va a incorporar. No me caso con temarios antiguos o tecnologías concretas. Mi protagonista es mi alumnado y a él me debo. Mi obligación es que se incorpore a las empresas de desarrollo con garantías de desarrollar su potencial desde el comienzo. La ley me marca unas competencias, me fija unos contenidos básicos, pero yo elijo de acuerdo al contexto cómo y con qué.</p>
<p>A nivel profesional, a veces los clientes buscan algo específico, pero también quieren una solución que se adapte a sus necesidades. Muchas veces escuchan determinadas propuestas; otras, como profesional que uno es, pues implementa una solución que se adapte a las necesidades de los clientes con las tecnologías indicadas.</p>
<p>A nivel personal, no me caso con nadie y, como con la música o las series, tengo mis gustos. Pero siempre busco lo mismo: efectividad y calidad. Si puedo &quot;matar varios pájaros&quot; de un tiro, lo haré (lo admito, soy vago). Y como bien se dice, &quot;para gustos, colores&quot; y como además me lo puedo permitir, hago lo que quiero y como quiero, pero siempre con coherencia. También busco escapar de la monotonía de enseñar siempre con lo mismo y de algunas caras de &quot;asco&quot; que me encuentro entre las aulas.</p>
<h2>Preferencias en Front-end</h2>
<p>Aquí es donde he tenido siempre las cosas claras. Trabajo con <a href="/posts/2021/2021-07-22-me-gusta-javascript.html" target="_blank">JavaScript o TypeScript</a>, pero siempre con <a href="https://vuejs.org/" target="_blank" rel="noopener noreferrer">Vue.js</a>. ¿Por qué? Un día daré los motivos, pero puedo hacer desde aplicaciones web, escritorio (Electron) o móviles (Quasar, Ionic, NativeScript). Lo acompaño de Pinia como <em>store</em> de referencia. Esa es la base. Además puedo usar Tailwind, Windi CSS y Bulma como ayuda para el CSS. Aprovecho las virtudes de Oruga y Quasar para hacer lo que quiero. Hasta esta web está hecha con Vue.js.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.freepik.com/vector-gratis/desarrollo-front-end-plano_107173-16796.jpg" alt="Imagen">
</p>
<h2>Preferencias en Back-end</h2>
<p>A día de hoy me muevo de nuevo en el mundo de JavaScript y TypeScript con más comodidad gracias a Node.js. Con ello desarrollo REST y GraphQL si es el caso. Si necesito ir a Spring recurro a <a href="/posts/2021/2021-02-10-detalle-de-Kotlin.html" target="_blank">Kotlin</a>. Quizás por la pesadez de darlo siempre en clase rehúyo de usar Java. Me parece pesado y antiguo, y aunque desde Java 8 me gusta, creo que Kotlin me aporta mucho más. Para mí no es el lenguaje de &quot;solo Android&quot;, es mucho más y me gusta para muchos aspectos de <em>back-end</em>. Estos días hablaré de ello. Para almacenamiento me muevo entre Firebase, MongoDB y MariaDB principalmente, dependiendo de lo que vaya a hacer.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://clarusway.com/wp-content/uploads/2021/06/backend-developer-technical-skills-1.png" alt="Imagen">
</p>
<h2>Preferencias en desarrollo móvil</h2>
<p>Reitero lo anterior: JavaScript, TypeScript, Vue.js y Kotlin. No necesito más. Eso sí, con sus añadidos.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://d540vms5r2s2d.cloudfront.net/mad/uploads/Mobile_app_development-life-cycle.jpg" alt="Imagen">
</p>
<h2>Repositorios y despliegues</h2>
<p>Aquí lo tengo muy claro. Siempre <a href="/posts/2021/2021-05-11-github-imprescindible.html" target="_blank">GitHub</a> y <a href="/posts/2021/2021-05-20-gitkraken-superpoder-git.html" target="_blank">GitKraken</a>. Esta web incluso la despliego en GitHub Pages con GitHub Actions. GitKraken es mi herramienta de cabecera. Luego puedo usar Firebase, Heroku y Netlify. No puedo vivir sin Docker, y es una de las herramientas que más utilizo. De hecho, esta página web sigue la filosofía <a href="https://www.genbeta.com/desarrollo/jamstack-nuevo-enfoque-desarrollo-web-impulsado-exito-financiero-netlify" target="_blank" rel="noopener noreferrer">JAMstack</a>. Me muevo entre Jest, JUnit y Mockito para test.</p>
<h2>Mis IDEs y herramientas</h2>
<p>Aquí vuelvo a tener las cosas muy claras. Ganan por goleada Visual Studio Code como editor y JetBrains (IntelliJ IDEA) como IDE. Ambas opciones por potencia y personalización. Y como curiosidad uso ZSH como <em>shell</em> de terminal, tanto en Linux como en Mac. No uso Windows para programar.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://tiposdeide.files.wordpress.com/2018/10/1533172648445_1.jpg" alt="Imagen">
</p>
<h2>Conclusión</h2>
<p>No voy a engañar a nadie, quizás no sean las mejores opciones, pero me parecen efectivas a nivel personal para lo que hago, y como bien remarco, es a nivel personal. Obviamente profesionalmente me muevo por mucho más. Pero esta entrada quería hablar de cómo disfruto más y de qué me hace sentir más productivo. Y muchas veces lo hago asqueado por modas o tendencias, que lo único que hacen es refritos de conceptos ya existentes y a veces utilizándolos erróneamente. Pero eso dará para otra entrada.</p>
]]></content:encoded>
      <enclosure url="https://miro.medium.com/max/1400/1*f3Jrn27ggVFkwy9fw_ExOg.jpeg" type="image/jpeg"/>
    </item>
    <item>
      <title>He vuelto a JAVA y lo sigo odiando</title>
      <link>https://joseluisgs.dev/posts/2021/2021-11-17-he-vuelto-a-java.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-11-17-he-vuelto-a-java.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">He vuelto a JAVA y lo sigo odiando</source>
      <description>O como la dependencia a un lenguaje te puede hacer fracasar</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 17 Nov 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Aprovecho que mi alumnado está con un examen para lanzar una serie de reflexiones. Como docente y desarrollador que soy, trabajo con distintos lenguajes. Es por ello por lo que para cada proyecto analizo pros y contras, y siempre elijo el que me ofrezca mayor productividad y seguridad para el desarrollo. ¿Odio JAVA? Por supuesto que no. Pero el título es sugerente para captar tu atención y para advertirte que no dependas de una sola opción.</p>
<!-- more -->
<h2>Programación y Docencia</h2>
<p>Está claro que cuando enseñas buscas dos cosas: un lenguaje con el que desarrollar unas competencias demandadas por la industria, donde poder desarrollar un marco teórico-práctico. A veces no es el mejor o peor, sino el más adecuado.</p>
<p>En mi anterior centro, el conjunto de profesores cambiábamos de lenguaje con el objetivo de que el alumnado no fuera dependiente y aprendiera a discernir entre las ventajas e inconvenientes de cada uno de ellos. Creo que esto era una gran ventaja y virtud. De hecho, se desarrollaban diversas habilidades mucho más rápido y el alumnado podría aprender en DAM: Java, Kotlin, C#, Python, Dart con Flutter, etc. Mientras en DAW tenían Java, PHP, JavaScript, TypeScript. Sin hablar de <em>frameworks</em>, como Spring Boot, Laravel, Express, etc. Conclusión: alumnos más demandados y completos.</p>
<p>Este año, en mi nuevo centro, esto no es así. La apuesta principal en DAM es con Java. Es lógico, porque es un lenguaje base para enseñar todo. Obviamente otros profesores enseñan Python o PHP, pero todo se resume a usar JAVA como protagonista.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://poster.keepcalmandposters.com/default/5884760_keep_calm_and_hate_java.png" alt="Imagen">
</p>
<h2>Más allá de JAVA</h2>
<p>No voy a negar que Java me puede gustar más o menos, o que hay alternativas mejores. De hecho, este año en Programación he cambiado de lenguaje para usar dos distintos y el año que viene me planteo algunas cosas darlas con Kotlin, como este año ha sido TypeScript. Puedo desarrollar los mismos conceptos con ellos y así rompo la dependencia de asociación que crea el alumnado con el lenguaje. Para mí es muy importante que programen con la cabeza. El lenguaje es solo un instrumento y veo fundamental desarrollar los contenidos sin crear esa dependencia.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://i.redd.it/m7oposzkd3b71.jpg" alt="Imagen">
</p>
<p>En cualquier caso, cuando lleguen a una empresa nadie les asegura que vayan a trabajar solo con Java y deben ser capaces de explotar lo aprendido con cualquier lenguaje.</p>
<p>No voy a negar que Java ha mejorado mucho, que cada vez es más ágil: API Stream, menos verboso y muchas más cosas. La mayoría de ellas sacadas de su primo Kotlin y de la propia evolución de otros lenguajes. Pero sigue sin gustarme. Y no me gusta cuando veo esa imposición a usarlo docentemente para todo y sacrificar la libertad que el conocer distintos lenguajes te puede dar para expresarte como desarrollador. No me gusta el &quot;<em>only JAVA</em>&quot;. Me frustra ver estadísticas de mi perfil con las horas de su uso y ver que a mí no me motiva. Lo entiendo y lo avalo como docente, pero sé también que hay que trabajar para no depender solo de él teniendo excelentes alternativas y que el paso de una a otra es cada vez menor. Por ejemplo, de Kotlin a Java te sirve todo y además ya existe hasta la plantilla de Spring Boot.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://i.pinimg.com/originals/37/07/23/37072349abbbdf9aa86321eb07e1804e.png" alt="Imagen">
</p>
<h2>Conclusión</h2>
<p>Me gusta Java, sí, pero no es lo único que se debe aprender. Y animo a mi alumnado a que no se sienta encorsetado ni atado a ello. Cada día son más fáciles los cambios, y si tú aprendes conceptualmente a programar (o usas la cabeza) verás que el lenguaje es solo el medio. Y más sabiendo que muchos beben de la misma sintaxis del &quot;abuelito&quot; C.</p>
<blockquote>
<p>&quot;Elige siempre lo que te haga ser más productivo y ágil para el problema que vas a resolver&quot;.</p>
</blockquote>
<blockquote>
<p>Alguien que pensó en eso 😀</p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://www.teahub.io/photos/full/22-220143_java-java-wallpaper-hd.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Rendirse no es opción</title>
      <link>https://joseluisgs.dev/posts/2021/2021-11-11-rendirse-no-es-opcion.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-11-11-rendirse-no-es-opcion.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Rendirse no es opción</source>
      <description>Y fallar está permitido</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Thu, 11 Nov 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>En esta nueva etapa estoy experimentando muchas cosas nuevas, y otras no tan nuevas, como las que se repiten con el alumnado en los primeros meses: frustración, rendición o simplemente asimilar que no todo sale a la primera. A continuación te cuento lo que pienso de esta situación.</p>
<!-- more -->
<h2>El problema de la inmediatez</h2>
<p>En esta sociedad en la que vivimos, nos hemos acostumbrado a que todo está a un clic de distancia: desde mandar un mensaje a buscar información, delegando en ello lo que Google nos quiera mostrar. Poco a poco nos olvidamos de que hay algunos problemas que requieren maduración o simplemente asimilar que no se resuelven con un simple clic.</p>
<p>Desarrollar aplicaciones informáticas consiste en resolver un puzle aplicando distintas piezas, partiendo de un análisis del problema, diseñando la solución, pasando a la implementación de la misma, para comprobar y verificar que funciona y es válida.</p>
<p>Al vivir en el mundo de la inmediatez y de un solo clic, queremos suprimir las dos primeras etapas y codificar todo sin pararnos a analizar para luego diseñar la solución. Pero es que programar no solo es codificar en un lenguaje: es descomponer un problema en pasos, una serie de sentencias, estructuras, objetos, usar librerías... No se trata de escribir por escribir.</p>
<p>Y es ahí donde nace el problema. Si lo unimos al problema de base de que se ha partido de no fomentar esta etapa, nos encontramos con proyectos de desarrolladores/as que son incapaces de resolver un problema porque no tienen la paciencia necesaria para llegar a la solución o simplemente para aceptar el reto de resolverlo. Y no es la primera vez que parte de mi alumnado se deja los módulos por no saber gestionar estas situaciones.</p>
<h2>Aceptando el reto</h2>
<p>Y he hablado de reto. Porque programar es resolver problemas que a veces nos retan y, como todo reto, implica aceptar la frustración inicial de no tener los medios para resolverlos, investigar para ello, alcanzar las competencias necesarias y posteriormente intentarlo. Esto no nos garantiza que lo consigamos a la primera, pero si nos rendimos nunca lo conseguiremos. Eso creo que está claro.</p>
<h2>Frustración</h2>
<p>Cuando desarrollamos, debemos aprender a lidiar con la frustración y con aquella parte de nosotros que nos boicotea diciendo que no servimos. La verdad es que, como nos han inculcado que todo es fácil y maravilloso, que vivimos en un mundo ideal donde con un clic todo se hace realidad, nos olvidamos de que las soluciones hay que trabajarlas y que madurar algo implica dar pequeños pasos pero constantes hasta conseguirlo. A veces tropezaremos, pero no debemos desistir. Siento decirte que no todo es fácil, pero no nos olvidemos de que todo es posible (la mayoría de las veces).</p>
<h2>Ten la mente abierta</h2>
<p>Por otro lado, siempre habrá el alumno/a que ante nuevos contenidos, técnicas o herramientas se vea reacio/a a aplicarlos. Total, si ya sabe resolverlos, ¿para qué complicarse? Bueno, pues porque hay que avanzar, aceptar el reto, y cuantas más herramientas técnicas dominemos mejor podremos afrontar nuevos problemas. No todo se resuelve con un martillo o destornillador: a veces necesitaremos un bisturí. El problema es que no gestionan bien la salida de su zona de confort. Pero retarnos, seguir avanzando y autoaprender es una obligación.</p>
<h2>Fallar está permitido</h2>
<p>A día de hoy, nunca le he dicho a mi alumnado que no se puede fallar. Y tampoco conozco a nadie que nunca haya fallado. De hecho, yo mismo he aprendido mucho más solucionando un error o <em>bug</em> de dos horas que si me hubiese salido todo a la primera. Gracias a ese <em>bug</em> he investigado varias soluciones alternativas o he comprendido cómo funciona una librería determinada. Me ha hecho crecer. Seguiré fallando, lo sé, me lo permito. Fallaré nuevamente y fallaré mejor. Seré constante. Leeré el error que me lance el IDE y aceptaré dicho reto. Como pone el tatuaje de uno de mis tenistas favoritos, Stan Wawrinka:</p>
<blockquote>
<p>&quot;Always tried, always failed, no matter, try again, fail again, fail better&quot;.</p>
</blockquote>
<blockquote>
<p>Samuel Beckett</p>
</blockquote>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://i.pinimg.com/originals/9e/1d/b5/9e1db50599aa69bb7bf5687b3f61a5f0.png" alt="Imagen">
</p>
<p>Quizás esa educación de jugar al tenis, de fallar y olvidarse, de ir a por el siguiente punto sin quedarse en el error, me inculcó esa capacidad. Piensa en Rafa Nadal: siempre se le ha valorado su mentalidad.</p>
<h2>La solución parte de ti</h2>
<p>Una buena actitud proactiva te va a ayudar mucho. No te lances mensajes negativos, pero tampoco vayas con un exceso de confianza. La peor de las batallas son las que no nos atrevemos a luchar y en las que nos rendimos antes de hacerlo. Para recoger la cosecha antes hay que sembrar, y te vas a ensuciar las manos. Esto es un viaje. Si aprendes estos hábitos, poco a poco lo conseguirás. Es verdad que no todo el mundo madura y crece como programador al mismo ritmo. Pero no te voy a engañar: hay cosas que no conseguirás y deberás aceptar dicha derrota para poder seguir avanzando. Uno no se ahoga por caer en el mar, sino por quedarte sumergido en él.</p>
<p>Tampoco busques excusas ni eches balones fuera. Nunca has tenido tantos medios para aprender. El profesor no es tu enemigo. Piensa también en lo que aportas tú en este proceso de formación en el que te encuentras.</p>
<p>No será la primera vez que Son Goku debe entrenar para salvar el universo y la tierra. No era el más talentoso ni el más listo, pero se dejaba la piel y aceptaba el reto.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://i.pinimg.com/originals/b3/6f/0b/b36f0b89158400dd025da12b273d913b.jpg" alt="Imagen">
</p>
<p>Gestiona la frustración, permítete fallar, comprende que hay que seguir avanzando y aprendiendo cosas nuevas y que nada es inmediato.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://c.wallhere.com/photos/53/a2/metal_Full_Metal_Alchemist_Automail_Elric_Edward_Fullmetal_Alchemist_Brotherhood-245111.jpg!d" alt="Imagen">
</p>
<p>Para terminar te dejo un ejemplo de superación de adversidades. Como me gusta mucho la guitarra y el <em>rock</em>, Tony Iommi es un ejemplo de no darse por vencido y buscar soluciones para llegar a tu meta, y de paso convertirse en un pionero. Te invito a que conozcas su historia.</p>
<p style="text-align:center;">
  <iframe width="560" height="315" src="https://www.youtube.com/embed/aLREOFqlBrI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</p>
]]></content:encoded>
      <enclosure url="https://buenavibra.es/wp-content/uploads/2018/04/mito22.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Nuevas aventuras. Saliendo de la zona de confort</title>
      <link>https://joseluisgs.dev/posts/2021/2021-09-05-nuevas-aventuras.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-09-05-nuevas-aventuras.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Nuevas aventuras. Saliendo de la zona de confort</source>
      <description>Nunca digas adiós, solo hasta luego</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Sun, 05 Sep 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>A veces la vida te va empujando por caminos que nunca tenías pensados e incluso a tener que poner un paréntesis a uno de los mejores puestos de trabajo que uno puede tener. Pero es bueno salir de la zona de confort y empezar a explorar nuevas aventuras. Hay que crecer y avanzar, siempre agradeciendo lo vivido y aprendido.
Te comento mi nuevo camino.</p>
<!-- more -->
<h2>Zona de confort y los retos</h2>
<p>La zona de confort es la zona en la que estás cuando te mueves en un entorno en el que dominas y las cosas te resultan conocidas y cómodas. Tus hábitos, tus rutinas, tus conocimientos, tus aptitudes y tus comportamientos son tu zona de confort porque los conoces.
Después del cambio a Córdoba y de volver a Puertollano, aprendí que uno no debe aspirar solo al reconocimiento profesional y vivir en ese confort. Ese sentimiento se vio amplificado en los meses de aislamiento del COVID-19.</p>
<p>Profesionalmente he vivido de todo: desde dar conferencias y trabajar en investigación hasta dar clase en todo tipo de centros y niveles de estudios, no solo académicos, sino quedarme helado en pistas de tenis. Me he sentido y me siento afortunado por ello.</p>
<p>He tenido la suerte de conocer gente maravillosa en mi viaje. Pero el confort profesional a veces es perjudicial. Siempre he tenido inquietudes tanto profesionales como personales y, a veces, es necesario &quot;pegarle una patada a lo conocido&quot; y adentrarse en la aventura para sentirse vivo, incluso con los miedos o inseguridades que eso pueda provocar y, si nos dejan, poder soñar un poco más y afrontar nuevos retos. No es fácil.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://blogs.x.uoc.edu/recursos-humanos/wp-content/uploads/sites/4/2013/01/Mapa-de-TAAS-con-QR-scaled.jpg" alt="Imagen">
</p>
<h2>Hasta luego, CIFP Virgen de Gracia 👋</h2>
<p>Si hay un instituto donde puedes realizarte como docente es el <a href="https://cifpvirgendegracia.com/" target="_blank" rel="noopener noreferrer">CIFP Virgen de Gracia</a>. Su nivel académico, técnico y, sobre todo, humano es insuperable. A lo largo de estos años he ejercido como docente, tutor y jefe de departamento, y siempre he aprendido de todos/as mis compañeros/as. Son una familia. De hecho, en tiempos duros donde no he podido ver a mis seres queridos (COVID-19), ellos han llenado ese hueco. Desde el principio me abrieron sus brazos, me dejaron material y confiaron en mí otorgándome responsabilidades, ya sea con la jefatura o con proyectos de innovación.</p>
<p>Gran parte de la culpa la tiene su equipo directivo, siempre dispuesto a apoyar a los demás, mis compañeros de departamento (mi familia) y el resto de compañeros de centro y de rueda. Desde que te levantabas y compartías rueda hablando de todo hasta los follones en los que nos metíamos en el departamento inventando cosas, la sensación de sentirte realizado como persona y docente nunca ha sido mejor.</p>
<p>No me gusta dar nombres porque ellos lo saben, pero parte de lo que soy a día de hoy es gracias a ellos. He encontrado almas gemelas que entienden como yo la docencia del desarrollo de <em>software</em>; he recibido consejos y ayuda que me han hecho crecer y ser mejor a día de hoy. Mi gran objetivo siempre ha sido devolverles con mi esfuerzo y trabajo diario todo lo que me aportaban en mi día a día. Supongo que a veces me equivoqué y les pido disculpas por ello, porque nadie es perfecto. Pero sí quiero darles las gracias por todo lo que han hecho por mí y por el docente que me han hecho ser.</p>
<p>No les voy a decir adiós porque espero que nuestros caminos se entrecrucen muchas veces en nuestra vida. Yo siempre estaré disponible para ellos.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://s1.lanzadigital.com/wp-content/uploads/2020/06/El-CIFP-Virgen-de-Gracia-cuenta-con-una-extensa-oferta-formativa.jpg" alt="Imagen">
</p>
<h2>Hola, Luis Vives 🙌</h2>
<p>Mi nuevo centro es el IES Luis Vives de Leganés, en Madrid. Todo es nuevo y es una nueva aventura: desde dónde vivir hasta a dónde ir o cómo será el centro. Poco puedo decir...</p>
<p>Una cosa tengo clara: confío en mi trabajo y en lo aprendido a lo largo de mi vida. El reto es grande y da mucho respeto. Pero paso a paso vamos caminando.</p>
<p>De paso, como ya he dicho, espero además crecer personalmente junto a aquellos/as que me han apoyado y me han dado oportunidades a nivel personal, y poco a poco hacer mi propio camino y ver dónde este me lleva.</p>
<blockquote>
<p>«Cada uno de nosotros está en la tierra para descubrir su propio camino, y jamás seremos felices si seguimos el de otro».</p>
<p>James van Praagh</p>
<p><span class="iconify" data-icon="carbon:user-military" style="color: #1fedce;" data-width="48" data-height="48"></span></p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://www.irishtimes.com/polopoly_fs/1.2904309.1482173186!/image/image_gen/derivatives/box_620_330/image.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Testeando en Vue.js con Jest y Cypress</title>
      <link>https://joseluisgs.dev/posts/2021/2021-08-05-testeando-vue.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-08-05-testeando-vue.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Testeando en Vue.js con Jest y Cypress</source>
      <description>Aplicando Vue Test Utils en un ejemplo para obtener el tiempo</description>
      <category>Proyectos</category>
      <pubDate>Thu, 05 Aug 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Cuando desarrollamos cualquier aplicación es fundamental que aseguremos que todo funciona de la mejor manera posible. Para ello, debemos probar nuestra aplicación en un entorno de pruebas. En este proyecto mostramos cómo usar <em>Vue Test Utils</em> en una aplicación para consultar el tiempo usando Vue.js y <em>OpenWeatherMap API</em> y mostrar distintos mecanismos de testeo bajo TDD usando Jest y Cypress dentro de <em>Vue Test Utils</em>.</p>
<!-- more -->
<h2>Vue Weather Testing</h2>
<p>El objetivo de este proyecto es partir de una simple aplicación realizada en Vue.js que consulta el tiempo de una ciudad (buscador) usando <em>OpenWeatherMap API</em> y, en base a ella, mostrar distintas formas de realizar los test usando Jest y Cypress dentro de <em>Vue Test Utils</em>.</p>
<p>No es tanto el aspecto estético, sino el interno, mostrando cómo realizar test unitarios, de integración y E2E sobre una <em>app</em> Vue.js, donde se pueda ver cómo testear tanto un componente aislado y dentro de este sus propiedades y métodos, como la interrelación de distintos componentes que dan forma a esta aplicación, así como la <em>app</em> como un todo.</p>
<p>Antes de seguir, os puedo indicar que prepararé un ejemplo de aplicación de la metodología TDD donde abordaremos también el uso de Jest y Cypress para realizar pruebas de nuestros proyectos y cuyas referencias y repositorios indico a lo largo de esta entrada.</p>
<p>Este proyecto puede verse como una continuación del contenido mostrado en:</p>
<ul>
<li><a href="https://github.com/joseluisgs/testing-js-jest" target="_blank" rel="noopener noreferrer">Testing JS con JEST</a></li>
<li><a href="https://github.com/joseluisgs/testing-js-cypress" target="_blank" rel="noopener noreferrer">Testing JS con Cypress</a></li>
</ul>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/Vue.js- Ready-%2342b983" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/TypeScript-Ready-3178c6" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/JS Style-AirBnB-ff69b4" alt="Logo">
</p>
<h2>Testeando con Vue Test Utils</h2>
<h3>Jest</h3>
<p><a href="https://jestjs.io/docs/es-ES/getting-started" target="_blank" rel="noopener noreferrer">Jest</a> es una de las posibilidades que tenemos para testear nuestro código o proyecto en Vue.js. Se define como la <em>suite</em> de &quot;test con 0 configuración&quot;; es decir, mientras otras <em>suites</em> de test necesitan de un motor (<em>test runner</em>) para pasar los test y de la propia <em>suite</em> de test, así como de una librería de aserciones o <em>matchers</em>, Jest intenta que todo esto esté ya agrupado para agilizar el proceso de test desde el principio. Esto no quiere decir que no se pueda ampliar o profundizar y personalizar con otras librerías o que no tenga la potencia de otros, y está pensada para test unitarios y de integración.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/max/1058/1*xHwuLD0XRtfxhjV-qQjWrQ.png" alt="Jest">
</p>
<div class="hint-container tip">
<p class="hint-container-title">Antes de seguir</p>
<ul>
<li>Te recomiendo que visites este proyecto para que te suenen algunos conceptos: <a href="https://github.com/joseluisgs/testing-js-jest" target="_blank" rel="noopener noreferrer">Testing JS con JEST</a></li>
</ul>
</div>
<h4>ShallowMount vs Mount</h4>
<ul>
<li><code>shallowMount</code>: nos permite cargar un componente de manera individual para testearlo, creando un <em>wrapper</em> pero sin componentes hijos.</li>
<li><code>mount</code>: carga el componente y sus componentes hijos.</li>
</ul>
<p><code>shallowMount()</code> es mejor para probar un componente individual de forma aislada, ya que los componentes secundarios (hijos) se eliminan. Es ideal para las pruebas unitarias. Además, el uso de <code>shallowMount()</code> para probar un componente con muchos componentes secundarios (hijos) puede mejorar el tiempo de ejecución de la prueba unitaria, ya que no hay ningún costo (en términos de tiempo) para renderizar o usar los componentes secundarios (hijos).</p>
<p><code>mount()</code> es útil cuando desea incluir la prueba del comportamiento de los componentes secundarios (hijos) en el test.</p>
<p>El objeto <em>wrapper</em> nos permite probar todos los aspectos del HTML generado por el componente Vue y todas las propiedades (como los datos o métodos) del componente Vue.</p>
<h4>Aserciones y Matchers</h4>
<p>Los <a href="https://jestjs.io/docs/es-ES/using-matchers" target="_blank" rel="noopener noreferrer"><em>Matchers</em></a> nos permiten comparar de diferente manera valores esperados con los obtenidos. Podemos hacerlo de la siguiente manera, aunque hay <a href="https://jestjs.io/docs/es-ES/expect" target="_blank" rel="noopener noreferrer">más</a>:</p>
<h5>Igualdad</h5>
<ul>
<li><code>.toBe</code>: usado para comparar valores primitivos.</li>
<li><code>.toEqual</code>: usado para comparar recursivamente todas las propiedades de un objeto, también conocido como igualdad profunda.</li>
</ul>
<h5>Numéricos</h5>
<ul>
<li><code>.toBeLessThan</code>: el valor es menor que.</li>
<li><code>.toBeLessThanOrEqual</code>: el valor es menor o igual que.</li>
<li><code>.toBeGreaterThanOrEqual</code>: el valor es mayor o igual que.</li>
<li><code>.toBeGreaterThan</code>: el valor es mayor que.</li>
</ul>
<h5>Boolean, Nulos y Undefined</h5>
<ul>
<li><code>.toBeTruthy</code>: el valor es verdadero.</li>
<li><code>.toBeFalsy</code>: el valor es falso.</li>
<li><code>.toBeUndefined</code>: el valor es '<em>undefined</em>'.</li>
<li><code>.toBeNull</code>: el valor es '<em>null</em>'.</li>
</ul>
<h5>Arrays y contenido</h5>
<ul>
<li><code>.toContain</code>: contiene el elemento dentro del <em>array</em>.</li>
<li><code>.toHaveLength</code>: el <em>array</em> tiene la longitud.</li>
</ul>
<h5>Strings</h5>
<ul>
<li><code>.toMatch</code>: comprueba que un texto coincide con una expresión regular.</li>
<li><code>.toHaveLength</code>: comprueba la longitud.</li>
<li>Podemos usar otros anteriores.</li>
</ul>
<h4>Uso de Mocks</h4>
<p>Simulamos las llamadas a la API REST sin salir al exterior. De esta manera:</p>
<ul>
<li>Simulamos peticiones a la API REST satisfactorias.</li>
<li>Simulamos llamadas a la API REST que fallan.
Usando los <em>mocks</em>, podemos ver cómo reaccionan nuestros componentes sin necesidad de &quot;gastar&quot; tiempo en llamar constantemente al servicio externo.</li>
</ul>
<h4>Estructura de un test</h4>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">import</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> { </span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">shallowMount</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> } </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">from</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> '@vue/test-utils'</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">import</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> App</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> from</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> '@/App.vue'</span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Importa el componente a testear</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">import</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> axios</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> from</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> 'axios'</span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Importa la librería a mockear</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Mockeamos las librerías que vamos a usar</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">jest</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">mock</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">'axios'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Describimos la suite de test</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">describe</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">'Tests para el ... Componente'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, () </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">  let</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75"> wrapper</span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> null</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Antes de cada test</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">  beforeEach</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(() </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // Creamos los mocks </span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // renderizamos el componente</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">    wrapper</span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> shallowMount</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">App</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  })</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">  // Después de cada test</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">  afterEach</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(() </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    jest</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">resetModules</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">    jest</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">clearAllMocks</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">()  </span><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">// Si estás mockeando una librería</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  })</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">  test</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">'Caso de Test X'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, () </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // comprobamos el nombre del componente</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    expect</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">wrapper</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">vm</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">$options</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">).</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">toMatch</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">'...'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  })</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">  test</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">'Caso de Test Y'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, () </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">=></span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#ABB2BF">    ...</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">  })</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#0184BC;--shiki-dark:#ABB2BF">  ...</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">})</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h4>Code Coverage</h4>
<p>Si quieres tener un informe de la cobertura de tu código (%) añade estas líneas a tu fichero <code>jest.config.js</code>:</p>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">collectCoverage</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: </span><span style="--shiki-light:#986801;--shiki-dark:#D19A66">true</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">collectCoverageFrom</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">  "src/**/*.{js,vue}"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">  "!**/node_modules/**"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">],</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">coverageReporters</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">: [</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">  "html"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">,</span></span>
<span class="line"><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">  "text-summary"</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">]</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3>Cypress</h3>
<p><a href="https://www.cypress.io/" target="_blank" rel="noopener noreferrer">Cypress</a> es una de las muchas posibilidades que tenemos para testear nuestro código o proyecto en Vue.js para realizar pruebas E2E de manera automatizada con mucha potencia, y nos permite muchas posibilidades para analizar que interaccionamos correctamente con nuestro código resolviendo las historias de usuario a realizar.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/max/400/1*AtCVsPmCft1K516gsb9n4Q.png" alt="Jest">
</p>
<div class="hint-container tip">
<p class="hint-container-title">Antes de seguir</p>
<ul>
<li>Te recomiendo que visites este proyecto para que te suenen algunos conceptos: <a href="https://github.com/joseluisgs/testing-js-cypress" target="_blank" rel="noopener noreferrer">Testing JS con Cypress</a></li>
</ul>
</div>
<h4>Aserciones</h4>
<p>Puedes consultarlas <a href="https://docs.cypress.io/guides/references/assertions.html" target="_blank" rel="noopener noreferrer">aquí</a>. Pero se basan principalmente en <a href="https://docs.cypress.io/guides/references/assertions.html#Chai" target="_blank" rel="noopener noreferrer">Chai</a> y <a href="https://docs.cypress.io/guides/references/assertions.html#Sinon-Chai" target="_blank" rel="noopener noreferrer">Sinon</a>.</p>
<h4>Algunos métodos útiles de Cypress</h4>
<ul>
<li><code>visit</code>: redirige a Chrome a la URL que se le pasa por parámetro.</li>
<li><code>get</code>: obtiene un elemento por el identificador que le pasemos para realizar acciones sobre él. Como hemos explicado en el apartado anterior, todos los identificadores que pasemos serán obtenidos del CSS.</li>
<li><code>children</code>: nos permite obtener un elemento que pasamos por parámetro, que desciende del elemento que hemos obtenido con la función <code>get</code>.</li>
<li><code>click</code>: realiza un clic sobre el elemento que hayamos obtenido con la función <code>get</code>.</li>
<li><code>type</code>: escribe sobre el elemento obtenido un texto que pasamos por parámetro. Por ejemplo, usamos esta función para elementos <code>input</code> donde queremos introducir un texto.</li>
<li><code>submit</code>: permite enviar el contenido del formulario.</li>
<li><code>contains</code>: para indicar el contenido del elemento.</li>
<li><code>have.attr</code>: para indicar que el elemento tiene un atributo en concreto.</li>
<li><code>include</code>: para indicar que el atributo de un elemento incluye un texto.</li>
</ul>
<p>A todas las funciones se les puede pasar un JSON con el elemento <code>timeout</code>. Este elemento nos permite incluir un tiempo que nos ayudará a esperar a que el elemento termine de cargar en la página.</p>
<h4>Buenas prácticas</h4>
<p>Es importante que tengamos <a href="https://docs.cypress.io/guides/references/best-practices.html" target="_blank" rel="noopener noreferrer">buenas prácticas</a> para testear sin problemas. Entre ellas, el manejo de selectores óptimos para nuestros elementos de la web, como pueden ser selectores de web del tipo con selectores del tipo id como son: <code>data-testid</code> (mi preferido para usarlo también con JEST) o <code>data-cy</code>.</p>
<h2>Repositorio</h2>
<div class="hint-container tip">
<p class="hint-container-title">Proyecto disponible</p>
<ul>
<li><a href="https://github.com/joseluisgs/vue-weather-testing" target="_blank" rel="noopener noreferrer">GitHub</a>.</li>
<li><a href="https://joseluisgs.github.io/vue-weather-testing" target="_blank" rel="noopener noreferrer">Web</a></li>
</ul>
</div>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/gV4LZJZ.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>TDD en JavaScript I. Usando Jest</title>
      <link>https://joseluisgs.dev/posts/2021/2021-08-06-TDD-javascript-I.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-08-06-TDD-javascript-I.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">TDD en JavaScript I. Usando Jest</source>
      <description>Aprendiendo las bases del TDD en JS con Jest</description>
      <category>Blog</category>
      <category>Proyectos</category>
      <pubDate>Fri, 06 Aug 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Estos días quiero presentaros una serie de proyectos y miniapuntes que realicé con mayor o menor acierto para explicar el despliegue seguro de las aplicaciones web. En estos miniproyectos hablaré del TDD y BDD y de cómo realizar pruebas unitarias, de integración o E2E. Además, mostraré en esta serie de entradas cómo manejar librerías específicas para ello, como pueden ser Jest o Cypress. En esta primera parte nos centraremos en el uso de Jest, especialmente para test unitarios y de integración y cómo usarlo en proyectos Node.js o de Vue.js.</p>
<!-- more -->
<h2>Principios del TDD</h2>
<p>Automatizar los test unitarios y poder trabajar con ellos nos ofrece iniciar las bases del <a href="https://uniwebsidad.com/libros/tdd" target="_blank" rel="noopener noreferrer">TDD (<em>Test-Driven Development</em>)</a>. El desarrollo guiado por pruebas de <em>software</em>, o <em>Test-driven development</em> (TDD), es una práctica de ingeniería de <em>software</em> que involucra otras dos prácticas: escribir las pruebas primero (<em>Test First Development</em>) y refactorización (<em>Refactoring</em>). Para ello:</p>
<ul>
<li>Crea el test.</li>
<li>Comprueba que falla.</li>
<li>Escribe el código justo para pasarlo.</li>
<li>Comprueba que funciona.</li>
<li>Refactoriza.</li>
<li>Vuelve al primer paso.</li>
<li>Y disfrutar 🙂.</li>
</ul>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.pinimg.com/originals/b9/be/51/b9be514dd54f4a058943099020284deb.png" alt="Imagen">
</p>
<p>El propósito del desarrollo guiado por pruebas es lograr un código limpio que funcione. La idea es que los requisitos sean traducidos a pruebas; de este modo, cuando las pruebas pasen, se garantizará que el <em>software</em> cumple con los requisitos que se han establecido. Para ello debemos trabajar en requisitos o en partes de las historias de usuario.</p>
<h2>ATDD (<em>Acceptance Test Driven Development</em>)</h2>
<p>Los tests de aceptación o de cliente son el criterio escrito de que el <em>software</em> cumple los requisitos de negocio que el cliente demanda. Los requisitos se traducen por ejemplos ejecutables (de cómo se ejecuta una funcionalidad con sus entradas y salidas esperadas) surgidos del consenso entre los distintos miembros del equipo, incluido, por supuesto, el cliente. Una vez que tenemos los ATDD, se crea el test que lo representa y, posteriormente, iniciamos TDD; de esta manera, el código que pasa el test se asegura que cumple con el requisito a conseguir.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/max/1022/1*t0vN-a82ilRIv7M9fgJvIw.png" alt="Imagen">
</p>
<h2>Jest</h2>
<p><a href="https://jestjs.io/docs/es-ES/getting-started" target="_blank" rel="noopener noreferrer">Jest</a> es una de las muchas posibilidades que tenemos para testear nuestro código o proyecto en JavaScript (ya sea en cliente o en Node.js). Jest está basado en <a href="https://jasmine.github.io/" target="_blank" rel="noopener noreferrer">Jasmine</a>, y se define como la <em>suite</em> de &quot;test con 0 configuración&quot;; es decir, mientras otras <em>suites</em> de test necesitan de un motor (<em>test runner</em>) para pasar los test y de la propia <em>suite</em> de test, así como de una librería de aserciones o <em>matchers</em>, Jest intenta que todo esto esté ya agrupado para agilizar el proceso de test desde el principio. Esto no quiere decir que no se pueda ampliar o profundizar y personalizar con otras librerías o que no tenga la potencia de otros.</p>
<p>En cualquier caso, las bases de estos ejemplos te servirán para las distintas alternativas existentes.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://landing-page-book.front10.com/images/frameworks/jest.png" alt="Imagen">
</p>
<h3>Otras alternativas</h3>
<p>Existen muchas alternativas y cada una enfocada a un aspecto. En este tutorial me centro en Jest, pero como he dicho hay varias, ya sea para test unitarios, TDD, BDD o E2E. Te recomiendo <a href="https://medium.com/welldone-software/an-overview-of-javascript-testing-7ce7298b9870" target="_blank" rel="noopener noreferrer">este artículo</a> para tener una visión al respecto. Remarco los siguientes para test unitarios:</p>
<ul>
<li><a href="https://jasmine.github.io/" target="_blank" rel="noopener noreferrer">Jasmine</a>. Es una de las librerías por excelencia para hacer test, &quot;padre&quot; de Jest y, además, la <em>suite</em> básica en <a href="https://angular.io/guide/testing" target="_blank" rel="noopener noreferrer">Angular</a>.</li>
<li><a href="https://karma-runner.github.io/latest/index.html" target="_blank" rel="noopener noreferrer">Karma</a>. Es un motor de test, desarrollado por el equipo de Angular, que suele usarse junto a Jasmine para este tipo de proyectos.</li>
<li><a href="https://mochajs.org/" target="_blank" rel="noopener noreferrer">Mocha</a>. Es una librería de test pensada sobre todo para aplicaciones Node.js, muy potente y configurable al gusto.</li>
<li><a href="https://www.chaijs.com/" target="_blank" rel="noopener noreferrer">Chai</a>. Es una librería de aserciones generalmente usada con Mocha para dar potencia a nuestros <em>matchers</em> en nuestros tests.</li>
</ul>
<div class="hint-container tip">
<p class="hint-container-title"><i class="iconfont reco-github"></i> Proyectos y repositorios</p>
<p>Esta entrada toma como punto de partida el siguiente proyecto:</p>
<ul>
<li><a href="https://github.com/joseluisgs/testing-js-jest" target="_blank" rel="noopener noreferrer">Testing JS con Jest</a></li>
</ul>
<p>Además, ideas similares a las mostradas puedes encontrarlas aplicadas en los siguientes proyectos:</p>
<ul>
<li><a href="https://github.com/joseluisgs/vue-weather-testing" target="_blank" rel="noopener noreferrer">Vue Weather Testing</a></li>
<li><a href="https://github.com/joseluisgs/ts-api-rest" target="_blank" rel="noopener noreferrer">TS Api Rest</a></li>
</ul>
</div>
<h3>Instalación</h3>
<p>Es importante seguir la <a href="https://jestjs.io/docs/es-ES/getting-started" target="_blank" rel="noopener noreferrer">documentación oficial</a>.</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">npm</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> install</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --save-dev</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> jest</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h3>Otras configuraciones</h3>
<p>Otras configuraciones realizadas las tienes en <code>package.json</code>.</p>
<h3>Ejecutando el test</h3>
<p>Debes tener un directorio llamado <code>tests</code>, y en él ficheros <code>.spec.test</code>.</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">npm</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> test</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h3>Matchers</h3>
<p>Los <a href="https://jestjs.io/docs/es-ES/using-matchers" target="_blank" rel="noopener noreferrer"><em>Matchers</em></a> nos permiten comparar de diferente manera valores esperados con los obtenidos. Podemos hacerlo de la siguiente manera, aunque hay <a href="https://jestjs.io/docs/es-ES/expect" target="_blank" rel="noopener noreferrer">más</a>:</p>
<h4>Igualdad</h4>
<ul>
<li><code>.toBe</code>: usado para comparar valores primitivos.</li>
<li><code>.toEqual</code>: usado para comparar recursivamente todas las propiedades de un objeto, también conocido como igualdad profunda.</li>
</ul>
<h4>Numéricos</h4>
<ul>
<li><code>.toBeLessThan</code>: el valor es menor que.</li>
<li><code>.toBeLessThanOrEqual</code>: el valor es menor o igual que.</li>
<li><code>.toBeGreaterThanOrEqual</code>: el valor es mayor o igual que.</li>
<li><code>.toBeGreaterThan</code>: el valor es mayor que.</li>
</ul>
<h4>Boolean, Nulos y Undefined</h4>
<ul>
<li><code>.toBeTruthy</code>: el valor es verdadero.</li>
<li><code>.toBeFalsy</code>: el valor es falso.</li>
<li><code>.toBeUndefined</code>: el valor es '<em>undefined</em>'.</li>
<li><code>.toBeNull</code>: el valor es '<em>null</em>'.</li>
</ul>
<h4>Arrays y contenido</h4>
<ul>
<li><code>.toContain</code>: contiene el elemento dentro del <em>array</em>.</li>
<li><code>.toHaveLength</code>: el <em>array</em> tiene la longitud.</li>
</ul>
<h4>Strings</h4>
<ul>
<li><code>.toMatch</code>: comprueba que un texto coincide con una expresión regular.</li>
<li><code>.toHaveLength</code>: comprueba la longitud.</li>
<li>Podemos usar otros anteriores.</li>
</ul>
<h3>Cobertura</h3>
<p>Podemos saber cuánto hemos testeado nuestro código realizando un análisis de cobertura. Jest nos ofrece el <em>flag</em> <code>--coverage</code> en la línea de comandos para comprobar la cobertura de nuestros test.</p>
<h4>Informe de cobertura consola</h4>
<p><code>npm run test:coverage</code></p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">jest</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --coverage</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> PASS</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">  tests/funciones.spec.js</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div>]]></content:encoded>
      <enclosure url="https://cdn-media-1.freecodecamp.org/images/1*0jCUKud4CkbbmNrFDzIQZw.png" type="image/png"/>
    </item>
    <item>
      <title>TDD en JavaScript II. Usando Cypress</title>
      <link>https://joseluisgs.dev/posts/2021/2021-08-09-TDD-javascript-II.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-08-09-TDD-javascript-II.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">TDD en JavaScript II. Usando Cypress</source>
      <description>Aprendiendo las bases del TDD en JS con Cypress</description>
      <category>Blog</category>
      <category>Proyectos</category>
      <pubDate>Mon, 09 Aug 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Como ya comenzamos en <a href="/posts/2021/2021-08-06-TDD-javascript-I.html" target="_blank">anteriores publicaciones</a>, vamos a seguir con una serie de apuntes y miniproyectos para iniciarse en TDD y BDD para realizar pruebas unitarias, de integración o E2E. En esta segunda parte nos centraremos en el uso de Cypress para usarla en los test E2E, usándola en todo tipo de aplicaciones web y remarcando, por supuesto, su uso en Vue.js.</p>
<!-- more -->
<h2>Introducción al Testing E2E</h2>
<p>Los tests E2E (<em>End to End</em>) simulan el comportamiento de un usuario real. Prueban toda la aplicación de principio a fin, cubriendo así secciones que las pruebas unitarias y las pruebas de integración no cubren.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://blog.irontec.com/wp-content/uploads/2019/01/test-pyramid-768x367.jpg" alt="Imagen">
</p>
<p>Aunque la pirámide de Cohn aconseja realizar más tests unitarios y de integración que tests E2E, ya que estos últimos pueden llegar a ser más frágiles y lentos, sabemos que son pruebas con mayor integración entre todas las partes de la aplicación y ejecutarán una gran parte del código de la aplicación.</p>
<p>El objetivo de estas pruebas es el mismo que cualquier otro tipo de prueba: la detección de errores. Pero la perspectiva E2E nos permite dar un paso más y, aparte de errores con una visibilidad más o menos inmediata, podremos determinar la existencia de indefiniciones funcionales o errores ocultos en base a las historias de usuarios.</p>
<h3>Objetivos</h3>
<p>En las pruebas E2E detectamos principalmente:</p>
<ul>
<li>Errores en la definición de la comunicación de dos sistemas: un sistema envía unos parámetros, pero el sistema con el que se comunica espera recibir otros y se genera un error.</li>
<li>Ausencia de un sistema: no tenemos la versión actualizada de un sistema que forma parte del flujo de negocio a probar, o no se ha tenido en cuenta que ese sistema debe participar o debe modificarse.</li>
<li>Error en la definición del funcionamiento del flujo funcional: todos los componentes y sistemas funcionan correctamente, hemos ejecutado la prueba de principio a fin sin errores, pero el resultado final es incoherente con el esperado. Esta es la gran potencia de las pruebas E2E: la detección de errores de definición, y la solución suele pasar por una redefinición del proceso.</li>
<li>Que las historias de usuario se cumplen.</li>
</ul>
<p>Debido a la importancia del usuario, es importante que estas pruebas se realicen teniendo en cuenta las historias de usuario para comprobar que los resultados tras la interacción cumplen los criterios de aceptación.</p>
<h2>E2E en BDD/TDD</h2>
<p>En definitiva, definimos funcionalidades y escenarios de uso que desarrollamos en distintos componentes y funciones que testeamos (test unitarios), integramos (integración) y, finalmente, comprobamos si todo el flujo sigue el escenario indicado desde el punto de vista del usuario.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://blog.softtek.com/hs-fs/hubfs/blogs/innovationlabs/bdd.png?width=832&name=bdd.png" alt="Imagen">
</p>
<h3>Historias de usuario</h3>
<p>Una historia de usuario es una explicación general e informal de una función de <em>software</em> escrita desde la perspectiva del usuario final. Su propósito es articular cómo proporcionará una función de <em>software</em> valor al cliente.</p>
<p>Las historias de usuario nos muestran los requisitos a conseguir, la interacción a realizar, los resultados esperados y cómo se realizará la aceptación de las mismas; es por ello que son nuestro principal &quot;contrato&quot; para nuestros test unitarios y sobre todo nuestros test E2E.</p>
<h4>Beneficios</h4>
<p>Las historias de usuario tienen varios beneficios clave:</p>
<ul>
<li>Las historias centran la atención en el usuario. Una lista de tareas pendientes mantiene al equipo centrado en tareas que deben completarse, pero un conjunto de historias lo mantiene centrado en solucionar problemas para usuarios reales.</li>
<li>Las historias permiten la colaboración. Con el objetivo definido, el equipo puede colaborar para decidir cómo ofrecer un mejor servicio al usuario y cumplir con dicho objetivo.</li>
<li>Las historias impulsan soluciones creativas. Las historias fomentan que el equipo piense de forma crítica y creativa sobre cómo lograr mejor un objetivo.</li>
<li>Las historias motivan. Con cada historia, el equipo de desarrollo disfruta de un pequeño reto y una pequeña victoria, lo que aumenta la motivación.</li>
<li>Son la base de nuestros test y nos aseguran que una vez cumplidas se cumplen los requisitos del <em>software</em> y sus criterios de aceptación.</li>
</ul>
<h4>¿Cómo usarlas?</h4>
<ul>
<li>Describe tareas o subtareas: decide qué pasos específicos deben completarse y quién es responsable de cada uno de ellos.</li>
<li>Perfiles de usuario: ¿para quién? Si hay varios usuarios finales, considera crear varias historias.</li>
<li>Pasos ordenados: escribe una historia para cada paso en un proceso más grande.</li>
<li>Escucha el <em>feedback</em>: habla con los usuarios y capta sus problemas y necesidades en lo que dicen. No es necesario tener que estar adivinando las historias cuando puedes obtenerlas de tus clientes.</li>
<li>Tiempo: el tiempo es un tema delicado. Muchos equipos de desarrollo evitan hablar sobre el tiempo y, en su lugar, confían en sus marcos de trabajo de estimación. Dado que las historias deberían completarse en un <em>sprint</em>, aquellas que puedan necesitar semanas o meses deberían dividirse en historias más pequeñas o considerarse un <em>epic</em> independiente.</li>
</ul>
<h4>Plantilla y ejemplos de historias de usuario</h4>
<p>Las historias de usuario suelen expresarse con una frase simple con la siguiente estructura:</p>
<p>&quot;Como [perfil], [quiero] [para]&quot;.</p>
<p>Desglosemos esta estructura:</p>
<ul>
<li>&quot;Como [perfil]&quot;: ¿para quién desarrollamos esto? No solo buscamos un puesto, buscamos el perfil de la persona. Max. Nuestro equipo debería comprender quién es Max. Con suerte, hemos entrevistado a muchos Max. Comprendemos cómo trabaja esa persona, cómo piensa y cómo se siente. Sentimos empatía por Max.</li>
<li>&quot;Quiere&quot;: aquí describimos su intención, no las funciones que usan. ¿Qué es lo que están intentando lograr realmente? Esta descripción debería realizarse con independencia de las implementaciones; si describes algún elemento de la IU y no el objetivo del usuario, estás cometiendo un error.</li>
<li>&quot;Para&quot;: ¿cómo encaja su deseo inmediato de hacer algo en la perspectiva general? ¿Cuál es el beneficio general que intentan lograr? ¿Cuál es el gran problema que debe resolverse?</li>
</ul>
<p>Por ejemplo, las historias de usuario pueden tener este aspecto:</p>
<ul>
<li>Como Pepe, quiero invitar a mis amigos para que podamos disfrutar de este servicio juntos.</li>
<li>Como Ana, quiero organizar mi trabajo para poder sentir que tengo un mayor control.</li>
<li>Como gestor, quiero poder comprender el progreso de mis compañeros para poder informar sobre nuestros éxitos y fallos.</li>
</ul>
<h2>CYPRESS</h2>
<p><a href="https://docs.cypress.io/guides/overview/why-cypress.html#In-a-nutshell" target="_blank" rel="noopener noreferrer">Cypress</a> es una de las muchas posibilidades que tenemos para testear nuestro código o proyecto en JavaScript/HTML para realizar pruebas E2E de manera automatizada con mucha potencia, y nos permite muchas posibilidades para analizar que interaccionamos con nuestro código resolviendo las <a href="https://www.atlassian.com/es/agile/project-management/user-stories" target="_blank" rel="noopener noreferrer">historias de usuario</a> a realizar. Debo remarcar que Cypress nos permite hacer pruebas de integración y de componentes en sus últimas versiones.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://buddy.works/blog/thumbnails/cypress-cover.png" alt="Imagen">
</p>
<p>En cualquier caso, las bases de estos ejemplos te servirán para las distintas alternativas existentes.</p>
<p>Recuerda que Cypress.io es una herramienta de testeo de <em>front-end</em> de código abierto construida para la web moderna. Este <em>framework</em> &quot;todo en uno&quot; incluye librerías de aserciones, <em>mocks</em> y pruebas <em>end-to-end</em> automáticas sin utilizar Selenium. Como dice en su web: &quot;Cypress prueba todo lo que se ejecuta en un navegador web&quot;. Esto no implica que te saltes los test unitarios 😉.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://i.ytimg.com/vi/woI490HRM34/maxresdefault.jpg" alt="Imagen">
</p>
<h3>Otras alternativas</h3>
<p>Existen muchas alternativas. En este tutorial me centro en Cypress, pero como he dicho hay varias, ya sea para test unitarios, TDD, BDD o E2E. Te recomiendo <a href="https://medium.com/welldone-software/an-overview-of-javascript-testing-7ce7298b9870" target="_blank" rel="noopener noreferrer">este artículo</a> para tener una visión al respecto.</p>
<div class="hint-container tip">
<p class="hint-container-title"><i class="iconfont reco-github"></i> Proyectos y repositorios</p>
<p>Esta entrada toma como punto de partida el siguiente proyecto:</p>
<ul>
<li><a href="https://github.com/joseluisgs/testing-js-cypress" target="_blank" rel="noopener noreferrer">Testing JS con Cypress</a></li>
<li><a href="https://github.com/joseluisgs/vue-weather-testing" target="_blank" rel="noopener noreferrer">Vue Weather Testing</a></li>
</ul>
</div>
<h3>Instalación</h3>
<p>Es importante seguir la <a href="https://jestjs.io/docs/es-ES/getting-started" target="_blank" rel="noopener noreferrer">documentación oficial</a>.</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">npm</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> install</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> cypress</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --save-dev</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h3>Otras configuraciones</h3>
<p>Otras configuraciones realizadas las tienes en <code>package.json</code>.</p>
<h3>Ejecutando Cypress</h3>
<p>Debes tener un directorio llamado <code>tests</code> y, en ellos, ficheros <code>.spec.test</code>.</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">./node_modules/.bin/cypress</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> open</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">npm</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> cy:open</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h3>La carpeta Cypress</h3>
<ul>
<li><em>Fixtures</em>: datos estáticos que pueden ser utilizados para los tests.</li>
<li><em>Integration</em>: lugar donde colocaremos los tests. Por defecto contiene una carpeta de tests de ejemplo.</li>
<li><em>Plugins</em>: permiten acceder, modificar o ampliar el comportamiento interno de Cypress.</li>
<li><em>Support</em>: lugar para colocar comportamientos reutilizables, como comandos personalizados o anulaciones globales, que estarán disponibles para todos los tests.</li>
</ul>
<h3>Aserciones</h3>
<p>Puedes consultarlas <a href="https://docs.cypress.io/guides/references/assertions.html" target="_blank" rel="noopener noreferrer">aquí</a>. Pero se basan principalmente en <a href="https://docs.cypress.io/guides/references/assertions.html#Chai" target="_blank" rel="noopener noreferrer">Chai</a> y <a href="https://docs.cypress.io/guides/references/assertions.html#Sinon-Chai" target="_blank" rel="noopener noreferrer">Sinon</a>.</p>
<h3>Algunos métodos útiles de Cypress</h3>
<ul>
<li><code>visit</code>: redirige a Chrome a la URL que se le pasa por parámetro.</li>
<li><code>get</code>: obtiene un elemento por el identificador que le pasemos para realizar acciones sobre él. Como hemos explicado en el apartado anterior, todos los identificadores que pasemos serán obtenidos del CSS.</li>
<li><code>children</code>: nos permite obtener un elemento que pasamos por parámetro, que desciende del elemento que hemos obtenido con la función <code>get</code>.</li>
<li><code>click</code>: realiza un clic sobre el elemento que hayamos obtenido con la función <code>get</code>.</li>
<li><code>type</code>: escribe sobre el elemento obtenido un texto que pasamos por parámetro. Por ejemplo, usamos esta función para elementos <code>input</code> donde queremos introducir un texto.</li>
<li><code>submit</code>: permite enviar el contenido del formulario.</li>
<li><code>contains</code>: para indicar el contenido del elemento.</li>
<li><code>have.attr</code>: para indicar que el elemento tiene un atributo en concreto.</li>
<li><code>include</code>: para indicar que el atributo de un elemento incluye un texto.</li>
</ul>
<p>A todas las funciones se les puede pasar un JSON con el elemento <code>timeout</code>. Este elemento nos permite incluir un tiempo que nos ayudará a esperar a que el elemento termine de cargar en la página.</p>
<h3>Ejecutando los tests</h3>
<p>El comando <code>run</code> ejecutará todos los tests que tengamos dentro de la carpeta <em>Integration</em>.</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">./node_modules/.bin/cypress</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> run</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">npm</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> cy:run</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h4>Si el test falla</h4>
<p>Si nuestro test fallase, podríamos ejecutarlo en modo <em>headed</em> para poder depurarlo más fácilmente.</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">./node_modules/.bin/cypress</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --headed</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">npm</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> cy:run</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> --headed</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><h3>Buenas prácticas</h3>
<p>Es importante que tengamos <a href="https://docs.cypress.io/guides/references/best-practices.html" target="_blank" rel="noopener noreferrer">buenas prácticas</a> para testear sin problemas. Entre ellas, el manejo de selectores óptimos para nuestros elementos de la web, como pueden ser selectores de web del tipo con selectores del tipo id como son: <code>data-testid</code> (mi preferido para usarlo también con JEST) o <code>data-cy</code>.</p>
<h3>Ejemplos</h3>
<p>En la carpeta <code>Integrations/examples</code> tienes muchos ejemplos para aprender todo de cómo usarlo. <a href="https://github.com/bhaidar/testing-workshop-cypress" target="_blank" rel="noopener noreferrer">Aquí también</a> puedes encontrar más información de cómo usarlo.</p>
<h2>Testeando un <em>framework</em>: Vue.js</h2>
<p>Nos centraremos en ejecutar el comando <code>test:e2e</code> para ejecutar nuestros test E2E con Cypress. Aunque, como he indicado, en las nuevas versiones de Cypress podemos testear aisladamente cada componente, así como conjuntamente.</p>
<p>Para ello crearemos el proyecto con <a href="https://cli.vuejs.org/" target="_blank" rel="noopener noreferrer">Vue CLI</a>, indicando en la configuración manual el uso de test E2E. Puedes verlo en el proyecto <code>todoapp</code> del repositorio indicado, una aplicación clonada del listado de ejemplos de Vue.js y que solo nos interesa para testear. Podríamos haber cogido cualquiera nuestra 😃. Un ejemplo completo más profundo lo tienes <a href="https://www.cypress.io/blog/2017/11/28/testing-vue-web-application-with-vuex-data-store-and-rest-backend/" target="_blank" rel="noopener noreferrer">aquí</a>.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://external-preview.redd.it/3FhNraSXNHOViE2YLUCYjjD_GlyAaMw0gcA2VI40R4s.jpg?auto=webp&s=c547decaa3066c5bc1845332427b850270ef84e7" alt="Imagen">
</p>
<h3>Configurando Cypress con Vue CLI</h3>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? Please pick a preset: Manually </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">select</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75"> features</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? Check the features needed </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">for</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> your project: Choose Vue version, Babel, Linter, Unit, E2E</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? Choose a version of Vue.js that you want to start the project with 2.x</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? Pick a linter / formatter config: Airbnb</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? Pick additional lint features: Lint on save</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? Pick a unit testing solution: Jest</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">? Pick an E2E testing solution: (</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">Use</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> arrow</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> keys</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> ❯</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> Cypress</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (Chrome </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">only</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> Nightwatch</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (WebDriver-based) </span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> WebdriverIO</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (DevTools/DevTools </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">based</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Ejemplo para ejecutar los test:</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">npm</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> run</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> test:e2e</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>Iremos aplicando ATDD/TDD en test E2E. Puedes seguir el proyecto <code>todoapp</code> <em>commit</em> a <em>commit</em>, así como sucesivos tests que vayan surgiendo.</p>
<div class="hint-container tip">
<p class="hint-container-title"><i class="iconfont reco-github"></i> Proyectos y repositorios que pueden ayudarte a iniciarse con Cypress</p>
<ul>
<li><a href="https://github.com/joseluisgs/testing-js-cypress" target="_blank" rel="noopener noreferrer">Testing JS con Cypress</a></li>
<li><a href="https://github.com/joseluisgs/vue-weather-testing" target="_blank" rel="noopener noreferrer">Vue Weather Testing</a></li>
</ul>
</div>
]]></content:encoded>
      <enclosure url="https://blog.magicpod.com/hubfs/cypress-end-to-end-test.png" type="image/png"/>
    </item>
    <item>
      <title>Me gusta JavaScript</title>
      <link>https://joseluisgs.dev/posts/2021/2021-07-22-me-gusta-javascript.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-07-22-me-gusta-javascript.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Me gusta JavaScript</source>
      <description>Un lenguaje y stack donde cada día me siento más cómodo</description>
      <category>Blog</category>
      <category>Proyectos</category>
      <pubDate>Thu, 22 Jul 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>No os voy a engañar. Yo era el tipo de persona que no soportaba JavaScript en sus comienzos y solo le veía utilidad para mejorar determinados aspectos de la web. Era un apasionado de las <em>apps</em> móviles y del <em>back-end</em>. Pero desde el cambio de filosofía del 2015 unido a Node.js y a <em>frameworks</em> actuales, JavaScript (ES6 y versiones posteriores) y su mundo cada día me lo ponen más fácil. Ahora te cuento mis motivos.</p>
<!-- more -->
<h2>JavaScript</h2>
<p>El mundo de JavaScript, mejor dicho, ECMAScript, es apasionante y cada día cambia y crece más, a veces incluso es mareante y crea algo de vértigo al ver la cantidad de tecnologías que están disponibles. Todo tiene sus cosas buenas y malas. Reconozco que algo cambió en mí desde la especificación de 2015 (ES6) de JavaScript para que actualmente sea uno de mis lenguajes de referencia unido a las diversas tecnologías que han crecido a su alrededor.</p>
<p>Yo, que me gustaba (y me gusta) el desarrollo móvil y el <em>back-end</em>, estaba algo hastiado por la pesadez de Java (en peso y rendimiento) para todas las cosas. No voy a negar que me encanta Spring Boot, pero le veía falta de agilidad; a la hora del escritorio, Swing no da para más. A nivel de móviles, menos mal que Kotlin ha venido a agilizar las cosas. También me aburría PHP en el <em>back</em>, pero no Laravel, el cual sigo encontrando elegante y una opción que siempre recomendaré.</p>
<p>Quizás sea el propio cambio, o la búsqueda de nuevos caminos y no dejar de aprender, lo que poco a poco hizo que el universo JavaScript me fuera atrapando. Esto me fue atrapando, no tanto por su potencia como lenguaje aislado para la web y el manejo del DOM, sino por la cantidad de tecnologías que han crecido a su alrededor que me han hecho sentirme tan cómodo, tanto como profesor como desarrollador, al poder centrar todos mis desarrollos con esta tecnología, lo que permitía no ir dando saltos de un lenguaje a otro. Quizás tecnológicamente no sea la mejor opción, pero me permite combinar distintos paradigmas: objetos, funcional, etc., para agilizar o aumentar la velocidad de desarrollo y con ello mostrar el mismo código desde distintas vertientes.</p>
<div class="hint-container warning">
<p class="hint-container-title">¡Advertencia!</p>
<p>Si no lo tienes claro aún, Java != JavaScript (ECMAScript).</p>
</div>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://wi.wallpapertip.com/wsimgs/83-838172_programming-javascript.jpg" alt="Logo"></p>
<h2>Node.js</h2>
<p>Mi primera alegría fue poder ir del <em>front</em> al <em>back</em> con JavaScript. Node.js puede que no sea la mejor opción para todos los desarrollos <em>back</em>, pero sí que me ofrece a mí la simplicidad para poder realizar el <em>back</em> que necesito eficientemente. Sobre todo a la hora de hacer servicios o microservicios que trabajan como <a href="https://joseluisgs.github.io/proyectos/2021-06-16-api-rest-node.html" target="_blank" rel="noopener noreferrer">API REST</a>.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://niixer.com/wp-content/uploads/2020/11/Node.JS-Use-Cases-Cover-Image.png" alt="Node"></p>
<h2>Multiplataforma</h2>
<p>La otra gran ventaja de usar el <em>stack</em> JS es poder usarlo en distintos desarrollos y distintas plataformas e independencia del sistema operativo. Desde aplicaciones <em>back</em> o de escritorio independientes del SO sin lidiar con Java-Swing o .NET gracias a Electron, web (obviamente), hasta aplicaciones móviles (NativeScript, Ionic) o incluso construir <em>skills</em> de <a href="https://joseluisgs.github.io/proyectos/2021-05-24-skill-informatica.html" target="_blank" rel="noopener noreferrer">Alexa</a>. El mero hecho de no cambiar de lenguaje ya de por sí es una opción muy interesante que aporta comodidad y uniformidad a lo que hago. Repito: quizás no es la mejor opción, pero yo me siento bien, y muchas cosas las hago solo para mí 😀.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://ais-10072.kxcdn.com/wp-content/uploads/2020/09/cross-platform-app-development-Company.png" alt="Node"></p>
<h2>Web</h2>
<p>JavaScript nació para &quot;darle poderes&quot; al desarrollo web y es su principal nicho. Gracias a ES2015 ganó mucho como lenguaje y sobre todo gracias a los <em>frameworks</em> existentes uno tiene opciones para aburrirse para poder desarrollar. En mi caso siento debilidad por Vue.js y su entorno 💘; dedicaré un día un especial completo a ello. Pero digamos que tengo toda la potencia para cubrir lo que necesito en cualquier tipo de aplicaciones web, ya sea con Vanilla JS, como con Vue.js, como usando otras alternativas derivadas como Nuxt.js, VuePress o usando otras librerías o <em>frameworks</em> como Ionic, NativeScript, Framework7, etc.</p>
<p style="text-align:center;"></p>]]></content:encoded>
      <enclosure url="https://i.pinimg.com/originals/71/ee/32/71ee32577432648f9e45fbd63b2cf261.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Desarrolla tu API REST en Node.js</title>
      <link>https://joseluisgs.dev/posts/2021/2021-06-16-api-rest-node.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-06-16-api-rest-node.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Desarrolla tu API REST en Node.js</source>
      <description>Distintos ejemplos de cómo crear tu propia API REST</description>
      <category>Proyectos</category>
      <pubDate>Wed, 16 Jun 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Una de las cosas con las que más cómodo me siento es con la facilidad con la que, usando JavaScript y TypeScript, puedo desarrollar cualquier tipo de producto, desde el <em>back</em> hasta el <em>front</em>, pasando por dispositivos móviles o aplicaciones de Alexa. En esta entrada te comento, a nivel didáctico, distintos proyectos usados y librerías para aprender a montarte tu propio <em>back-end</em> para que consuman tu API REST.</p>
<!-- more -->
<h2>Tu API REST</h2>
<p>Cuando creamos una API REST tenemos muchas tecnologías y opciones para poder realizarla con éxito. Hay tantas que dependerá de la naturaleza del problema cuál debas elegir. Ya sea con TypeScript o JavaScript sobre Node.js, debes pensar cómo y de qué manera estructurar tu código. Ya uses MongoDB, Amazon, MariaDB o Firebase, hacerte tu propio <em>back</em> te va a dar el control de cómo y de qué manera acceden a tus recursos desde cualquier dispositivo.</p>
<p>En esta entrada te mostraré distintas tecnologías y proyectos usados como ejemplos con el alumnado o a nivel personal donde se integran distintas opciones. Algunas, ya verás, se irán repitiendo en ellos como un patrón base a seguir.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://blog.boardinfinity.com/content/images/2019/07/secure-rest-api-in-nodejs-18f43b3033c239da5d2525cfd9fdc98f.png" alt="Logo"></p>
<h2>Principales ideas</h2>
<h3>Adapta a tu problema</h3>
<p>Divídela por capas; usa, por ejemplo, el patrón MVC, que en Node.js ofrece una arquitectura de fácil implementación y extensión de tu problema. Rutas 100% RESTful, usa los verbos HTTP para realizar todas tus operaciones. Patrón de operaciones CRUD sobre recursos: podrás añadir, listar, modificar o eliminar. Sube tus ficheros a tu servidor. Todo almacenado usando MongoDB.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://topdev.vn/blog/wp-content/uploads/2019/04/restful-rest-diagram-api.jpg" alt="Logo"></p>
<h3>Seguridad ante todo</h3>
<p>JSON Web Token (abreviado JWT) es un estándar abierto basado en JSON propuesto por IETF (RFC 7519) para la creación de tokens de acceso que permiten la propagación de identidad y privilegios. Además, hemos implementado la opción de refresco de tokens para añadir más funcionalidad.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://rosolutions.com.mx/blog/wp-content/uploads/2018/11/1_g15QJL_ONdBGAdjgnld3pg.png" alt="Logo"></p>
<h3>CI/CD</h3>
<p>Compila, prueba, despliega. Solo haz clic. Mejora tu productividad aplicando integración y despliegue continuo (CI/CD). Usaremos GitHub Actions para crear un flujo de trabajo y compilar, realizar el análisis de código estático y ejecutar pruebas automáticas para la detección temprana de errores y, finalmente, el despliegue de todo el proyecto en Heroku y DockerHub.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://www.siliconweek.com/wp-content/uploads/2019/03/1EBXc9eJ1YRFLtkNI_djaAw.png" alt="Logo"></p>
<h3>La nube es tu amiga</h3>
<p>No importa dónde estés. Yo estaré allí. No te limites a implementar en un solo lugar: usa la nube para un desarrollo continuo y descentralizado. Usa MongoDB Atlas, Firebase, Amazon AWS, GitHub como repositorio de código, pruebas unitarias automatizadas, Heroku y DockerHub para su despliegue. Si no tienes cliente, puedes usar Postman. Todo esto ayudará a mejorar tu productividad. No importa dónde estés, simplemente desarrolla tu API para que esté siempre disponible.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://freepngimg.com/thumb/cloud_computing/23593-5-cloud-computing-free-download.png" alt="Logo"></p>
<h2>NodeMonREST</h2>
<p>Ejemplo de <a href="https://nodemonrest.herokuapp.com/" target="_blank" rel="noopener noreferrer">API REST</a> en Node.js, usando Mongo, JWT y AWS S3 con CI/CD bajo GitHub Actions para fines docentes.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://github.com/joseluisgs/NodeMonRest/workflows/NodeMonRest CI/CD/badge.svg" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/Docker-passing-blue" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/Heroku-passing-blueviolet" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/github/v/release/joseluisgs/NodeMonRest" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/github/license/joseluisgs/NodeMonRest" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/JS Code-ES2019-yellow" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/JS Style-AirBnB-ff69b4" alt="Logo">
</p>
<h3>Descripción</h3>
<p>Este proyecto tiene nombre de Pokemon 😃. El objetivo principal docente es aplicar distintas técnicas para construir un esqueleto de API REST usable en distintos proyectos. La idea es hacer un esqueleto lo suficientemente genérico, adaptable y extensible en módulos para ser aplicado en distintos problemas y con él resolver cuestiones que se nos pueden presentar genéricas en cada uno de ellos, con el objetivo de mostrar para el ámbito docente cómo poder realizarlo. Es una aplicación puramente docente. Entre las distintas técnicas usadas destacan:</p>
<ul>
<li>Distribución de los elementos del sistema. Tenemos distribuidos en distintos nodos cada uno de los componentes cruciales del sistema: código, información y almacenamiento de ficheros.</li>
<li>Patrón <a href="https://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador" target="_blank" rel="noopener noreferrer">MVC</a>. La vista será cualquier cliente que consuma nuestra API.</li>
<li><a href="https://lemoncode.net/lemoncode-blog/2018/1/29/javascript-asincrono" target="_blank" rel="noopener noreferrer">Asincronía</a> y respuesta a eventos. Uso de promesas e interacción basada en eventos, que es uno de los aspectos más fuertes de Node.js.</li>
<li>Acceso a bases de datos NoSQL usando MongoDB.</li>
<li>Autenticación y autorización usando <a href="https://jwt.io/introduction/" target="_blank" rel="noopener noreferrer">JWT</a>.</li>
<li>Autorización basada en permisos de usuario.</li>
<li>Manejo de <a href="https://developer.mozilla.org/es/docs/Web/HTTP/Access_control_CORS" target="_blank" rel="noopener noreferrer">CORS</a>.</li>
<li>Algunos <a href="https://sourcemaking.com/design_patterns" target="_blank" rel="noopener noreferrer">patrones de diseño</a> conocidos.</li>
<li>JS código <a href="https://www.ecma-international.org/ecma-262/" target="_blank" rel="noopener noreferrer">ECMA2019</a>. De esta manera nos aseguramos seguir los estándares marcados para este tipo de lenguaje, pero tratando los módulos como indica Node.js, usando Babel para compatibilidad. Además, se ha aplicado el estilo <a href="https://airbnb.io/javascript/" target="_blank" rel="noopener noreferrer">AirBnB</a>, uno de los más seguidos con el objetivo de mantener una filosofía de sintaxis y estilo de programación ampliamente seguida en la comunidad JS/Node.</li>
<li>Almacenamiento en la nube usando <a href="https://aws.amazon.com/es/" target="_blank" rel="noopener noreferrer">AWS</a>.</li>
<li>Desarrollo <a href="https://www.redhat.com/es/topics/devops/what-is-ci-cd" target="_blank" rel="noopener noreferrer">CI/CD</a> usando <a href="https://github.com/features/actions" target="_blank" rel="noopener noreferrer">GitHub Actions</a>.</li>
</ul>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://danielpecos.com/assets/2017/09/swaggerfornodejs.jpg" alt="Logo"></p>
<h3>Tecnologías y librerías usadas</h3>
<div class="hint-container warning">
<p class="hint-container-title">Atención</p>
<p>Este proyecto tiene algo de antigüedad y puede que las librerías estén en desuso. Te recomiendo que eches un ojo a <a href="#typescript-api-rest">TypeScript API REST</a> porque, siguiendo esta misma filosofía, puedes ver una versión más moderna en el uso de librerías, organización de código y distintas versiones de persistencia de la información.</p>
</div>
<ul>
<li><a href="https://nodejs.org/es/" target="_blank" rel="noopener noreferrer">Node.js</a>. JS en servidor.</li>
<li><a href="https://www.mongodb.com/es" target="_blank" rel="noopener noreferrer">MongoDB</a>. He usado su versión en la nube <a href="https://www.mongodb.com/cloud/atlas" target="_blank" rel="noopener noreferrer">Atlas</a>.</li>
<li><a href="https://expressjs.com/es/" target="_blank" rel="noopener noreferrer">Express</a>. <em>Framework</em> de aplicaciones web para la API. Con él creo y gestiono las rutas. Además, nos permite fácilmente crear <em>middlewares</em>, con lo cual podremos aplicar logs específicos, filtrar para autorizaciones y autenticaciones y ampliar mediante <em>middleware</em>. Es lo que más me gusta de esta librería. Te recomiendo mirar el código de los ficheros <em>route</em> y <em>middleware</em> para ver cómo realizo estas acciones. Una de las cosas importantes es cómo he creado el servidor para que pueda ser levantado como instancia en cada una de las pruebas.</li>
<li><a href="https://mongoosejs.com/" target="_blank" rel="noopener noreferrer">Mongoose</a>. Conjunto de librerías para operar con bases de datos MongoDB. He implementado el acceso usando <em>singleton</em>.</li>
<li><a href="https://www.npmjs.com/package/jwt-simple" target="_blank" rel="noopener noreferrer">JWT-Simple</a>. Para implementar la autenticación basada en JWT. Esta librería actúa en base a <em>middleware</em> con Express. Los propios tokens caducan dependiendo del valor de <code>.env</code> <code>TOKEN_LIFE</code> en minutos. Para la parte de autorización, también hemos encapsulado en ellos los permisos de usuario que tengan. También hemos usado el <a href="https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/" target="_blank" rel="noopener noreferrer">refresco de tokens</a>, en base a UUID almacenando los tokens de refresco en MongoDB con un índice TTL de la colección en base al valor de <code>.env</code> <code>TOKEN_REFRESH</code> en minutos. De esta manera se autodestruyen pasado ese tiempo y libera el token de refresco asociado al token de usuario, dando un poco de seguridad extra. El objetivo de implementar este tipo de token de refresco es que, si el <em>access token</em> tiene fecha de expiración, una vez que caduca el usuario tendría que autenticarse de nuevo para obtener un <em>access token</em>. Con el <em>refresh token</em>, este paso se puede saltar y con una petición a la API obtener un nuevo <em>access token</em> que permita al usuario seguir accediendo a los recursos de la aplicación hasta que el <em>refresh token</em> caduque. Se debe tener en cuenta que el TTL del token de autenticación debe ser menor que el de refresco.</li>
<li><a href="https://www.npmjs.com/package/bcrypt" target="_blank" rel="noopener noreferrer">BCrypt</a>. Librería de criptografía para manejar las contraseñas de los usuarios.</li>
<li><a href="https://www.npmjs.com/package/body-parser" target="_blank" rel="noopener noreferrer">Body Parser</a>. <em>Middleware</em> que parsea los <em>body</em> como objetos (ya no se utiliza, pero cuando hice este proyecto sí; puedes mirar <a href="#typescript-api-rest">el ejemplo con TypeScript</a> siguiente para ver otras opciones).</li>
<li><a href="https://www.npmjs.com/package/cors" target="_blank" rel="noopener noreferrer">Cors</a>. <em>Middleware</em> para manejo de <a href="https://developer.mozilla.org/es/docs/Web/HTTP/Access_control_CORS" target="_blank" rel="noopener noreferrer">CORS</a>.</li>
<li><a href="https://www.npmjs.com/package/dotenv" target="_blank" rel="noopener noreferrer">Dotenv</a>. Para leer las variables de entorno del fichero <code>.env</code>.</li>
<li><a href="https://www.npmjs.com/package/morgan" target="_blank" rel="noopener noreferrer">Morgan</a>. <em>Middleware</em> <em>Request logger</em> el cual nos permitirá sacar logs de nuestras peticiones HTTP.</li>
<li><a href="https://www.npmjs.com/package/uuid" target="_blank" rel="noopener noreferrer">UUID</a>. Implementa el RFC4122 UUID para los tokens de refresco.</li>
<li><a href="https://www.npmjs.com/package/express-fileupload" target="_blank" rel="noopener noreferrer">Express-fileupload</a>. Es un <em>middleware</em> para Express el cual nos ayuda a procesar peticiones <em>multipart</em> o subida de imágenes. Se ha puesto que el tamaño máximo por imagen sea 2 MB, aunque se puede cambiar en el fichero <code>.env</code>. Los directorios para almacenar imágenes o ficheros están en <code>.env</code>; puedes poner el mismo o lo que quieras, pues se crean dinámicamente dentro de <code>public/uploads</code> (<code>FILES_PATH</code>) y son accesibles directamente por la ruta <code>url/files</code> (<code>FILES_URL</code>). Puedes usar el mismo si quieres.</li>
<li><a href="https://aws.amazon.com/es/" target="_blank" rel="noopener noreferrer">AWS</a>. Se ha implementado el sistema de almacenamiento en la nube para no depender localmente del servidor. Si quieres la versión en el servidor, revisa <a href="https://github.com/joseluisgs/NodeMonRest/tree/Ficheros_Locales" target="_blank" rel="noopener noreferrer">esta rama</a>. La idea de usar este tipo de tecnologías es aprender a usar almacenamiento en la nube siguiendo la filosofía de distribución de cada uno de los elementos del sistema: código, bases de datos y ficheros.</li>
<li><a href="https://www.npmjs.com/package/@hapi/joi" target="_blank" rel="noopener noreferrer">Joi</a>. Nos sirve para validar los datos de entrada en base a un esquema de validación, por si no usamos la validación en los propios esquemas de Mongo. Es importante que el <em>back</em> valide todos los datos por si se ha escapado algo del <em>front</em>. No podemos dejar nada a la suerte. ¡Luke, somos la última esperanza!</li>
<li><a href="https://www.npmjs.com/package/mongoose-unique-validator" target="_blank" rel="noopener noreferrer">Mongoose-unique-validator</a>. Nos sirve para validar los campos <em>unique</em>. Actúa como <em>middleware</em>.</li>
<li><a href="https://www.npmjs.com/package/underscore" target="_blank" rel="noopener noreferrer">Underscore</a>. Nos permite extender las posibilidades de la programación funcional para algunos métodos.</li>
<li><a href="https://www.npmjs.com/package/express-handlebars" target="_blank" rel="noopener noreferrer">Express-handlebars</a>. Personalmente, uno de los mejores motores de plantillas para Node.js, basado en <a href="https://handlebarsjs.com/" target="_blank" rel="noopener noreferrer">Handlebars</a>. Lo he usado de ejemplo para hacer algunas páginas estáticas de presentación de la API.</li>
<li><a href="https://mochajs.org/" target="_blank" rel="noopener noreferrer">Mocha</a> y <a href="https://www.chaijs.com/" target="_blank" rel="noopener noreferrer">Chai</a>. Se han utilizado estas librerías para los test por su funcionalidad y porque se adaptan perfectamente al proceso de integración continua que se ha marcado como objetivo.</li>
<li><a href="https://babeljs.io/" target="_blank" rel="noopener noreferrer">Babel</a> y <a href="https://eslint.org/" target="_blank" rel="noopener noreferrer">ESLint</a> con el objetivo de construir un código ampliamente compatible y estandarizado de JS.</li>
<li><a href="https://github.com/features/actions" target="_blank" rel="noopener noreferrer">GitHub Actions</a>. Es una de las grandes herramientas que se ha usado para la integración/distribución continuas <a href="https://www.redhat.com/es/topics/devops/what-is-ci-cd" target="_blank" rel="noopener noreferrer">CI/CD</a>. Para ello hemos integrado el entorno de ejecución con pruebas y el despliegue como <em>docker</em> en <a href="https://hub.docker.com/r/joseluisgs/nodemonrest" target="_blank" rel="noopener noreferrer">DockerHub</a> y su despliegue para su uso en <a href="https://nodemonrest.herokuapp.com/" target="_blank" rel="noopener noreferrer">Heroku</a>.</li>
</ul>
<h3>Repositorio</h3>
<div class="hint-container tip">
<p class="hint-container-title">Proyecto disponible:</p>
<ul>
<li><a href="https://github.com/joseluisgs/NodeMonRest" target="_blank" rel="noopener noreferrer">GitHub</a>.</li>
<li><a href="https://nodemonrest.herokuapp.com/" target="_blank" rel="noopener noreferrer">Heroku</a></li>
<li><a href="https://hub.docker.com/r/joseluisgs/nodemonrest" target="_blank" rel="noopener noreferrer">DockerHub</a></li>
</ul>
</div>
<h2>Notas-Back-NEM</h2>
<p>Este proyecto tiene como objetivo hacer la parte de <em>backend</em> por capas de una aplicación de notas para consumirla con distintos clientes usando las tecnologías de Node.js, MongoDB y Express, y está basada en el proyecto NodeMonREST. Como implementación de almacenamiento se ha utilizado Google Firebase Cloud Storage.</p>
<h3>Repositorio</h3>
<div class="hint-container tip">
<p class="hint-container-title">Proyecto disponible:</p>
<ul>
<li><a href="https://github.com/joseluisgs/notas-back-nem" target="_blank" rel="noopener noreferrer">GitHub</a>.</li>
</ul>
</div>
<h2>TypeScript API REST</h2>
<p>Ejemplo de una API REST realizada con TypeScript. Autenticación, CRUD, transferencia de ficheros. Modos de trabajo en: memoria, MongoDB o MariaDB. Despliegue en Docker y test con Jest.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/TypeScript-Ready-3178c6" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/Docker-Ready-2496ed" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/NodeJS-Ready-83BA63" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/JS Style-AirBnB-ff69b4" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/github/license/joseluisgs/todo-native-script" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/github/last-commit/joseluisgs/ts-api-rest" alt="Logo">
</p>
<h3>Descripción</h3>
<p>El proyecto consiste en que tengas un ejemplo de API REST, pero realizada con TypeScript, con el objetivo de mejorar con tipos tus desarrollos. Además, propone el uso de ficheros, autenticación y autorización mediante JWT. Tiene tres modos de uso: memoria, con MongoDB (NoSQL) y MariaDB (SQL). Acceso desde: http://localhost:8000.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://res.cloudinary.com/practicaldev/image/fetch/s--06ezZhc0--/c_imagga_scale,f_auto,fl_progressive,h_900,q_auto,w_1600/https://dev-to-uploads.s3.amazonaws.com/i/cosh3nee9nkfwnsmpn9p.png" alt="Logo"></p>
<h3>Arquitectura y diseño</h3>
<p>El diseño de esta API REST se corresponde con el patrón Servidor-&gt;Enrutador-&gt;Controlador-&gt;Modelo.
El servidor escucha en un puerto diversas peticiones. Las procesa según su ruta o punto de entrada y se las pasa al enrutador.
El enrutador analiza la petición dependiendo de la ruta y se la pasa al controlador correspondiente a dicha ruta, que ejecutará el método indicado.
El controlador realiza el método indicado según la ruta, consultando los modelos y el almacenamiento para ello.
El modelo es la estructuración de los datos a tratar.</p>
<p>En todo momento se ofrece información de la petición en base a los códigos de estado HTTP.</p>
<h3>Modos de funcionamiento</h3>
<p>Este proyecto está basado en dos modos de funcionamiento en la URL: http://localhost:8000.</p>
<ul>
<li>Memoria: usando almacenamiento en memoria. Lo tienes en la rama memoria.</li>
<li>MongoDB: usando almacenamiento en MongoDB. Lo tienes en la rama MongoDB.</li>
<li>MariaDB: usando almacenamiento en MariaDB. Lo tienes en la rama MariaDB.</li>
</ul>
<h3>Autenticación y autorización: JWT y <em>middleware</em></h3>
<p>Se ha implementado un sistema de autenticación y autorización basado en JWT y aplicando un <em>middleware</em> para analizar si el usuario puede entrar a un recurso, ya sea porque está autenticado para ello (<em>auth</em>), tiene permisos dependiendo de su rol (<em>grant</em>) o dicho recurso le pertenece si tenemos datos que los relacionan (<em>owner</em>). Se ha jugado con distintas políticas dependiendo del recurso y se puede adaptar a las distintas necesidades del problema. En el código podrás ver distintas soluciones con <em>middleware</em> o dentro del controlador.</p>
<h3>Validadores de datos</h3>
<p>Se han implementado dos sistemas de validación de campos según los requisitos de los tipos de datos. Por un lado, un <em>middleware</em> de validación y por otro, si no queremos hacerlo de esta manera, con funciones auxiliares en el propio controlador.</p>
<h3><em>Endpoints</em></h3>
<p>Los <em>endpoints</em> para conectarse y consumir esta API REST empiezan siempre por <code>/api/vx/recurso</code>, donde <code>x</code> es la versión de esta API y <code>recurso</code> es el recurso a consumir, por ejemplo <code>/api/v1/juegos</code> desde http://localhost:8000.</p>
<p>| Método | Recurso            | Auten./Autor.         | Descripción                                                             |
|</p>
]]></content:encoded>
      <enclosure url="https://bs-uploads.toptal.io/blackfish-uploads/blog/article/content/cover_image_file/cover_image/616614/0405_Building_a_Node.js-TypeScript_REST_API_Zara_Newsletter___blog-1507ad3436895bfe7cc6cf35e4efb17f.png" type="image/png"/>
    </item>
    <item>
      <title>Soy nuevo GitHub Campus Advisor</title>
      <link>https://joseluisgs.dev/posts/2021/2021-06-02-github-advisor.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-06-02-github-advisor.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Soy nuevo GitHub Campus Advisor</source>
      <description>O como me han reconocido la labor de aplicar GitHub a nivel docente</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 02 Jun 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Hace un tiempo comenté cómo GitHub se había convertido en mi <a href="https://joseluisgs.github.io/blog/2021-05-11-github-imprescindible.html#github-educativo" target="_blank" rel="noopener noreferrer">herramienta imprescindible</a>. Desde hace unos días, soy <em>GitHub Campus Advisor</em> y con ello se me presenta otro nuevo reto: ayudar a profesores y alumnos/as a cómo usar GitHub para el desarrollo de <em>software</em> y aprovecharlo a nivel docente.</p>
<!-- more -->
<h2>GitHub Campus Advisor</h2>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://scontent-mad1-1.xx.fbcdn.net/v/t1.6435-0/p320x320/37719204_1032949566864994_5259837527317020672_n.png?_nc_cat=108&ccb=1-3&_nc_sid=e3f864&_nc_ohc=8JHthO64zLoAX-wpflD&_nc_oc=AQnxsi041y2XxmZDkm5_6RQzGwuirKUF5vOPiM4vhBBRzFCvtLPb6CfjCA9lzu1HckY&_nc_ht=scontent-mad1-1.xx&tp=30&oh=0558fc54302f86840c776f5ad035e6db&oe=60BF929A" alt="Logo"></p>
<p>A día de hoy, considero fundamentales las tecnologías de <a href="https://joseluisgs.github.io/blog/2021-05-11-github-imprescindible.html#github-educativo" target="_blank" rel="noopener noreferrer">GitHub</a> y su paquete educativo. Ahora me han nombrado <em>GitHub Campus Advisor</em>. Me encanta la idea de poder ayudar a profesorado y alumnado a desarrollar nuestro código usando GitHub. A la misma vez, me hace sentir muy feliz por mi centro, compañeros/as y alumnado, que se beneficiarán de ello, así como por cualquier persona que necesite mi ayuda. Y, sobre todo, me ilusiona poder ayudar y responder dudas que la comunidad me haga. Es un honor que GitHub confíe en mí para ello.</p>
<h3>¿Qué haré?</h3>
<p>Seguiré con mi rutina diaria, pues ya hemos demostrado en cursos como DAW que integrar GitHub en todo el proceso de enseñanza-aprendizaje es posible en todos los módulos de segundo. Podéis ver la experiencia en nuestros <a href="https://informaticacifpvg.netlify.app/proyectos/innovacion/" target="_blank" rel="noopener noreferrer">proyectos de innovación</a>.</p>
<p style="text-align:center;">
 <a href="https://education.github.com" target="_blank"> 
     <img loading="lazy" style="border-radius: 0.25rem;" src="https://sdtimes.com/wp-content/uploads/2018/06/40984500-0e7a615e-68b0-11e8-8fdf-e8b14a2b14d7.png" alt="Log1" borderradius="1rem" boxshadow="0 5px 18px rgba(0,0,0,0.3)">
 </a>
</p>
<p>Mostraré y aplicaré mi experiencia docente y como desarrollador aplicando las tecnologías de GitHub en mis módulos, tutoriales y proyectos. Fomentaré el uso en la comunidad educativa y seré soporte de quien lo necesite. Así que, si os interesa el material o simplemente os puedo ayudar en algo, no dudéis en contactar conmigo. Será un placer poder echaros un cable con ello. ¡Cuenta conmigo! 💪</p>
<p>Se vienen cosas muy interesantes en el futuro y, con GitHub, desarrollar y aprender será mucho más fácil.</p>
<p style="text-align:center;">
 <a href="https://education.github.com/teachers/advisors" target="_blank"> 
     <img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/dxgrokV.jpg" alt="Log1" height="250" borderradius="1rem" boxshadow="0 5px 18px rgba(0,0,0,0.3)">
 </a>
 <a href="https://education.github.com/teachers/advisors" target="_blank"> 
     <img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/PpZeJcs.png" alt="Log1" height="250" borderradius="1rem" boxshadow="0 5px 18px rgba(0,0,0,0.3)">
 </a>
</p>
]]></content:encoded>
      <enclosure url="https://i.ytimg.com/vi/uWsXEmaM3PA/maxresdefault.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Construyendo una skill de Alexa</title>
      <link>https://joseluisgs.dev/posts/2021/2021-05-24-skill-informatica.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-05-24-skill-informatica.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Construyendo una skill de Alexa</source>
      <description>Ejemplo de una Skill para el Departamento de Informática</description>
      <category>Proyectos</category>
      <pubDate>Mon, 24 May 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>La <a href="https://www.amazon.es/JLGS-Inform%C3%A1tica-Virgen-de-Gracia/dp/B088JPBLS9/ref=sr_1_8?__mk_es_ES=%C3%85M%C3%85%C5%BD%C3%95%C3%91&amp;dchild=1&amp;keywords=informatica+skill&amp;qid=1621671069&amp;sr=8-8" target="_blank" rel="noopener noreferrer">Skill del Departamento de Informática</a> representa un proyecto de innovación docente con fines didácticos con el objetivo de cómo diseñar un asistente de voz para conocer los ciclos existentes de la familia de informática. Te comento en esta entrada las bases del desarrollo de una <em>skill</em>.</p>
<!-- more -->
<h2>La Skill</h2>
<p>Esta entrada voy a comenzar al revés 😉, te muestro el resultado y luego te iré comentando pasos o ideas para desarrollar una <em>skill</em>.</p>
<p>Como he dicho, esta <em>skill</em> es un ejemplo didáctico y no es la mejor solución técnica al 100% por varios motivos, pero sí es muy útil para su uso docente y sobre todo para conocer el proceso de creación de una <em>Skill</em> de Amazon Alexa. Para ello sería interesante haber usado bases de datos o servicios web para hacer búsquedas más potentes y no ficheros, sobre todo para los datos locales. Pero se muestra el uso y consumo de servicios web en otras partes de su funcionalidad. Lo importante es conocer el proceso de desarrollo y diseño de interacción de voz (<em>front-end</em>) y el procesamiento de intenciones y eventos (<em>back-end</em>), y se propondrá como ampliación otras tecnologías como las indicadas en un futuro.</p>
<p>Esta <em>skill</em> representa un proyecto de innovación docente con fines didácticos con el objetivo de cómo diseñar un asistente de voz para conocer los ciclos existentes de la familia de informática, conocer los módulos, obtener información detallada de los mismos, saber cómo contactar, conocer las noticias del centro y del departamento, recordar tareas a realizar o cosas curiosas relacionadas con el desarrollo de <em>software</em>.</p>
<p>La <em>skill</em> se ha desarrollado dentro de los programas de formación de Desarrollo de Aplicaciones Multiplataforma (DAM), Desarrollo de Aplicaciones Web (DAW), Administración de Sistemas Informáticos y Redes (ASIR) y Sistemas Microinformáticos en Red (SMR), ciclos de formación profesional que se pueden estudiar en nuestro centro.</p>
<div class="hint-container tip">
<p class="hint-container-title">Tecnologías</p>
<ul>
<li><a href="https://developer.amazon.com/es/documentation/" target="_blank" rel="noopener noreferrer">Alexa Developer Doc</a></li>
<li><a href="https://developer.amazon.com/es-ES/docs/alexa/ask-overviews/build-skills-with-the-alexa-skills-kit.html" target="_blank" rel="noopener noreferrer">Alexa Skill Kit</a></li>
<li><a href="https://developer.amazon.com/es-ES/docs/alexa/alexa-voice-service/get-started-with-alexa-voice-service.html" target="_blank" rel="noopener noreferrer">Alexa Voice Service</a></li>
<li>Node.js: en el <em>Backend</em>.</li>
<li>Lambda Function. Como parte del AWS <em>Serverless</em>.</li>
</ul>
</div>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://informaticacifpvg.netlify.app/assets/img/proyectos/departamento_skill.jpg" alt="Logo"></p>
<h2>Desarrollando para Alexa</h2>
<p>Antes de nada, quiero presentarte el <a href="https://github.com/joseluisgs/alexa-skill-tutorial" target="_blank" rel="noopener noreferrer">tutorial</a> que hicimos en el grupo de trabajo del <a href="https://informaticacifpvg.netlify.app/proyectos/departamento_skill_alexa/" target="_blank" rel="noopener noreferrer">Dpto. de Informática de Virgen de Gracia</a>, donde se muestran los pasos para hacer una <em>skill</em> de manera más detallada y que resumo en esta entrada de la web.</p>
<div class="hint-container tip">
<p class="hint-container-title">¿Cómo hacer una skill?</p>
<ul>
<li><a href="https://github.com/joseluisgs/alexa-skill-tutorial" target="_blank" rel="noopener noreferrer">Tutorial disponible en repositorio 💻</a></li>
</ul>
</div>
<h3>¿Qué es una Skill de Alexa?</h3>
<p>Alexa es el servicio de voz ubicado en la nube de Amazon disponible en los dispositivos de Amazon y dispositivos de terceros con Alexa integrada. Además, cuenta con funcionalidades, o lo que Amazon llama «Skills», que permiten a los consumidores crear una experiencia más personalizada.</p>
<h3>¿Qué es el Alexa Skills Kit?</h3>
<p>El Alexa Skills Kit (ASK) es un conjunto de herramientas, documentación, muestras de código y API en <em>self-service</em> con el que puedes añadir <em>Skills</em> a Alexa de forma rápida y sencilla. El ASK permite a diseñadores, desarrolladores y marcas crear <em>Skills</em> atractivas y llegar a los consumidores. Con este kit, puedes aprovechar el conocimiento y la innovación de Amazon en el sector del diseño de voz.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://www.thurrott.com/wp-content/uploads/sites/2/2020/07/amazon-conversations.jpg" alt="Logo"></p>
<h3>Pasos para crear tu skill</h3>
<p>Primero se crea una nueva <em>Skill</em> de Alexa en la consola de desarrollo de Amazon y se configura el modelo de interacción para la interfaz de voz. Basándose en este modelo de interacción, se crea una función Lambda AWS (su <em>back-end</em>, por ejemplo en Node.js) que incluye la lógica del programa de tu <em>Skill</em>, la cual se ejecuta en la plataforma de computación en la nube AWS de Amazon. Para ello debemos crear el modelo interactivo de voz, probarlo y finalmente publicarlo.</p>
<h3>Arquitectura de una Skill</h3>
<p>Una <em>Skill</em> se basa en el siguiente funcionamiento:</p>
<ul>
<li>Esta <em>skill</em> se lanzará a través de la palabra de inicio (Alexa) seguida de <em>invocation name</em> (<em>nombre de invocación</em>, <em>skill</em>) que a su vez tendrá una serie de frases de declaración de intenciones (<em>intent</em>).</li>
<li>Un <em>intent</em> está compuesto por dos partes: los <em>utterances</em> (sentencias) que van a servir para invocarlo y los <em>slots</em>
(argumentos), opcionales, que puede tener.</li>
<li>Por lo tanto tendremos un ejemplo: <em>Alexa, abre informática y dime los módulos de 2.º DAM</em>.</li>
</ul>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://blogs.encamina.com/piensa-en-software-desarrolla-en-colores/wp-content/uploads/sites/21/2019/01/alexa-architecture.jpg" alt="Logo"></p>
<h3>Interaction Model: Invocación</h3>
<p>El <strong>interaction model del skill</strong> consta de, al menos, dos partes: el nombre de invocación de tu <em>skill</em> y el conjunto de <em>intent</em> (acciones) que deben corresponder con las peticiones de los clientes. Es el <em>Front-end</em> de
nuestra aplicación.</p>
<ul>
<li>Nombre de invocación: el nombre de invocación será lo que el usuario use para comenzar a interactuar
con tu <em>skill</em> y debe cumplir una serie de requisitos.</li>
<li>Creo que es bastante importante la elección que se hace porque el usuario podrá invocar el <em>skill</em> y ejecutar
un <em>intent</em> de una sola vez, <em>one-shot invocation</em>. Por ejemplo, &quot;Alexa, abre informática virgen de gracia y dime
los módulos de segundo DAM&quot; y tiene que tener sentido. El usuario también podría conseguir el mismo
resultado con: &quot;Alexa, abrir informática virgen de gracia DAM”.</li>
<li>A mí me gusta bastante la forma de usarlo de <em>one-shot invocation</em> y me he querido centrar en eso al diseñar
el modelo.</li>
</ul>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://www.ionos.es/digitalguide/fileadmin/DigitalGuide/Screenshots_2019/alexa-skills-programmieren-EN-7.png" alt="Logo"></p>
<h3>Interaction Model: Intents</h3>
<p>Un <strong>intent</strong> está compuesto por dos partes: los <em>utterances</em> (sentencias) que van a servir para invocarlo y los
<em>slots</em> (argumentos), opcionales, que puede tener. Un par de cosas sobre los <em>utterances</em>:</p>
<ul>
<li>Dentro de un mismo <em>intent</em> no puedes tener dos <em>utterances</em> repetidos, pero sí el mismo con distintos <em>slots</em>.</li>
<li>Los <em>utterances</em> los usa Alexa para inferir qué <em>intent</em> corresponde a lo que quiere hacer el usuario.</li>
<li>Es importante que fijes el orden porque irá resolviendo según cómo los diseñes.</li>
</ul>
<p><strong>Intents obligatorios</strong>
Al crear un <em>skill</em> nuevo hay algunos <em>intents</em> obligatorios, predefinidos ya por Alexa, que tienes que tener cubiertos y
te los indica la consola debajo de &quot;Built-in intents&quot;. Verás que son básicos, sin <em>slots</em>. Se pueden extender:</p>
<ul>
<li><code>NavigateHome</code>, que se usa para <em>skill</em> multimodal, los que tienen pantalla, y equivale a un <em>exit</em>.</li>
<li><code>Cancel</code>, se usaría para cancelar alguna interacción en proceso.</li>
<li><code>Stop</code>, para dejar de usar el <em>skill</em>.</li>
<li><code>Help</code>, se invocaría por el usuario para pedir ayuda a nuestro <em>skill</em>.</li>
<li>Para cada uno de estos tendrás que elegir <em>utterances</em> que vayan a invocarlo.</li>
</ul>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://res.cloudinary.com/hy4kyit2a/f_auto,fl_lossy,q_70/learn/modules/alexa-development-basics/how-to-design-voice-user-interface/images/1a5b4adf061c45f5e8b6172b6a481523_cj-8-udtea-300410-s-8-t-04-lhtyws.png" alt="Logo"></p>
<h3>Interaction Model: Build model</h3>
<p>Tenemos que pulsar el botón de &quot;Build Model&quot; y lo generará. El modelo que se genera no es
más que un JSON con toda la información que hemos ido construyendo con la consola. Este JSON se puede
ver una vez generado y modificar en vez de usar los <em>inputs</em> de <em>utterance</em> y todo eso que vimos antes.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://www.ionos.es/digitalguide/fileadmin/DigitalGuide/Screenshots_2019/alexa-skills-programmieren-EN-20.png" alt="Logo"></p>
<h3>Code</h3>
<p>Es la pestaña donde programamos la funcionalidad, <em>Back-end</em>. Se implementan dentro de un AWS Lambda. Con la ventaja de que te lo
interconecta todo automáticamente si usas Node.js o Python. Si quieres Java, lo tendrás que hacer de manera manual.
Importante:</p>
<ul>
<li><code>const Alexa = require('ask-sdk-core')</code> -&gt; librería a usar.</li>
<li>Tantos <code>RequestHandler</code> como necesitemos para manejar los <em>intents</em> que vengan del <em>skill</em>. El mismo <code>RequestHandler</code> puede manejar más de un <em>intent</em>.</li>
</ul>
<p>El método <code>canHandle</code> se usa para chequear si el <em>handler</em> puede manejar la petición que llega. Aquí la lógica básica que se suele hacer es mirar el
nombre del <em>intent</em> de la <em>request</em>.
El método <code>handle</code> es el encargado de recibir el <em>input</em> y construir la respuesta para el usuario a partir de su <em>request</em>. Es el método donde irá el código más interesante del <em>handler</em>. En el caso del ejemplo me gustaría destacar tres cosas:</p>
<ul>
<li><code>speak</code>, le estamos dando a la respuesta el texto que Alexa dirá de voz al usuario.</li>
<li><code>simpleCard</code>, aquí construimos una salida para Alexa que será útil para dispositivos con pantalla, como la <em>app</em> del móvil.</li>
<li><code>shouldEndSession</code>, con esto le indicamos a Alexa que, una vez manejada la <em>request</em>, no esperamos otra interacción con el usuario y cerramos la sesión, es decir, el <em>skill</em>.</li>
</ul>
<p>Si queremos elaborar una respuesta de dos tipos: texto que dirá Alexa con voz y, además, información para construir una &quot;tarjeta&quot; que mostrará Alexa
en pantalla. Por último, con la opción de finalizar la sesión indicamos que no esperamos una interacción posterior. Sería un <em>handler</em> orientado a una
petición concreta y sin diálogo.</p>
<p>En el caso de que nuestro <em>handler</em> quiera indicar a Alexa que esperamos seguir interactuando con el usuario, tenemos que construir la respuesta
con <code>reprompt</code>.
<code>exports.handler = Alexa.SkillBuilders.custom()</code> -&gt; ojo, el orden de los <em>handlers</em> importa a la hora de decidir cuál usará para manejar la petición del
usuario. Irá validando los <em>handlers</em> en el orden que los hemos registrado y usará el primero que retorne <code>true</code> en la validación del método <code>canHandle</code>. Son los <em>endpoints</em> de nuestra <em>skill</em>.</p>
<div class="language-js line-numbers-mode" data-highlighter="shiki" data-ext="js" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-js"><span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">const</span><span style="--shiki-light:#986801;--shiki-dark:#E5C07B"> LaunchRequestHandler</span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">    canHandle</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">handlerInput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> Alexa</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getRequestType</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">handlerInput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">requestEnvelope</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) </span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2">===</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> 'LaunchRequest'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    },</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">    // add the async keyword to this function so we can make async requests to the ISP API</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">    async</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF"> handle</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-light-font-style:inherit;--shiki-dark:#E06C75;--shiki-dark-font-style:italic">handlerInput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic">        // add these two lines</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        const</span><span style="--shiki-light:#986801;--shiki-dark:#E5C07B"> locale</span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> handlerInput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">requestEnvelope</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">request</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">locale</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        const</span><span style="--shiki-light:#986801;--shiki-dark:#E5C07B"> ms</span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> handlerInput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E5C07B">serviceClientFactory</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getMonetizationServiceClient</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        try</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> {</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">            const</span><span style="--shiki-light:#986801;--shiki-dark:#E5C07B"> inSkillProductsList</span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD"> await</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> ms</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getInSkillProducts</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">locale</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">            console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">inSkillProductsList</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        } </span><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">catch</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> (</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">err</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">) {</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B">            console</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">log</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">'ERROR'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">, </span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">err</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">);</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">        </span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        const</span><span style="--shiki-light:#986801;--shiki-dark:#E5C07B"> speakOutput</span><span style="--shiki-light:#0184BC;--shiki-dark:#56B6C2"> =</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> 'Welcome, you can say Hello or Help. Which would you like to try?'</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">;</span></span>
<span class="line"><span style="--shiki-light:#A626A4;--shiki-dark:#C678DD">        return</span><span style="--shiki-light:#383A42;--shiki-dark:#E5C07B"> handlerInput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">.</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">responseBuilder</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">            .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">speak</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">speakOutput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">            .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">reprompt</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">(</span><span style="--shiki-light:#383A42;--shiki-dark:#E06C75">speakOutput</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">)</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">            .</span><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">getResponse</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">();</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">    }</span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">};</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="http://alexaskillstutorials.com/wp-content/uploads/2020/01/make-money-alexa-skill-save-deploy-code-1024x437.png" alt="Logo"></p>
<h3>Test</h3>
<p>Nos vamos a la pestaña de <em>Test</em>, marcamos que estamos en <em>Development</em> y probamos a decir &quot;open Hello World&quot; o el <em>Invocation Name</em> que tiene de lanzamiento, o escribiéndolo en el cuadro de diálogo. De esta manera podremos probar nuestros <em>intents</em>, nuestros modelos interactivos y la respuesta del <em>back-end</em> a nuestras peticiones.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://www.ionos.es/digitalguide/fileadmin/DigitalGuide/Screenshots_2019/alexa-skills-programmieren-EN-30.png" alt="Logo"></p>
<h3>Internacionalización</h3>
<p>Copiamos nuestro JSON de interfaz (en inglés o español) y nos cambiamos a la pestaña de lenguaje en
español y lo pegamos.</p>
<ul>
<li>Traducimos nuestras frases. Le damos a construir y probamos. ¡Las respuestas siguen en inglés!</li>
<li>Hemos localizado el <em>Front-end</em>, pero no el <em>Back-end</em>. Ahora debes abrir el fichero <code>index.js</code> y traducir cada
frase, si quieres. Esto es para que utilices plantillas si no puedes usarlo todo en español desde el comienzo.</li>
</ul>
<h3>Publicación</h3>
<p>Si has probado tu nueva <em>Skill</em> de Alexa y la has encontrado satisfactoria, puedes ponerla a disposición de otros usuarios a través de la <em>Alexa Skills Store</em>. Debes proporcionar toda la información necesaria para su publicación.</p>
<p>Para ello, ve a la sección &quot;<em>Distribution</em>&quot; (publicación) haciendo clic en el botón del mismo nombre situado en el menú de navegación de la consola de desarrollo de Alexa. Rellena todos los campos obligatorios bajo &quot;<em>Skill Preview</em>&quot; (vista previa de habilidades), &quot;<em>Privacy &amp; Compliance</em>&quot; (privacidad y cumplimiento) y &quot;<em>Availability</em>&quot; (disponibilidad).</p>
<p>En &quot;<em>Skill preview</em>&quot;, introduce toda la información que debe mostrarse en la vista previa a los usuarios en el país de destino deseado.</p>
<h3>Certificación</h3>
<p>Una vez que hayas ingresado toda la información requerida para la publicación, puedes entregar tu <em>Skill</em> para su validación. Una vez que la <em>Skill</em> de Alexa que has programado complete con éxito el test funcional, estará lista para el paso final de la publicación, la &quot;<em>Submission</em>&quot; (enviar). Haz clic en &quot;<em>Submit for Review</em>&quot; (enviar para revisión) para certificar tu <em>Skill</em>.
Una vez que Amazon ha completado la revisión, recibirás un correo electrónico en la cuenta asociada a tu cuenta de desarrollador de Amazon. Existen básicamente dos escenarios posibles:</p>
<ul>
<li>Tu <em>Skill</em> se ha certificado con éxito: en este caso, se te comunicará por correo electrónico cuándo se espera que tu <em>Skill</em> se publique en la <em>Alexa Skills Store</em>.</li>
<li>Tu <em>Skill</em> no ha sido certificada: en este caso, Amazon ha identificado problemas durante el proceso de certificación. El correo electrónico incluirá un informe detallado de los cambios que se requieren para su certificación. Una vez que hayas hecho los ajustes necesarios, puedes volver a presentar tu <em>Skill</em> en cualquier momento.</li>
</ul>
<p>Puedes ver el estado actual de todas las <em>Skills</em> de Alexa que has creado en la vista general de <em>Skills</em> de la <em>Alexa Developer Console</em>.</p>
<ul>
<li>&quot;<em>In development</em>&quot;: tu <em>skill</em> está en desarrollo.</li>
<li>&quot;<em>Certification</em>&quot;: tu <em>skill</em> está en el proceso de certificación.</li>
<li>&quot;<em>Live</em>&quot;: tu <em>skill</em> está disponible para los usuarios a través de la <em>Alexa Skills Store</em>.</li>
</ul>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://cdn.outsource2india.com/software/images/alexa-skill-development-services.jpg" alt="Logo"></p>
<div class="hint-container tip">
<p class="hint-container-title">¿Cómo hacer una skill?</p>
<ul>
<li><a href="https://github.com/joseluisgs/alexa-skill-tutorial" target="_blank" rel="noopener noreferrer">Tutorial disponible en repositorio 💻</a></li>
</ul>
</div>
]]></content:encoded>
      <enclosure url="https://m.media-amazon.com/images/G/01/DeveloperBlogs/AlexaBlogs/default/ACK_blog._CB483519732_.png" type="image/png"/>
    </item>
    <item>
      <title>GitKraken superpoder para tus proyectos</title>
      <link>https://joseluisgs.dev/posts/2021/2021-05-20-gitkraken-superpoder-git.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-05-20-gitkraken-superpoder-git.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">GitKraken superpoder para tus proyectos</source>
      <description>Vitaminando nuestro trabajo de desarrollo con la mejor herramienta existente</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Thu, 20 May 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><a href="https://www.gitkraken.com/invite/wdJ7HntT" target="_blank" rel="noopener noreferrer">GitKraken</a> es para mí el mejor complemento existente para trabajar con Git y GitHub. Nos ayuda a mejorar nuestra productividad diaria con los repositorios y, además, a nivel docente es insuperable con muchas ventajas. Nos ofrece todo aquello para hacer tu trabajo más fácil, intuitivo y divertido. ¡Superpoder para tus proyectos!</p>
<!-- more -->
<h2>GitKraken y su poder</h2>
<p>Me gusta <a href="https://www.gitkraken.com/invite/wdJ7HntT" target="_blank" rel="noopener noreferrer">GitKraken</a>. Simple, sencillo, efectivo, divertido y poderoso 💪. Sin duda, es una de las mejores herramientas existentes para trabajar con Git a nivel local y con repositorios remotos como GitHub o GitLab. La rapidez y potencia es su principal característica, y la facilidad con la que gestionar repositorios, tu código y operar con ellos: ramas, conflictos, versiones, etiquetas, revertir cambios, etc.</p>
<p>Pero lo que más me gusta es su faceta educativa. GitKraken me ofrece un entorno visual lo suficientemente atractivo donde el alumnado puede ver el árbol de nuestros proyectos, seguir cada rama y confirmación de manera visual, colaborar en cambios, ver los cambios existentes o proponer mejoras. Además, GitKraken es ligero, multiplataforma, gratuito y además tienes licencia <em>pro</em> a nivel educativo.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://blog.axosoft.com/wp-content/uploads/2016/10/BluePrint-Blog-Header.png" alt="Logo"></p>
<h2>GitKraken UI</h2>
<p>El gran poder de GitKraken reside en la simplicidad de su <a href="https://support.gitkraken.com/start-here/interface/" target="_blank" rel="noopener noreferrer">interfaz de usuario</a>. Gracias a ella, puedes realizar todas las acciones de Git sobre tu repositorio y observar los cambios existentes. Su diseño en pestañas nos permite poder trabajar de manera muy cómoda con varios repositorios a la vez.</p>
<p>Por otro lado, nos permite poder integrar el mismo repositorio local con diferentes repositorios remotos, manejar etiquetas locales y remotas, hacer <em>pull request</em> o trabajar con acciones de GitHub.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://www.linuxadictos.com/wp-content/uploads/gitkraken.png" alt="Logo"></p>
<p>Pero, sin duda, su mayor atractivo es la parte central donde siempre tenemos a la vista el árbol de nuestro proyecto o código con las distintas ramas, versiones, colaboradores, cambios, etc. Fácilmente podemos navegar por los distintos cambios. A nivel docente esto me ha ayudado bastante, pues en nuestros proyectos cada sesión o momento importante en clase es mostrado en un <em><strong>commit</strong></em> y cada añadido de funcionalidad en una <em><strong>rama</strong></em>. El alumnado puede experimentar con sus propias versiones. Además, cada lección queda debidamente etiquetada con su <em><strong>tag</strong></em> y <em><strong>versión</strong></em>.</p>
<p>¿Quieres proponer un cambio o hacer un <em><strong>cherry-pick</strong></em>? Bueno, también puedes hacerlo arrastrando y soltando 🤯. ¿La has cagado con un <em>commit</em> o un <em>merge</em>? Tranquilidad, con GitKraken no hay problema, pues mientras en consola son algunos comandos los que debes aplicar, aquí tienes el típico botón de deshacer y rehacer 😁. ¿Un perfil para cada proyecto? También lo tienes 😉. Finalmente, su sistema de resolución de conflictos es de lo mejor que he visto: visual y fácil de usar.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/gs571xU.png" alt="Logo"></p>
<div class="hint-container tip">
<p class="hint-container-title">Ventajas</p>
<ul>
<li>Usabilidad y experiencia positiva.</li>
<li>Productividad.</li>
<li>Visibilidad del repositorio.</li>
<li>Control de conflictos.</li>
<li>Resolución de problemas.</li>
<li>Arrastrar y soltar.</li>
<li>Perfiles de uso.</li>
</ul>
</div>
<h3>Flujo de trabajo</h3>
<p>Otra de las cosas que más me gusta de GitKraken es la facilidad para gestionar un flujo de trabajo con <a href="https://www.gitkraken.com/learn/git/git-flow" target="_blank" rel="noopener noreferrer">GitFlow</a>. Siempre tenemos problemas para que el alumnado entienda cuándo y cómo debe crear una rama o gestionar los cambios a través de ellas. GitFlow me ofrece poder trabajar de esa manera de la forma más ordenada posible. Quizás sea excesivo, pero a ellos les ayuda a aclararse. De hecho, su <a href="https://support.gitkraken.com/img/documentation/repositories/gitflow/git-flow-start@2x.png" target="_blank" rel="noopener noreferrer">guía de cómo utilizarlo</a> es de las mejores y visualmente la más sencilla de comprender.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://support.gitkraken.com/img/documentation/repositories/gitflow/git-flow-start@2x.png" alt="Logo"></p>
<h3>Pull Request y colaboración</h3>
<p>Sinceramente, esto es de lo mejor de GitKraken. La facilidad para el trabajo colaborativo y lo visualmente atractivo que es para el alumnado. Hacer <a href="https://support.gitkraken.com/working-with-repositories/pull-requests/" target="_blank" rel="noopener noreferrer"><em><strong>pull request</strong></em></a> es tan sencillo como arrastrar y directamente realizar el cambio.</p>
<p>De la misma manera podemos resolver los conflictos, aprobar el cambio o realizar las acciones pertinentes sobre él. Seguimos sin salir del mismo entorno. Y además podemos ver estos cambios en nuestras <em>Boards</em>, como veremos más adelante. Comodidad ante todo.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://support.gitkraken.com/img/documentation/repositories/pull-request.gif" alt="Logo"></p>
<h3>Acciones</h3>
<p>Soy un gran fan del DevOps y del CI/CD. GitKraken me ofrece la posibilidad de trabajar fácilmente con las <a href="https://support.gitkraken.com/git-workflows-and-extensions/github-actions/" target="_blank" rel="noopener noreferrer">Acciones de GitHub</a>.</p>
<p>De esta manera, puedo crear un flujo de trabajo para automatizar, por ejemplo, despliegues en base a cambios en determinadas ramas o etiquetas. De hecho, puedo subir automáticamente mi proyecto a Docker Hub, o desplegarlo en Heroku o Netlify si pasa las pruebas fácilmente, como se puede ver en este <a href="https://github.com/joseluisgs/NodeMonRest/actions/runs/113842594" target="_blank" rel="noopener noreferrer">ejemplo</a>. Para mí es muy cómodo trabajar así y gestionar mi flujo de trabajo también desde GitKraken.</p>
<p style="text-align:center;"><iframe width="560" height="315" src="https://www.youtube.com/embed/qr3vwIvXUfc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<h3>Aprendiendo de lo que hacemos</h3>
<p>Seguro que has pensado que usando este tipo de herramienta uno no aprendería a usar con suficiente destreza Git. No es cierto. Primero, porque ya nos encargamos de eso 😉. Segundo, porque tiene una <a href="https://support.gitkraken.com/working-with-repositories/activity-logs/" target="_blank" rel="noopener noreferrer">consola de logs</a> muy buena donde puedes ver qué comandos se lanzan para cada acción e investigarlos. De hecho, ha sido un recurso muy interesante para que el alumnado comprenda la secuencia de comandos detrás de algunas acciones.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://support.gitkraken.com/img/documentation/repositories/activity.gif" alt="Logo"></p>
<h2>GitKraken para aprender Git</h2>
<p>Una de las cosas que más valoro de GitKraken es lo fácil que nos lo pone para aprender Git y el manejo de repositorios. Sus <a href="https://www.gitkraken.com/learn/git" target="_blank" rel="noopener noreferrer">tutoriales</a> son muy visuales y facilitan mucho cómo asimilar conceptos de <a href="https://www.gitkraken.com/learn/git/commands" target="_blank" rel="noopener noreferrer">Git</a>.</p>
<p>Además, tenemos chuletas de comandos para manejar <a href="https://www.gitkraken.com/downloads/gitkraken-git-gui-cheat-sheet.pdf" target="_blank" rel="noopener noreferrer">Git</a> y <a href="https://www.gitkraken.com/downloads/gitkraken-for-github-cheat-sheet-v3.pdf" target="_blank" rel="noopener noreferrer">GitHub</a>.</p>
<p>Y destaco mucho sus especiales de <a href="https://www.gitkraken.com/learn/git/best-practices" target="_blank" rel="noopener noreferrer">buenas prácticas</a> para saber no solo el qué, sino el cómo hacerlo y cuándo.</p>
<p style="text-align:center;"><iframe width="560" height="315" src="https://www.youtube.com/embed/ub9GfRziCtU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<div class="hint-container tip">
<p class="hint-container-title">Ventajas</p>
<ul>
<li>Tutoriales.</li>
<li>Guías temáticas.</li>
<li>Vídeos.</li>
<li>Chuletas.</li>
<li>Buenas prácticas.</li>
</ul>
</div>
<h2>GitKraken Suite</h2>
<p>Pero si queremos el superpoder completo, lo ideal es trabajar con la <em>Suite</em> que nos ofrece GitKraken. Está formada, además, por <em>Boards</em> y <em>Timelines</em>.</p>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://1v5ymx3zt3y73fq5gy23rtnc-wpengine.netdna-ssl.com/wp-content/uploads/2021/05/gitkraken-suite-desktop-landscape.png" alt="Educación">
</p>
<h3>Boards</h3>
<p><a href="https://www.gitkraken.com/boards" target="_blank" rel="noopener noreferrer"><em>Boards</em></a> es el complemento ideal para mis clases. Con ella planifico, gestiono mis tareas y muestro paso a paso lo que voy haciendo.</p>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://support.gitkraken.com/img/documentation/glo/start-glo-ing/glo-full.png" alt="Educación">
</p>
<p>Con <em>Boards</em> puedo trabajar con tableros <a href="https://support.gitkraken.com/boards/quick-start/" target="_blank" rel="noopener noreferrer">Kanban</a>, lo puedo sincronizar con mi repositorio GitHub y su <em>Projects</em>. Puedo llevar una lista de tareas, invitar al alumnado en proyectos colaborativos, adjuntar ficheros o versiones de los proyectos o tener un <a href="https://support.gitkraken.com/boards/calendar/" target="_blank" rel="noopener noreferrer">calendario</a> de clase. Además, podemos acceder directamente a ellos desde <a href="https://marketplace.visualstudio.com/items?itemName=axosoft.gitkraken-glo" target="_blank" rel="noopener noreferrer">VS Code</a>.</p>
<p>No solo la uso para proyectos. Me ayuda bastante como base para el cuaderno del profesor para poder planificar ya sea unidades, trimestres o módulos.</p>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://support.gitkraken.com/img/documentation/glo/calendar/add-card.gif" alt="Educación">
</p>
<h3>Timelines</h3>
<p>Una de las cosas más divertidas en clases es usar <a href="https://www.gitkraken.com/timelines" target="_blank" rel="noopener noreferrer"><em>Timeline</em></a> para mostrar el desarrollo de las prácticas o proyectos que realizamos. De esta manera, visualmente podemos ver su estado, evolución y versiones.</p>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://support.gitkraken.com/img/documentation/timelines/overview/timelines-add.gif" alt="Educación">
</p>
<p>A nivel educativo, supone una gran herramienta docente junto a <em>Boards</em>, pues podemos analizar cómo ha sido el proceso de enseñanza-aprendizaje. De hecho, no lo uso solo para proyectos, sino para mostrar cómo hemos desarrollado distintas unidades o temas, o el tiempo que nos ha llevado. Además, me sirve como retrospectiva para sacar conclusiones, reflexionar y tomar nota para futuros proyectos o cursos y se integra con <em>Boards</em>. Puedes tener un cuaderno del profesor bastante completo con ambas herramientas.</p>
<p style="text-align:center;"><iframe width="560" height="315" src="https://www.youtube.com/embed/3jTuGXzhd5Y" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<h2>Mi opinión</h2>
<p>No te lo voy a negar, GitKraken <em>Suite</em> es vitamina pura para dar clases de desarrollo o simplemente para mejorar tu productividad diaria como desarrollador, ya sea solo o en equipo. Además, con <em>Boards</em> y <em>Timelines</em> puedes gestionar un cuaderno del profesor muy completo. No todas las ventajas son para desarrollar o gestionar código.</p>
<p>Posiblemente por separado existan muchas herramientas mejores, pero tener todo integrado en el mismo flujo de trabajo es una ventaja fundamental. A nivel de clase nos sirve para unificar en un solo lugar una serie de herramientas que ayudan al trabajo diario de cómo enseñar y aprender a desarrollar <em>software</em>.</p>
<div class="hint-container tip">
<p class="hint-container-title">Mi superpoder</p>
<ul>
<li>Cliente de Git visual e interactivo.</li>
<li>Integración con GitHub (y GitLab, BitBucket).</li>
<li>Facilidad de <em>Pull Request</em>, conflictos, cambios y &quot;resolver cagadas&quot;.</li>
<li>Manejo de acciones para CI/CD.</li>
<li><em>Boards</em>, para planificar tu proyecto o clases con <em>Kanban</em>, lista de tareas o calendario.</li>
<li><em>Timelines</em> para ver &quot;la vida&quot; de tus proyectos, clases, temas, etc.</li>
</ul>
</div>
<div class="hint-container tip">
<p class="hint-container-title">¡Obtén tu superpoder!</p>
<p>¿Quieres GitKraken gratis? <a href="https://www.gitkraken.com/invite/wdJ7HntT" target="_blank" rel="noopener noreferrer">Haz clic aquí</a>.</p>
</div>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://blog.axosoft.com/wp-content/uploads/2018/03/v3.5_Thumbnail-01-450x253.png" alt="Educación">
</p>
]]></content:encoded>
      <enclosure url="https://www.dovesco.com/sites/default/files/2020-09/axosoft.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Proyecto de innovación COMUNICA</title>
      <link>https://joseluisgs.dev/posts/2021/2021-05-13-innovacion-comunica.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-05-13-innovacion-comunica.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Proyecto de innovación COMUNICA</source>
      <description>Metodología de enseñanza-aprendizaje en el desarrollo del software basada en retos multiequipo, desarrollo de competencias orientadas al entorno profesional</description>
      <category>Proyectos</category>
      <category>Docencia</category>
      <category>Investigación</category>
      <pubDate>Thu, 13 May 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Una de las cosas que más me gusta de mi profesión es la parte de innovación. No solo se trata de dar clase, sino de crear nuevos mecanismos para impartirla y con ellos intentar resolver problemas que existen en la sociedad. Te comento uno de los proyectos que tengo la suerte de coordinar este año.</p>
<!-- more -->
<h2>Descripción</h2>
<p>Cuando se planteó <strong>COMUNICA</strong>, pensamos en ir un pasito más allá de nuestro anterior <a href="https://informaticacifpvg.netlify.app/proyectos/innovacion_coworking/" target="_blank" rel="noopener noreferrer">proyecto</a>. El objetivo de este proyecto es desarrollar una metodología de enseñanza-aprendizaje que nos ayudaría a desarrollar tanto los Resultados de Aprendizaje como las capacidades profesionales, sociales y personales usando proyectos reales y, con ellos, fomentar su integración más rápida en la empresa, completando su formación para su adaptación al entorno profesional.</p>
<p>Finalmente, nuestro proyecto fue uno de los elegidos como <em>Proyectos de innovación educativa en Formación Profesional con el objetivo de introducir el empleo de nuevas metodologías didácticas, apoyados en recursos asociados a las nuevas tecnologías, conducentes a la mejora de la calidad educativa y la consecución de buenos resultados académicos del alumnado, con una planificación y desarrollo sostenibles en el tiempo</em> en este curso 2020/2021.</p>
<h2>En relación con la sociedad</h2>
<p>Por otro lado, queremos <strong>fomentar la conciencia social y trabajar con problemas reales</strong> resolubles para que el alumnado desarrolle mediante esta tecnología una aplicación similar a la que desarrollaría en su posible entorno de trabajo, lo que nos ayudaría a desarrollar no solo los Resultados de Aprendizaje indicados en cada ciclo de Formación Profesional, sino a desarrollar las capacidades profesionales, sociales y personales indicadas en cada uno de nuestros ciclos de la familia de Informática y Comunicaciones.</p>
<p>Es por ello que bajo este proyecto pondremos en práctica esta metodología de desarrollo basado en <strong>proyectos de manera colaborativa, ágil y multidisciplinar, intermodular</strong>, bajo los estándares de calidad y respaldada por el entorno laboral del alumnado, mediante la cual se realizará el desarrollo de un <em><strong>Sistema de Comunicación Aumentativo y Alternativo multiplataforma y accesible con funcionalidades de control domótico</strong></em> con el objetivo de ofrecer herramientas para la rehabilitación e integración de las personas con dificultades de este tipo, independientemente de los problemas que lo generen (paraplejia, parálisis cerebral, autismos, etc.).</p>
<div class="hint-container tip">
<p class="hint-container-title">Objetivo</p>
<p>Desarrollar una metodología de enseñanza-aprendizaje que desarrolle tanto los Resultados de Aprendizaje como las capacidades profesionales, sociales y personales usando proyectos reales y siguiendo técnicas ágiles y de calidad demandadas por el sector del desarrollo de <em>software</em> y que ayuden a la integración exitosa en el entorno profesional del desarrollo de <em>software</em>.</p>
</div>
<h2>Participantes</h2>
<h3>Alumnado</h3>
<p>Los ciclos formativos que participarán en el proyecto de innovación y el grado en el que se beneficiarán de este son:</p>
<ul>
<li>Alumnado de 2.º curso del CGS de Desarrollo de Aplicaciones Web.</li>
<li>Alumnado de 2.º curso del CGS de Desarrollo de Aplicaciones Multiplataforma.</li>
<li>Alumnado de 2.º curso del CGS de Administración de Sistemas Informáticos en Red.</li>
</ul>
<h3>Profesorado</h3>
<p>Miembros del departamento de Informática y Comunicaciones del <a href="https://informaticacifpvg.netlify.app" target="_blank" rel="noopener noreferrer">CIFP Virgen de Gracia</a>, principalmente los profesores que forman parte de los equipos educativos del alumnado participante.</p>
<h3>Participante externo</h3>
<p>Fundación para la Atención Integral de las Personas con Discapacidad Intelectual - FUENTE AGRIA de Ciudad Real, representada por ASPADES - La Laguna (Puertollano, Ciudad Real). Esta fundación representará el papel de <em>client owner</em>, es decir, es la que va a utilizar el producto final y va a participar en su desarrollo.</p>
<h2>Metodología aplicada</h2>
<h3>Integración de módulos</h3>
<p>La fuerte relación existente entre los módulos del segundo curso del ciclo de Desarrollo de Aplicaciones Multiplataforma (DAM), Desarrollo de Aplicaciones Web (DAW) y Administración de Sistemas Informáticos y Redes (ASIR) lo hace idóneo para un proyecto común que entrelace lo aprendido en cada módulo, reforzando el aprendizaje de los mismos y ampliando, incluso, contenidos.</p>
<p>El aula como empresa de desarrollo de <em>software</em> aplicando dinámicas de planificación SCRUM y KANBAN y de herramientas profesionales relacionadas con la forma de trabajo de una empresa moderna de desarrollo de <em>software</em>. Habilidades de organización, autogestión, colaboración, oratoria y síntesis (entre otras) serán desarrolladas. De esta manera, el alumnado titulará con los conocimientos y las habilidades que le permitirán integrarse de una forma más eficiente y rápida en el sector empresarial.</p>
<h3>Aprendizaje colaborativo basado en retos</h3>
<p>Mediante esta metodología se lanzará un reto en el aula, el cual se llevará a cabo a través de la filosofía Aprendizaje-Servicio. Se caracteriza por la intermodularidad, equipos docentes de ciclos autogestionados, evaluar para desarrollar en el desarrollo competencial y la adecuación del espacio de aprendizaje en entornos activos-colaborativos.</p>
<h3>Metodología orientada al marco laboral y al entorno empresarial</h3>
<ul>
<li>
<p><strong>DevOps</strong> (acrónimo inglés de <em>development</em> —desarrollo— y <em>operations</em> —operaciones—) es una práctica de ingeniería de <em>software</em> que tiene como objetivo unificar el desarrollo de <em>software</em> (Dev) y la operación del <em>software</em> (Ops). La principal característica del movimiento DevOps es defender enérgicamente la automatización y el monitoreo en todos los pasos de la construcción del <em>software</em>, desde la integración, las pruebas y la liberación hasta la implementación y la administración de la infraestructura.</p>
</li>
<li>
<p><strong>Desarrollo ágil</strong> de <em>software</em> que proporciona un enfoque para la toma de decisiones en los proyectos de desarrollo de <em>software</em>, y está relacionado con métodos de ingeniería del <em>software</em> basados en el desarrollo iterativo e incremental, donde los requisitos y soluciones evolucionan con el tiempo según la necesidad del proyecto. Así, el trabajo es realizado mediante la colaboración de equipos autoorganizados y multidisciplinares, inmersos en un proceso compartido de toma de decisiones a corto plazo. Esta filosofía se resume en los siguientes principios: individuos e interacciones sobre procesos y herramientas, <em>software</em> funcionando sobre documentación extensiva, colaboración con el cliente sobre negociación contractual y respuesta ante el cambio sobre seguir un plan.</p>
</li>
</ul>
<h2>El proyecto</h2>
<p>El proyecto consiste en el desarrollo de un comunicador aumentativo y alternativo que permita a personas con problemas de comunicación y parálisis no solo comunicarse directamente con otras personas que estén en la misma estancia, sino en distintos lugares. Permite, además, comunicarse con su entorno, es decir, poder realizar acciones con elementos de su hogar.</p>
<p>La aplicación será multidispositivo y hará uso de los <em>Amazon Web Services</em> y la tecnología de control por voz <em>Alexa</em>, seguimiento ocular mediante <em>Eye Tracking</em> y control táctil. De esta manera, <strong>COMUNICA</strong> ofrecerá un mecanismo que permita romper barreras y fomentar la rehabilitación e integración de las personas con dificultades de este tipo, independientemente de los problemas que lo generen (paraplejia, parálisis cerebral, autismos, etc.).</p>
<p>Para ello, el sistema <strong>COMUNICA</strong> hará uso de la teoría de relaciones de equivalencia de Sidman para crear una asociación entre pictogramas, sonido y acciones. Para ello, haremos uso de redes hipermedia para poder crear estructuras gramaticales sencillas basadas en sujeto + verbo + predicado, y con ello poder establecer comunicaciones sencillas con personas o realizar acciones con objetos. Esto es debido a que existe un conjunto de relaciones condicionales entre estímulos, de manera que a partir del entrenamiento explícito (directo) de una relación entre estímulos, aparecen nuevas relaciones implícitas entre los estímulos como resultado del entrenamiento.</p>
<h3>Mecanismos de comunicación</h3>
<p>El comunicador muestra una estructura de plantillas o redes hipermedia con imágenes similar a la que se hace de forma tradicional con las plantillas de pictogramas en papel. Las imágenes pueden ser pictogramas, fotografías y dibujos, y representan objetos que pueden pedirse o acciones que pueden ser realizadas. Cuando una imagen es seleccionada, se está indicando que se desea ese objeto o acción, y se escucha un sonido que es una frase o una palabra grabada. El sonido permite al que lo escucha saber qué desea el usuario y al propio usuario. Además, al elegir una imagen de una plantilla es posible ir a otra plantilla donde se puede continuar con la selección de más imágenes.</p>
<p>Esta &quot;navegación&quot; entre elementos de la red hipermedia y una gramática BNF o red semántica permite construir frases o hacer selecciones en las que se categoricen los objetos que se eligen, como podemos ver en el ejemplo de boceto de interfaz.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/iXDFWVH.jpg" alt="Logo"></p>
<p>Además, el sistema permite:</p>
<ul>
<li><strong>Comunicación persona-persona</strong>. Gracias a la asociación pictograma-sonido y sujeto más acción con la cual el usuario va navegando a través de la red hipermedia y el uso de altavoces, podremos recibir las intenciones que el usuario quiere expresar.</li>
<li><strong>Comunicación persona-persona remota</strong>. La aplicación permite enviar las intenciones del usuario a otro usuario usando los servicios web desarrollados y que el usuario remoto lo reciba en su dispositivo móvil.</li>
<li><strong>Comunicación persona remota-persona</strong>. La aplicación permite recibir, gracias a los servicios web, mensajes de personas que no estén presentes en el mismo espacio y reproducirlos en los altavoces manejados por nuestro sistema o realizar llamadas de urgencia y socorro si se necesita.</li>
<li><strong>Comunicación persona-entorno</strong>. La aplicación permite, mediante el uso de elementos gráficos, poder interactuar con el entorno: encender luces, calefacción, aire acondicionado, lavadora, portero, teléfono, etc., así como rutinas o disparadores de varias acciones simultáneas rompiendo las barreras existentes para que los usuarios puedan interactuar con los elementos de su entorno. Además, también permite que remotamente otra persona pueda interactuar con los elementos del entorno del usuario facilitando la labor de asistencia.</li>
<li><strong>Comunicación persona remota-entorno</strong>. La aplicación también permite que remotamente otra persona pueda interactuar con los elementos del entorno del usuario facilitando la labor de asistencia.</li>
<li><strong>Administración y creación de perfiles</strong>. Se podrán crear perfiles de usuarios para adaptar la construcción de frases de comunicación y acciones, así como personalizar los elementos gráficos y sonidos al perfil del usuario.</li>
<li><strong>Multiplataforma</strong>. La aplicación estará disponible en formato web y en móvil (tableta, teléfono y televisión).</li>
<li><strong>Interacción accesible</strong>. Los modos de interacción serán táctil, reconocimiento de voz y seguimiento ocular.</li>
<li><strong>Despliegue</strong>. Para un despliegue rápido se utilizará la tecnología Docker, que permite crear contenedores de <em>software</em>, de forma que la aplicación pueda rápidamente transportarse al servidor. Además, la aplicación estará disponible en un servidor web y en las tiendas de aplicaciones móviles.</li>
</ul>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/v7DCGef.jpg" alt="Logo"></p>
<h2>Conclusiones</h2>
<p>Como se ha explicado en los puntos anteriores, el objetivo del proyecto de innovación educativa <strong>COMUNICA</strong> es doble:</p>
<ul>
<li>Por un lado, construir una metodología que permita el desarrollo de competencias de forma integral.</li>
<li>Por otro, crear una herramienta de comunicación persona-persona y persona-entorno con el objetivo de romper las barreras de apoyo comunicativo para mejorar la calidad de vida de personas afectadas.</li>
</ul>
<p>Todos los puntos se esperan conseguir no solo creando un entorno favorable, sino con la participación y colaboración de todos los profesores implicados en el proyecto.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.imgur.com/ekQ3K1I.jpg" alt="Logo"></p>
<p>De la misma manera, me gusta pensar que no solo creamos un producto al uso en clase, sino que transmitimos unos valores y que ayudamos a las personas que lo pueden necesitar, lo que hace que el grado de implicación y motivación del alumnado aumente. Es solo por eso por lo que me hace sentir orgulloso poder hacer cosas como estas.</p>
<div class="hint-container tip">
<p class="hint-container-title">Reflexiones</p>
<p>Destaco sobre todo lo conseguido:</p>
<ul>
<li><em><strong>El desarrollo de competencias</strong></em> de forma integral por medio de una metodología aplicada a la creación de un producto real y útil, la integración de saberes de diferentes módulos y la colaboración con otras familias profesionales.</li>
<li><em><strong>El desarrollo de competencias profesionales y sociales</strong></em> a través del desarrollo de habilidades y aptitudes adecuadas para el desarrollo de <em>software</em>.</li>
<li><em><strong>Desarrollar las etapas de un desarrollo multidisciplinar basado en equipos</strong></em> de distintos perfiles.</li>
<li><em><strong>Desarrollar un modelo de metodología ágil centrada en el usuario</strong></em> en el desarrollo de sistemas interactivos multiplataforma aplicable al aula en un curso académico.</li>
<li><em><strong>Establecer mecanismos de desarrollo colaborativo entre alumnado de distintos ciclos</strong></em> simulando una empresa de <em>software</em> con distintas sedes.</li>
<li><em><strong>Establecer el espíritu solidario</strong></em> a través del desarrollo de un sistema de comunicación aumentativo y alternativo basado en redes hipermedia con la capacidad de control domótico.</li>
<li><em><strong>Implementar diversos sistemas de interacción con el usuario en distintos dispositivos</strong></em>: táctil, voz y ocular.</li>
<li><em><strong>Ampliar los conocimientos desarrollados en la formación de Centro de Trabajo realizado por el alumnado y en la formación DUAL</strong></em> recibida en los distintos programas del centro.</li>
<li><em><strong>Desarrollar en el alumno</strong></em> las capacidades necesarias para:
<ul>
<li><em>Entrevistarse</em> con clientes, guiarlos y obtener información clave para el desarrollo.</li>
<li><em>Integrarse</em> en equipos de desarrollo y comunicarse de forma fluida y efectiva.</li>
<li><em>Responsabilizarse</em> de los compromisos adquiridos en un proyecto con el cliente.</li>
<li>Ser capaz de realizar, mediante técnicas colaborativas, <em>el desarrollo de un producto software descentralizado.</em></li>
<li>Ser capaz de <em>evaluar el trabajo realizado</em>, plantear propuestas de mejora y saber realizarlas.</li>
</ul>
</li>
<li><em><strong>Fomentar el desarrollo de productos de software de calidad</strong></em> aplicando el último estándar ISO/IEC 25000 con el objetivo de fomentar una experiencia de usuario óptima en todas las situaciones.</li>
<li><em><strong>Completar el trabajo de fin de ciclo con proyectos reales</strong></em>.</li>
</ul>
</div>
]]></content:encoded>
      <enclosure url="https://i.imgur.com/LukNdoZ.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Mis Lugares app Android en Kotlin</title>
      <link>https://joseluisgs.dev/posts/2021/2021-05-12-mis-lugares-android-kotlin.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-05-12-mis-lugares-android-kotlin.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Mis Lugares app Android en Kotlin</source>
      <description>Un ejemplo de todo lo visto en Programación Multimedia y Dispositivos Móviles de 2.º DAM</description>
      <category>Proyectos</category>
      <category>Docencia</category>
      <pubDate>Wed, 12 May 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><a href="https://github.com/joseluisgs/MisLugares" target="_blank" rel="noopener noreferrer">Mis Lugares</a> es la <em>app</em> que resume todo lo trabajado en Programación Multimedia y Dispositivos Móviles. El objetivo principal es tener una <em>app</em> que cubra la mayoría de la funcionalidad y contenidos que se ven en este módulo de DAM, donde simulamos ser una empresa desarrollando un producto real. A continuación te comento los aspectos más importantes.</p>
<!-- more -->
<h2>Contexto</h2>
<p>Cuando planteé este curso, quise darle un enfoque puramente práctico trabajando con documentación oficial y proyectos reales, usando tecnologías demandadas por las empresas del sector de nuestra zona. Es por ello que decidí darle un giro de tuerca a Mis Lugares 2019, adaptándolo a las circunstancias actuales.</p>
<p>Por un lado, trabajamos en base a un reto/proyecto real. Segundo, se suprimen los apuntes por documentación oficial. Esto al principio choca, pero a la larga se agradece. En la profesión del desarrollo móvil las tecnologías cambian y es bueno adquirir la destreza de cómo moverse por documentaciones oficiales y cómo consultarlas.</p>
<p>Por otro lado, se ha usado GitHub/Git con el objetivo de control de código y diario de clase. El alumnado puede seguir lo hecho en clase, comparar el código, ver las distintas versiones o tecnologías por rama, aportar nuevas ideas, colaborar en grupo para su desarrollo, etc. Moviéndote por las ramas puedes ver las distintas opciones y soluciones presentadas a los problemas y tecnologías vistos en clase.</p>
<p>Finalmente, se han ido aplicando distintas tecnologías con el objetivo de abrir las puertas en el desarrollo móvil al alumnado y con ello sean capaces de dominar los aspectos más destacados de las mismas para después desarrollar sus propias <em>apps</em>.</p>
<div class="hint-container tip">
<p class="hint-container-title">Mis lugares</p>
<ul>
<li>Apuntes.</li>
<li>Ejemplo real.</li>
<li>Código para experimentar.</li>
<li>Aplicación de distintas tecnologías.</li>
<li>Manejo de repositorios.</li>
</ul>
</div>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://ciberninjas.com/wp-content/uploads/2021/02/21-nuevo-curriculum-android.png" alt="Logo"></p>
<h2>Mis Lugares App</h2>
<p>2DAM PMYDM <em>app</em> para poner en práctica todo lo visto en este curso en la primera parte del temario relacionada con programación móvil
en sistemas Android usando el lenguaje Kotlin. Podrás llevar tus momentos favoritos siempre en tu móvil.</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/App-Android-g" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/Code-Kotlin-blue" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/Firebase- Ready-FFA000" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/badge/Lisence-MIT-green" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" src="https://img.shields.io/github/last-commit/joseluisgs/MisLugaresKotlinRealm" alt="Logo">
</p>
<h3>Descripción</h3>
<p>En esta <em>app</em> manejaremos conceptos como diseño de interfaces dinámicas, almacenamiento en teléfono, base de datos y el uso
de sensores y elementos de tu móvil Android como pueden ser la cámara y el GPS, además de actualizaciones en tiempo real de la información usando como lenguaje de referencia Kotlin. La aplicación es de ámbito docente y, obviamente, muchos aspectos son mejorables, pero hay que entender el contexto en el cual se genera y se usa en clase.</p>
<p>El objetivo es poder llevar un registro de tus lugares favoritos siempre en tu móvil. A lo largo del curso avanzaremos y complementaremos el proyecto con distintas tecnologías en sus distintas versiones.</p>
<p>Es importante, antes de abordar esta <em>app</em>, que revises <a href="https://github.com/joseluisgs?tab=repositories" target="_blank" rel="noopener noreferrer">los proyectos realizados en este curso</a>.
En definitiva, este es un proyecto integrador que resume lo trabajado en este módulo.</p>
<h3>Versiones y revisiones</h3>
<p>Las versiones no están pensadas para que el alumnado maneje distintas técnicas. El objetivo es que aprenda a usar la tecnología más idónea dependiendo del problema.</p>
<ul>
<li><strong>v1.0.0</strong>: Versión donde el almacenamiento de todos los datos se hace de manera local usando una base de datos <a href="https://realm.io/" target="_blank" rel="noopener noreferrer">Realm</a>.
Las imágenes se utilizan usando el sistema de codificación en Base64. Esta manera no es muy correcta, pero puede ser útil si
nos enfrentamos a servicios remotos donde desconocemos cómo subir la imagen. Este sistema tiene el problema de que sobrecarga
la base de datos y no es muy recomendado salvo excepciones como esta. Se puede ver una versión alternativa almacenando las imágenes en sistemas de almacenamiento interno y externo.</li>
<li><strong>v2.0.0</strong>: Versión donde el almacenamiento de todos los datos se hace a través de una API REST con el objetivo de salir
al exterior para enviar o recibir información. De nuevo mandaremos las imágenes en Base64, aunque lo ideal sería hacer peticiones
<em>multipart</em>. De nuevo se hace hincapié en el uso docente y este es acercar la tecnología de uso de servicios web y de una API REST
para poder intercambiar información con una <em>app</em> móvil y el exterior (otros servidores y recursos).
Para el desarrollo de la API REST nos basaremos en el proyecto <a href="https://github.com/joseluisgs/APIRESTFake" target="_blank" rel="noopener noreferrer">API REST Fake</a>.</li>
<li><strong>v3.0.0</strong>: Versión donde se integra Firebase como sistema de <em>backend</em> de nuestra aplicación. Con ello nos ofrece poder
realizar un <em>backend</em> compatible con distintos clientes (móviles o web). Para ello haremos uso
de las distintas funcionalidades que nos aporta como <a href="https://firebase.google.com/products/auth?hl=es" target="_blank" rel="noopener noreferrer">Authentication</a>,
<a href="https://firebase.google.com/products/firestore?hl=es" target="_blank" rel="noopener noreferrer">Firestore</a>, <a href="https://firebase.google.com/products/storage?hl=es" target="_blank" rel="noopener noreferrer">Storage</a>,
<a href="https://firebase.google.com/products/cloud-messaging?hl=es" target="_blank" rel="noopener noreferrer">Messaging</a> o <a href="https://firebase.google.com/docs/analytics" target="_blank" rel="noopener noreferrer">Analytics</a>.</li>
</ul>
<h4>Referencias</h4>
<p>Se destacan las siguientes tecnologías usadas en algún momento o versión del proyecto, cuyos enlaces son los mismos que hemos utilizado como apuntes en clase y ejemplos individuales de los mismos están en mi <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">GitHub</a>.</p>
<ul>
<li><a href="https://developer.android.com/docs" target="_blank" rel="noopener noreferrer">Android</a></li>
<li><a href="https://kotlinlang.org/" target="_blank" rel="noopener noreferrer">Kotlin</a></li>
<li><a href="https://developer.android.com/guide/topics/ui" target="_blank" rel="noopener noreferrer">Interfaz de usuario</a></li>
<li><a href="https://developer.android.com/guide/topics/graphics" target="_blank" rel="noopener noreferrer">Imágenes y gráficos</a></li>
<li><a href="https://developer.android.com/guide/topics/media" target="_blank" rel="noopener noreferrer">Audio y vídeo</a></li>
<li><a href="https://realm.io/docs/kotlin/latest/" target="_blank" rel="noopener noreferrer">Bases de Datos Realm.io</a></li>
<li><a href="https://developer.android.com/training/camera" target="_blank" rel="noopener noreferrer">Uso de cámara</a></li>
<li><a href="https://developer.android.com/guide/topics/data" target="_blank" rel="noopener noreferrer">Sistemas de almacenamiento</a></li>
<li><a href="https://developer.android.com/guide/topics/permissions/overview" target="_blank" rel="noopener noreferrer">Manejo de permisos</a></li>
<li><a href="https://developer.android.com/guide/components/intents-common?hl=es" target="_blank" rel="noopener noreferrer">Uso de Intents</a></li>
<li><a href="https://developer.android.com/training/location" target="_blank" rel="noopener noreferrer">Geolocalización</a></li>
<li><a href="https://developers.google.com/maps/documentation/android-sdk/intro" target="_blank" rel="noopener noreferrer">Google Maps</a></li>
<li><a href="https://developer.android.com/training/data-storage/shared-preferences?hl=es" target="_blank" rel="noopener noreferrer">Uso de preferencias</a></li>
<li><a href="https://developer.android.com/guide/background" target="_blank" rel="noopener noreferrer">Tareas en segundo plano</a></li>
<li><a href="https://developer.android.com/training/wearables/apps/voice" target="_blank" rel="noopener noreferrer">Funciones de voz</a></li>
<li><a href="https://developer.android.com/training/animation/overview" target="_blank" rel="noopener noreferrer">Animaciones</a></li>
<li><a href="https://material.io/" target="_blank" rel="noopener noreferrer">Material Design</a></li>
<li><a href="https://github.com/google/gson" target="_blank" rel="noopener noreferrer">JSON con Gson</a></li>
<li><a href="https://github.com/zxing/zxing" target="_blank" rel="noopener noreferrer">Códigos QR</a></li>
<li><a href="https://github.com/Karumi/Dexter" target="_blank" rel="noopener noreferrer">Permisos con Dexter</a></li>
<li><a href="https://square.github.io/picasso/" target="_blank" rel="noopener noreferrer">Imágenes con Picasso</a></li>
<li><a href="https://programacionymas.com/blog/jwt-vs-cookies-y-sesiones" target="_blank" rel="noopener noreferrer">Sesiones y Tokens</a></li>
<li><a href="https://www.bbvaapimarket.com/es/mundo-api/api-rest-que-es-y-cuales-son-sus-ventajas-en-el-desarrollo-de-proyectos/#:~:text=En%20el%20campo%20de%20la,a%20partir%20de%20ese%20software." target="_blank" rel="noopener noreferrer">API REST</a></li>
<li><a href="https://developer.mozilla.org/es/docs/Web/HTTP/Methods" target="_blank" rel="noopener noreferrer">HTTP Métodos de petición</a></li>
<li><a href="https://developer.mozilla.org/es/docs/Web/HTTP/Status" target="_blank" rel="noopener noreferrer">HTTP Código de estado de respuestas</a></li>
<li><a href="https://square.github.io/retrofit/" target="_blank" rel="noopener noreferrer">Retrofit</a></li>
<li><a href="https://firebase.google.com/docs?hl=es" target="_blank" rel="noopener noreferrer">Firebase</a></li>
</ul>
<h3>Uso de Firebase v3.X.X</h3>
<p>En la versión 3.X.X se hace uso de Google Firebase como sistema o conjunto de servicios a usar por nuestro cliente: autenticación,
base de datos en tiempo real, almacenamiento de ficheros y notificaciones.
Es por ello que es fundamental que asegures cómo configurar tanto Firebase como tu proyecto Android para que funcione
perfectamente. Te recomiendo que sigas siempre la guía oficial de <a href="https://firebase.google.com/docs/android/setup?hl=es" target="_blank" rel="noopener noreferrer">Firebase</a>
o <a href="https://www.youtube.com/watch?v=IiuKAmgRYeM&amp;list=PLNdFk2_brsRcaGhfeeiVkW72qTYcn_nfQ" target="_blank" rel="noopener noreferrer">consultes otras alternativas</a>
actualizadas para ello; si no, será imposible que lo integres en tu propio proyecto. Cuidado con el nombre de paquetes.</p>
<h3>Uso del servidor propio para API REST v2.X.X</h3>
<p>Para la versión 2.X.X se hace uso del servidor propio <a href="https://github.com/joseluisgs/APIRESTFake" target="_blank" rel="noopener noreferrer">API REST Fake</a>, con
el objetivo de no complicar el asunto aprendiendo a hacer un servidor propio REST completo. Para ello se ha creado en la carpeta
API REST todo lo necesario para correr ese servidor, usando una base de datos JSON. De esta manera no te tienes que preocupar
de cómo se desarrolla este tipo de servicios, sino solo de consumirlo y usarlo en tu <em>app</em> móvil.</p>
<p>Para ello es fundamental tener instalado en tu sistema operativo <a href="https://nodejs.org/es/" target="_blank" rel="noopener noreferrer">Node.js</a>.
La base de datos está en el directorio <code>bd</code> y se llama <code>db.json</code>, y debe tener una estructura de <em>array</em> de objetos por cada recurso a consumir.
Puedes leer más al respecto en <a href="https://github.com/joseluisgs/APIRESTFake" target="_blank" rel="noopener noreferrer">API REST Fake</a> o consultar los ejemplos.</p>
<p>Esta pequeña aplicación que simula un servidor de API REST está probada en <em>Linux</em> y <em>OS X</em>. Si la vas a usar en <em>Windows</em>, por favor
asegúrate de que los <em>scripts</em> de NPM son compatibles y las rutas de tu sistema.</p>
<h4>Configuración y puesta en marcha</h4>
<p>Para arrancar el servidor debes hacer lo siguiente desde la carpeta apirest:</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$npm</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> install --> Instala las dependencias necesarias para su uso</span></span>
<span class="line"><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">$npm</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> start   --> Inicia el servidor.</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>Si todo ha ido bien obtendrás la siguiente salida:</p>
<div class="language-bash line-numbers-mode" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">⚑</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> Servidor</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> JSON</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> funcionando</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ✓</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> -> </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">http://localhost:6969</span></span>
<span class="line"><span style="--shiki-light:#4078F2;--shiki-dark:#61AFEF">⚑</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> Fake</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> API</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> REST</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> por</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> joseluisgs</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379"> ✓</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> -> </span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">https://github.com/joseluisgs/APIRESTFake</span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><p>Te recomiendo el uso de <a href="https://www.postman.com/" target="_blank" rel="noopener noreferrer">Postman</a> para testear y comprender cómo hacer las llamadas que luego
realizarás en <a href="https://square.github.io/retrofit/" target="_blank" rel="noopener noreferrer">Retrofit</a>.</p>
<h3>Consideraciones para ver los mapas</h3>
<p>Los mapas hacen uso de <em>Google Map Api Key</em>, es por ello que debes activar la clave de la API y activarla para tu proyecto,
pues puede que varíe a la huella del mío, o que simplemente yo haya desactivado la mía (te recuerdo que es un proyecto para fin docente y lo activo y desactivo sobre la marcha).
Por favor, sigue <a href="https://developers.google.com/maps/documentation/android-sdk/get-api-key?hl=es-419" target="_blank" rel="noopener noreferrer">este tutorial</a> para que puedas ver tus mapas con tu clave.</p>
<p>Recuerda cambiar el fichero <em>Manifest</em> y añadir:</p>
<div class="language-xml line-numbers-mode" data-highlighter="shiki" data-ext="xml" style="--shiki-light:#383A42;--shiki-dark:#abb2bf;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#282c34"><pre class="shiki shiki-themes one-light one-dark-pro vp-code"><code class="language-xml"><span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">uses-permission</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> android:name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"android.permission.ACCESS_FINE_LOCATION"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> /></span></span>
<span class="line"><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">&#x3C;</span><span style="--shiki-light:#E45649;--shiki-dark:#E06C75">meta-data</span><span style="--shiki-light:#986801;--shiki-dark:#D19A66"> android:name</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"com.google.android.geo.API_KEY"</span></span>
<span class="line"><span style="--shiki-light:#986801;--shiki-dark:#D19A66">android:value</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF">=</span><span style="--shiki-light:#50A14F;--shiki-dark:#98C379">"@string/google_maps_key"</span><span style="--shiki-light:#383A42;--shiki-dark:#ABB2BF"> /></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Te recomiendo que mires la pestaña <em>Run</em>, pues si falla el mapa te dirá si no se ha podido identificar correctamente con la clave de tu API generada. En el modo <em>debug</em> se hace en ese fichero con la huella SHA-1 y se pone; en el modo <em>release</em>
se debe generar con <em>keytool</em> la huella SHA-1 con los datos del paquete <em>release</em>, crear un proyecto y subirla a
<a href="https://developers.google.com/maps/documentation/android-sdk/get-api-key" target="_blank" rel="noopener noreferrer">https://developers.google.com/maps/documentation/android-sdk/get-api-key</a>.</p>
<h3>Capturas</h3>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/BIhr1xY.jpg" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/PrZxEZw.jpg" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/ZWrNzlM.jpg" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/h4iZgY2.jpg" alt="Logo">
</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/d3AOKdM.jpg" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/9fIDBM8.jpg" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/Kd4h7aL.jpg" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="325" src="https://i.imgur.com/va9mYna.jpg" alt="Logo">
</p>
<h4>Herramientas usadas</h4>
<p>Estas son las herramientas que más hemos usado en clase para la realización de este proyecto:</p>
<p style="text-align:center;">
<img loading="lazy" style="border-radius: 0.25rem;" height="45" src="https://logodownload.org/wp-content/uploads/2015/05/android-logo-7-1.png" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="45" src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/74/Kotlin_Icon.png/600px-Kotlin_Icon.png" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="45" src="https://firebase.google.com/downloads/brand-guidelines/PNG/logo-logomark.png?hl=es-419" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="45" src="https://resources.jetbrains.com/storage/products/intellij-idea/img/meta/intellij-idea_logo_300x300.png" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="45" src="https://miro.medium.com/max/650/1*zzvdRmHGGXONZpuQ2FeqsQ.png" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="45" src="https://cdn.iconscout.com/icon/free/png-256/github-153-675523.png" alt="Logo">
<img loading="lazy" style="border-radius: 0.25rem;" height="45" src="https://user-images.githubusercontent.com/17736615/30980083-f7f8a860-a43c-11e7-939e-f6717a2210fe.png" alt="Logo">
</p>
<h3>Licencia</h3>
<p>Este proyecto está licenciado bajo licencia <strong>MIT</strong>; si deseas saber más, visita el fichero
<a href="https://github.com/joseluisgs/MisLugaresKotlinRealm/blob/master/LICENSE" target="_blank" rel="noopener noreferrer">LICENSE</a> para su uso docente y educativo.</p>
]]></content:encoded>
      <enclosure url="https://3.bp.blogspot.com/-E_61ZBmBlwo/XaeWvxlFvWI/AAAAAAAAMqQ/7Zc_eiWh25kznrDFwwpEvHvRHSby-P2XwCLcBGAsYHQ/s1600/Introducing_LTS_Android_NDK_r21.png" type="image/png"/>
    </item>
    <item>
      <title>GitHub, mi herramienta imprescindible</title>
      <link>https://joseluisgs.dev/posts/2021/2021-05-11-github-imprescindible.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-05-11-github-imprescindible.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">GitHub, mi herramienta imprescindible</source>
      <description>Te cuento cómo Octocat es mi gran amigo y aliado dentro y fuera de las aulas</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Tue, 11 May 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Sí, soy un enamorado de las tecnologías de mi amigo <em>Octocat</em>. De hecho, se han convertido en estos dos últimos cursos en mi pilar central en el que desarrollo mi docencia y donde concentro <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">apuntes y prácticas</a> y proyectos personales. Me ofrece tantas ventajas, que voy a iniciar mi especial sobre tecnologías favoritas explicando cómo uso GitHub en mi día a día.</p>
<!-- more -->
<h2>Octocat y yo</h2>
<p>Me encanta empezar por GitHub. Es una herramienta tan importante, que nos aporta tanto, que se merece estar en el lugar que le corresponde.</p>
<p>Usar un sistema de control de versiones tiene muchas ventajas. No voy a entrar a explicarlo con mucho detalle, pero si eso lo unimos con las ventajas que nos ofrece GitHub como herramienta de apoyo docente, la cosa puede ser espectacular. Desde hace dos cursos uso Git y GitHub como pilar en todos mis módulos. Primero, porque lo considero imprescindible para impartir módulos y cursos de desarrollo de <em>software</em>. Segundo, porque me han tocado módulos donde entra en el temario. Tercero, y más importante, porque no imagino salir al mercado laboral sin un mínimo de soltura manejándolo.</p>
<p>GitHub me ofrece tantas ventajas que poco a poco las iré enumerando y confirmaréis cómo <em>Octocat</em> es uno más en mis clases y, muchas veces, mucho más importante que el propio profesor 🤨. ¿Despegamos?</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://i.pinimg.com/originals/ce/c6/0c/cec60c601c83652dd79980b561e65fdf.jpg" alt="Logo"></p>
<h2>GitHub en clase</h2>
<p>Comencemos con el propio objetivo de usar Git y GitHub: como sabemos, es un sistema de control de versiones con la posibilidad de usarlo remotamente. De esta manera mi código siempre está disponible para todo el mundo, y puedo tener una copia de seguridad de las prácticas, apuntes, etc.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://freeforstudents.org/img/cards/github-banner.png" alt="Logo"></p>
<p>En mi día a día como profesor y formador, aparte de desarrollador, es importante que cuando estamos enseñando o practicando con un código llevemos un diario y registro de cambios que facilite al alumnado seguir lo que se ha hecho en la clase (y a los inspectores/as de turno ver que trabajas...). De esta manera lo conseguimos de forma muy fácil. Cada <em><strong>commit</strong></em> es un momento clave en clase. De esta manera podemos seguir los distintos puntos de la clase, autocompletando y documentando lo que sucede en ella. Cada <em><strong>rama</strong></em> nos ofrece diversas alternativas de soluciones o de tecnologías aplicadas, incluso mejoras o cuestiones propuestas del alumnado. Todo lo hecho en el aula siempre queda patente para que experimenten sobre ello.</p>
<p>Además, el código siempre está disponible para el alumnado en todo momento: pueden hacer un <em><strong>fork</strong></em>, experimentar, variarlo, ver el seguimiento, compararlo, etc. Nos permite que tanto alumnado como profesorado trabajemos conjuntamente y colaboremos. Pueden hacerme <em><strong>pull request</strong></em> para corregir errores, propuestas de mejoras o completar código entre varias personas. Incluso lo he usado en exámenes: les das la estructura de código o plantilla que deben completar, subir y usar como base de nuestro sistema de desarrollo y despliegue continuo... ¿Qué más puedes pedir? ✌</p>
<div class="hint-container tip">
<p class="hint-container-title">Ventajas</p>
<ul>
<li>Diario de clase.</li>
<li>Aportar soluciones alternativas.</li>
<li>Colaborar en desarrollo entre alumnado.</li>
<li>Compartir tus desarrollos.</li>
<li>Plantillas de exámenes.</li>
<li>CI/CD en el aula.
:::</li>
</ul>
<h3>GitHub y COVID</h3>
<p>Si GitHub es útil en mi día a día, más lo ha sido en tiempos de COVID. Y por este motivo lo destaco un poco más. He vivido todo tipo de modalidades docentes en menos de un año. Confinamiento, telemática, presencial, semipresencial asíncrona, síncrona, con unos en un lado y otros en otro lado... A veces no sabíamos ni dónde, ni cómo 🤯. Lo que sí sabíamos es que estaba el <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">repositorio</a> para salvarnos, para seguir las clases y &quot;navegar&quot; a través de ellas. Ha venido a complementar la labor que hemos intentado hacer en nuestro día a día.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://hostgator.mx/blog/wp-content/uploads/2020/07/07-julho-github.png" alt="Logo"></p>
<h2>GitHub educativo</h2>
<p>Desde que el año pasado, como jefe de departamento, iniciamos el proceso para formalizar nuestros acuerdos con GitHub, hemos ido trabajando todas las ventajas que nos ofrece a nivel de centro para incorporarlas en el aula. Finalmente somos centro asociado de referencia, primero de Castilla-La Mancha, y con ello GitHub tiene mucho protagonismo en nuestros módulos.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://scontent-mad1-1.xx.fbcdn.net/v/t1.6435-0/p320x320/37719204_1032949566864994_5259837527317020672_n.png?_nc_cat=108&ccb=1-3&_nc_sid=e3f864&_nc_ohc=8JHthO64zLoAX-wpflD&_nc_oc=AQnxsi041y2XxmZDkm5_6RQzGwuirKUF5vOPiM4vhBBRzFCvtLPb6CfjCA9lzu1HckY&_nc_ht=scontent-mad1-1.xx&tp=30&oh=0558fc54302f86840c776f5ad035e6db&oe=60BF929A" alt="Educación"></p>
<p>Como he dicho, GitHub tiene un sensacional y potente programa educativo para centros, profesorado y alumnado con muchas ventajas. Te comento:</p>
<div class="hint-container tip">
<p class="hint-container-title">GitHub Educación</p>
<ul>
<li>Como <a href="https://education.github.com/schools" target="_blank" rel="noopener noreferrer">centro o campus asociado</a>, podemos tener disponibles aulas virtuales para compartir código, realizar test, servidor, etc.</li>
<li>Como docentes, tenemos una <a href="https://education.github.com/toolbox" target="_blank" rel="noopener noreferrer"><em>Toolbox</em></a> increíble que nos da acceso a poder usar distintos programas con licencias especiales que son muy útiles para nuestra labor.</li>
<li>Como estudiantes, tienes acceso al <a href="https://education.github.com/pack" target="_blank" rel="noopener noreferrer"><em>Student Developer Pack</em></a> con muchas licencias y programas muy punteros para completar tu formación o usarlos en tus prácticas.</li>
<li>Comunidad para intercambiar opiniones e ideas con el resto del mundo.</li>
<li>Recursos para clase: material formativo, chuletas, etc.
:::</li>
</ul>
<h2>Tutorial Git/GitHub</h2>
<p>Pero está claro que esto no surge de la noche a la mañana, es por ello que lo primero que hay que hacer al comenzar un nuevo curso es el tutorial de <a href="https://github.com/joseluisgs/git-tutorial" target="_blank" rel="noopener noreferrer">Git/GitHub</a> para el alumnado. Para ello dedicamos un tiempo importante en dominarlo. Es una inversión asegurada: todo lo que obtenemos son ventajas para el resto del curso. Nuestra idea es que esté tan automatizado como la imagen que tenemos en la puerta del aula.</p>
<p style="text-align:center;"><img loading="lazy" style="border-radius: 0.25rem;" src="https://miro.medium.com/max/2800/0*3iJLQaoQI66YJuQk.jpg" alt="Educación"></p>
<div class="hint-container tip">
<p class="hint-container-title">Tutorial</p>
<p>Este es nuestro tutorial de <a href="https://github.com/joseluisgs/git-tutorial" target="_blank" rel="noopener noreferrer">Git/GitHub</a>, colabora y aporta en lo que quieras 😉.</p>
</div>
</div>
</div>
<h2>Octocat siempre conmigo</h2>
<p>Espero haberte convencido un poco con todo esto. Las posibilidades son infinitas. <em>Octocat</em> es mi gran amigo y aliado dentro y fuera de las aulas. A nivel personal, lo he usado como base para todo lo que hago, incluso para el despliegue automatizado de mi propia web como puedes ver en su dominio.</p>
<p>Lo que más valoro es su sencillez y, a la vez, potencia 💪. Sé que puede haber distintas alternativas, pero nos ofrece mucho gracias a sus licencias educativas, lo que lo hace ideal no solo por usar GitHub, sino por todo el <em>software</em> al que te abre las puertas.</p>
<p>Si algo tengo claro es que mucho tienen que cambiar las cosas para que deje de usarlo. Además, como futurible &quot;<em>Advisor</em>&quot;, siempre es un placer ayudar a su integración en el aula. Si tienes alguna duda o quieres que te ayude en algo, solo tienes que contactar conmigo.</p>
<p>¡¡Muchas gracias, <em>Octocat</em>!!</p>
<p style="text-align:center;">
  <img loading="lazy" style="border-radius: 0.25rem;" src="https://www.wallpapertip.com/wmimgs/250-2508431_github-wallpaper.jpg" alt="Educación">
</p>
<div class="hint-container tip">
<p class="hint-container-title">Repositorio</p>
<p>Puedes pasarte por: <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">https://github.com/joseluisgs</a>. ¡Colabora y usa lo que quieras! 😉</p>
</div>
]]></content:encoded>
      <enclosure url="https://res.cloudinary.com/practicaldev/image/fetch/s--Nrzp31zz--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/i/kml9j34p9taplrnqtcez.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>Estudiar Informática en CIFP Virgen de Gracia</title>
      <link>https://joseluisgs.dev/posts/2021/2021-05-01-estdiar-informatica-cifp-virgen.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-05-01-estdiar-informatica-cifp-virgen.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Estudiar Informática en CIFP Virgen de Gracia</source>
      <description>¿Por qué deberías estudiar en CIFP Virgen de Gracia Informática?</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Sat, 01 May 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>En esta entrada te voy a intentar explicar y, si puedo, convencerte de por qué estudiar algunos de nuestros ciclos en nuestro centro es una opción más que recomendable. No sé si lo conseguiré, pero por intentarlo que no quede. Y ya de paso te presento la <a href="https://informaticacifpvg.netlify.app/" target="_blank" rel="noopener noreferrer">web del departamento</a>. Aún no está terminada, pero poco a poco tendrás en ella todo lo que necesitas para conocernos.</p>
<!-- more -->
<h2>¿Por qué?</h2>
<p>Esta sencilla pregunta tiene una sencilla respuesta 🔎. A nivel formativo somos Centro Integrado y de Referencia. Esto quiere decir que intentamos siempre estar a la vanguardia tecnológica y preparar a nuestro alumnado para lo que realmente necesitan las empresas. Trabajamos conjuntamente con ellas en distintos planes formativos y con ello conseguimos que el resultado de empleabilidad de nuestro alumnado sea realmente total. Te lo puedo resumir en:</p>
<ul>
<li>Porque es el trabajo del futuro, ya que en todas las empresas hay sistemas informáticos.</li>
<li>Porque realizarás prácticas en las mejores empresas de la zona, con grandes posibilidades de incorporarte a las mismas una vez terminada la fase de prácticas.</li>
<li>Porque si optas por la FP Dual, podrás completar tu formación en una empresa de tecnología puntera desde el primer curso.</li>
<li>Porque con el proyecto Erasmus, puedes realizar el periodo de prácticas en la ciudad europea de tu elección.</li>
</ul>
<h2>Titulaciones</h2>
<p>Puedes leer sobre ellas <a href="https://informaticacifpvg.netlify.app/fp_reglada/" target="_blank" rel="noopener noreferrer">aquí</a>. De hecho somos de los primeros siempre en implantar nuevas titulaciones debido a ser centro de referencia. Tenemos los nuevos cursos de especialización esperándote 👨–🎓. Nuestra oferta es:</p>
<ul>
<li><a href="https://informaticacifpvg.netlify.app/fp_reglada/smr/" target="_blank" rel="noopener noreferrer">Técnico en Sistemas Microinformáticos y Redes</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/fp_reglada/asir/" target="_blank" rel="noopener noreferrer">Técnico Superior en Administración de Sistemas Informáticos en Red</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/fp_reglada/daw/" target="_blank" rel="noopener noreferrer">Técnico Superior en Desarrollo de Aplicaciones Web</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/fp_reglada/dam/" target="_blank" rel="noopener noreferrer">Técnico Superior en Desarrollo de Aplicaciones Multiplataforma</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/fp_reglada/asir-e/" target="_blank" rel="noopener noreferrer">Técnico Superior en Administración de Sistemas Informáticos en Red (e-Learning)</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/fp_especializacion/ciberseguridad/" target="_blank" rel="noopener noreferrer">Curso de Especialización en Ciberseguridad en Entornos de Tecnologías de Información</a>.</li>
</ul>
<h2>Proyectos reales</h2>
<p>Es nuestra filosofía ayudarte a que encuentres tu camino como desarrollador o informático y que este sea según lo demandado en la actualidad. No es un camino fácil. Pero queremos que seas el protagonista de tu aprendizaje. Es por ello que siempre realizarás proyectos reales, que podrías hacer en tu día a día. De hecho, muchos de tus proyectos tendrán un fin real y se usarán por otras empresas, centros u ONG. De hecho, nos sentimos muy orgullosos del trabajo de nuestro alumnado. Puedes conocer algunos proyectos de nuestro alumnado 💻:</p>
<ul>
<li><a href="https://informaticacifpvg.netlify.app/proyectos/dam2/" target="_blank" rel="noopener noreferrer">Proyectos de DAM</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/proyectos/daw2/" target="_blank" rel="noopener noreferrer">Proyectos de DAW</a>.</li>
</ul>
<h2>Innovando</h2>
<p>Siempre estamos innovando o inventando algo para no aburrirnos. De hecho, ya hemos recibido distintos premios por nuestros proyectos de innovación. ¿Quieres conocerlos?</p>
<ul>
<li><a href="https://informaticacifpvg.netlify.app/proyectos/innovacion_coworking/" target="_blank" rel="noopener noreferrer">Agile Development &amp; DevOps</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/proyectos/innovacion_comunica/" target="_blank" rel="noopener noreferrer">Comunica</a>.</li>
</ul>
<p>Además, tenemos distintos grupos de trabajo donde realizamos distintos proyectos, aplicamos nuevas tecnologías y echamos un rato:</p>
<ul>
<li><a href="https://informaticacifpvg.netlify.app/proyectos/grupo_trabajo_js_2021/" target="_blank" rel="noopener noreferrer">Desarrollo y Despliegue de Apps Multiplataforma basadas en JS</a>.</li>
<li><a href="https://informaticacifpvg.netlify.app/proyectos/grupo_trabajo_ansible_2021/" target="_blank" rel="noopener noreferrer">Ansible</a>.</li>
</ul>
<p>Además, como departamento o individualmente realizamos <a href="https://informaticacifpvg.netlify.app/proyectos/departamento/" target="_blank" rel="noopener noreferrer">otros proyectos</a> para usarlos como ejemplo o a nivel docente.</p>
<h2>Tecnologías</h2>
<p>Siempre intentamos trabajar con las <a href="https://informaticacifpvg.netlify.app/tecnologias/" target="_blank" rel="noopener noreferrer">tecnologías</a>, lenguajes o sistemas más demandados. De hecho, tenemos distintos acuerdos para que se beneficie nuestro alumnado. Somos centro referencia y Campus de GitHub, Kotlin o JetBrains. Trabajamos con Navicat, DBeaver y muchos más.</p>
<p style="text-align:center;">
   <img loading="lazy" src="https://elevatecnologia.com/wp-content/uploads/2020/12/Las-10-principales-empresas-de-ingenieria-de-software-del-mundo.png" height="500">
 </p>
<div class="hint-container tip">
<p class="hint-container-title">Consejos</p>
<p>La página no está aún del todo terminada. Te invito a visitarla de vez en cuando 😉.</p>
</div>
]]></content:encoded>
      <enclosure url="https://www.mexicocss.com/storage/app/uploads/public/5d6/6c1/719/5d66c17196f37063403826.png" type="image/png"/>
    </item>
    <item>
      <title>Bonito detalle de JetBrains y Kotlin</title>
      <link>https://joseluisgs.dev/posts/2021/2021-02-10-detalle-de-Kotlin.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-02-10-detalle-de-Kotlin.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Bonito detalle de JetBrains y Kotlin</source>
      <description>JetBrains y Kotlin han tenido un detalle conmigo como profe</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Wed, 10 Feb 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Hace ya unos días que recibí un pequeño pero valioso detalle por parte de JetBrains y el equipo de Kotlin por mi labor docente este año aplicando sus tecnologías en el desarrollo móvil. En esta entrada te comento detalles al respecto 🙂.</p>
<!-- more -->
<h2>Contexto</h2>
<p>El año pasado decidí intentar acercarme aún más a las distintas tecnologías existentes en el desarrollo móvil en uno de los módulos que imparto: <a href="https://www.todofp.es/que-como-y-donde-estudiar/que-estudiar/familia/loe/informatica-comunicaciones/des-aplicaciones-multiplataforma.html" target="_blank" rel="noopener noreferrer">Programación Multimedia y Dispositivos Móviles de 2.º de DAM</a> 💻.</p>
<p>Desde que imparto dicho módulo, siempre aplico un aprendizaje basado en retos y proyectos, donde antes que memorizar es importante aprender sobre dicho campo en base a realizar distintos proyectos donde se apliquen tecnologías demandadas por el sector y, con ellas, se desarrollen las distintas competencias que nos llevan al éxito al desarrollarlo: desde la investigación y documentación, planificación, diseño, implementación y despliegue. El objetivo es simular pequeños equipos de desarrollo a los que les entra un proyecto y deben adaptarse al tiempo de entrega del mismo aprendiendo y dominando las tecnologías que subyacen en él.</p>
<p>Pero esto supone un poco de esfuerzo por nuestra parte como docentes, pero a veces se ve recompensado por compañías como <a href="https://www.jetbrains.com/es-es/" target="_blank" rel="noopener noreferrer">JetBrains</a> y su apuesta por la educación. Desde el año pasado, JetBrains colabora con mi instituto, el <a href="https://cifpvirgendegracia.com/" target="_blank" rel="noopener noreferrer">CIFP Virgen de Gracia</a>, ayudando con sus herramientas y entornos para poder impartir los distintos módulos, lo cual lo hace todo más <em>easy</em> ✌.</p>
<h2>Reconocimiento</h2>
<p>El año pasado, durante la cuarentena, decidí ampliar un poco el campo y las tecnologías usadas en el módulo de móviles y también que mi centro fuese reconocido por el equipo de <a href="https://kotlinlang.org/" target="_blank" rel="noopener noreferrer">Kotlin</a> como centro educativo de referencia para el aprendizaje de este lenguaje. Así que, tras algunos intercambios de información, el desarrollo de algunos proyectos y su aplicación educativa, hemos sido finalmente reconocidos, siendo el primer instituto a día de hoy de España en ello (hay otras universidades). Esto se une ya al reconocimiento como centro de referencia de otras compañías tecnológicas, como <a href="https://github.com/" target="_blank" rel="noopener noreferrer">GitHub</a> (hablaré más adelante), que han hecho a este centro gracias al esfuerzo y trabajo realizado por todo el departamento de Informática y sus miembros.</p>
<h2>Los proyectos</h2>
<p>Aunque los detallaré poco a poco en esta web, todos se encuentran en mi <a href="https://github.com/joseluisgs?tab=repositories" target="_blank" rel="noopener noreferrer">GitHub</a>. Pero voy a destacar uno de ellos: <a href="https://github.com/joseluisgs/MisLugares" target="_blank" rel="noopener noreferrer">Mis Lugares</a>.</p>
<h3>Mis Lugares</h3>
<p>Es un proyecto de desarrollo incremental, y en su <a href="https://github.com/joseluisgs/MisLugares/blob/master/README.md" target="_blank" rel="noopener noreferrer"><em>README</em></a> se pueden ver todas las tecnologías usadas, que van desde <em>Material Design</em>, <em>Firebase</em>, tiempo real, API REST, control por voz, bases de datos, mapas, geolocalización, cámara, ficheros, animaciones, etc. El objetivo, como ya he comentado, es aprender en base a proyectos reales, usando las documentaciones y recursos oficiales y simulando que nos lo han encargado como empresa. Toda la información está disponible <a href="https://github.com/joseluisgs/MisLugares" target="_blank" rel="noopener noreferrer">aquí</a>.</p>
<h3>Capturas</h3>
<p style="text-align:center;">
   <img loading="lazy" src="https://i.imgur.com/BIhr1xY.jpg" height="325">
   <img loading="lazy" src="https://i.imgur.com/PrZxEZw.jpg" height="325">
    <img loading="lazy" src="https://i.imgur.com/ZWrNzlM.jpg" height="325">
  <img src="https://i.imgur.com/h4iZgY2.jpg" height="325">
 </p>
 <p style="text-align:center;">
     <img loading="lazy" src="https://i.imgur.com/d3AOKdM.jpg" height="325">
      <img loading="lazy" src="https://i.imgur.com/9fIDBM8.jpg" height="325">
       <img loading="lazy" src="https://i.imgur.com/Kd4h7aL.jpg" height="325">
    <img loading="lazy" src="https://i.imgur.com/va9mYna.jpg" height="325">
 </p>
<h2>El detalle</h2>
<p>Un pequeño, pero a la vez bonito y valioso: unas camisetas, pegatinas, imanes, etc. La verdad lo que más ilusión me ha hecho es saber que en tiempos complicados se ayuda y se apoya a la educación. No han sido meses fáciles: COVID, administración que nos inunda de papeles, etc., por lo que es importante que algunas iniciativas sean bien vistas y si encima se apoya y se da un detalle por ellas, mucho mejor. No por mí, sino por el alumnado: ellos son los que a la larga se beneficiarán por todo esto 👨‍🎓.</p>
<p style="text-align:center;"><img loading="lazy" src="https://pbs.twimg.com/media/Et202MNWYAMD8qJ?format=jpg" alt="detalle"></p>
<div class="hint-container tip">
<p class="hint-container-title">Consejos</p>
<p>Gracias JetBrains y Kotlin, y sobre todo por el apoyo recibido 😊</p>
</div>
]]></content:encoded>
      <enclosure url="https://content.techgig.com/photo/82324241/5-reasons-why-you-should-learn-kotlin-in-2021.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>OpenWebinars</title>
      <link>https://joseluisgs.dev/posts/2021/2021-02-04-openwebinars.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-02-04-openwebinars.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">OpenWebinars</source>
      <description>Las ventajas de usar OpenWebinars en el aula</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Thu, 04 Feb 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Quiero seguir haciendo un repaso de las herramientas o entidades que colaboran con el departamento y nos ayudan en la formación del alumnado. Hoy quiero dar mi opinión de <a href="https://openwebinars.net/" target="_blank" rel="noopener noreferrer">OpenWebinars</a>.</p>
<!-- more -->
<h2>¿Qué me aporta?</h2>
<p><a href="https://openwebinars.net/" target="_blank" rel="noopener noreferrer">OpenWebinars</a> es una plataforma de formación en TIC, ya sea en desarrollo de <em>software</em> y/o sistemas para profesionales IT. Ofrece cursos específicos, talleres, carreras (conjunto de cursos para un determinado perfil, etc.). Además, puedes contratar planes mensuales y anuales que incluyen la tutorización continua y poder visualizar las clases todas las veces que quiera el usuario.</p>
<p>Una de las ventajas importantes de OpenWebinars es su apuesta por el ámbito educativo gracias a las <a href="https://recursos.openwebinars.net/becasow/" target="_blank" rel="noopener noreferrer">BecasOW</a>, un programa de formación gratuita dirigida a profesores y alumnos de Ciclos Formativos de Grado Superior para ampliar sus conocimientos en programación, desarrollo, administración de sistemas, ciberseguridad y <em>big data</em>, algunos de los perfiles más demandados en el mercado laboral.</p>
<p>Es aquí donde muestro mi agradecimiento por su apuesta y por los materiales y cursos que ofrecen. Primero, porque sirven de complemento a lo que vemos en clase o para que el alumnado conozca nuevas tecnologías y/o pueda formarse en ellas fuera de las aulas, lo que amplía el proceso de enseñanza-aprendizaje a mucho más allá de las aulas. Con ello se consigue que el alumnado tenga unos conocimientos mayores o pueda adquirirlos con distintos puntos de vista, lo cual enriquece mucho más de lo que pensamos. Por otro lado, otorga diplomas una vez pasas los cuestionarios y pruebas de cada curso.</p>
<p>Quiero destacar que su labor ha sido genial en tiempos de COVID porque incluso las becas las abrieron a alumnado de primer curso, lo cual supuso poder utilizar estos materiales en tiempos complicados para el alumnado y profesorado.</p>
<p style="text-align:center;"><img loading="lazy" src="https://sasr.es/wp-content/uploads/2019/05/BecasOW-300x174.png" alt="Logo"></p>
<p>Pero no es solo un recurso para el alumnado, sino que sirve al profesorado para aprender nuevas tecnologías, actualizarse o simplemente ampliar sus horizontes profesionales o particulares.</p>
<h2>¿Cómo lo utilizo?</h2>
<p>Generalmente lo uso de esta manera. A lo largo del curso les recomiendo una serie de cursos a realizar o donde pueden ver, profundizar o simplemente analizar desde otro punto de vista lo que vemos en clase. Para motivarlos, si me presentan los diplomas asociados, les premio con mejoras en la calificación.</p>
<p>Para proyectos de fin de grado/ciclo, recomiendo tecnologías emergentes con el objetivo de poder apostar fuerte de cara a una oportunidad laboral. Hablé de ello en <a href="https://joseluisgs.github.io/blog/2021-01-26-importancia-proyecto-final.html" target="_blank" rel="noopener noreferrer">esta entrada</a>. De esta manera, con estos cursos/talleres, el alumnado mientras trabaja en la FCT aprende nuevas tendencias tecnológicas que pueden aplicar en el proyecto. Por supuesto, intento resolverles las dudas en todo momento.</p>
<p>Soy muy partidario de tener distintos puntos de vista, y también reconozco que algunos cursos son mejorables. Pero no puedo dejar de felicitar y agradecer que hayan hecho este esfuerzo y apuesta por la educación, que siempre está tan necesitada de medios.</p>
<p>Finalmente, me ha permitido saciar el gusanillo de algunas tecnologías y temas para consultarlos o iniciarme o profundizar en ellos.</p>
<p>Si no conoces las <a href="https://recursos.openwebinars.net/becasow/" target="_blank" rel="noopener noreferrer">BecasOW</a>, solícitalas para tu centro: seguro que te dan un valor añadido en tu proceso de enseñanza/aprendizaje.</p>
<div class="hint-container tip">
<p class="hint-container-title">Consejos</p>
<p>Puedes consultar mi <a href="https://openwebinars.net/@gvq25aDx/" target="_blank" rel="noopener noreferrer">perfil de cursos y carreras</a> realizados en esta plataforma a día de hoy.</p>
</div>
]]></content:encoded>
      <enclosure url="https://iescomercio.com/informatica/wp-content/uploads/2020/06/logo-openwebinars-colabora.png" type="image/png"/>
    </item>
    <item>
      <title>Google for Education</title>
      <link>https://joseluisgs.dev/posts/2021/2021-02-01-google-education.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-02-01-google-education.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">Google for Education</source>
      <description>Mi experiencia con Google Education</description>
      <category>Blog</category>
      <category>Docencia</category>
      <pubDate>Mon, 01 Feb 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Como adelanté en mi <a href="https://twitter.com/joseluisgonsan/status/1355951733944557574" target="_blank" rel="noopener noreferrer">Twitter</a>, estos días voy a comenzar una serie de publicaciones sobre distintas tecnologías que uso en clase y que han confiado en nosotros para ayudarnos en esta labor y cómo las aplico en el día a día.</p>
<!-- more -->
<h2>¿Qué me aporta?</h2>
<p><a href="https://edu.google.com/intl/es-419_ALL/" target="_blank" rel="noopener noreferrer">Google for Education</a> es una serie de herramientas que <a href="https://edu.google.com/intl/es-419/products/gsuite-for-education/" target="_blank" rel="noopener noreferrer">Google</a> ofrece a centros educativos de manera gratuita que nos permite de manera sencilla mejorar nuestro proceso de enseñanza-aprendizaje.</p>
<p>Desde mi punto de vista, fue un gran acierto solicitarla para nuestro departamento y, por supuesto, para nuestro centro. Gracias a su agilidad y facilidad de configuración, hemos trabajado con ella en los últimos (casi) tres años. Y, por suerte, nos ha venido realmente bien en estos tiempos tan complicados para tener una plataforma sólida con la que trabajar.</p>
<p>Sobre todo, en mi caso, quiero destacar el uso de <em>Classroom</em> para llevar distintas aulas virtuales, unido a un <em>Drive</em> ilimitado y a un GMail que, seamos francos, es el mejor sistema de correo. Todas las herramientas integradas en la organización. Además, me parecen simples y cómodas las herramientas para móviles y <em>tablets</em> que me permiten poder corregir o hacer lo que sea en cualquier lado. Todo ello ha permitido que mis proyectos o las entregas del alumnado las pueda trabajar muy efectivamente, así como los nuevos sistemas para evaluar o calificar que se adaptan muy bien a las directrices de evaluación que sigo 🙋.</p>
<p>Como administrador, también os digo que es muy fácil de configurar, de hacer grupos, equipos, listas, etc. Además de tener un soporte estupendo.</p>
<p>Sé que no a todos/as os convencerá, pero agradezco que esté disponible y de manera gratis. Ya cada cual que elija lo que considere para su día a día. <a href="https://www.youtube.com/watch?v=JQ94DMXPKf0" target="_blank" rel="noopener noreferrer">Aquí</a> os dejo algunas ideas para profesores/as.</p>
<h2>Herramientas incluidas</h2>
<p>Estas son algunas de las herramientas que destaco:</p>
<ul>
<li>GMail: <em>Google Education</em> permite la personalización de las cuentas de correo electrónico, personalizadas de la manera nombre@tudominio.com.</li>
<li><em>Classroom</em>: aula virtual que permite el trabajo colaborativo. Se puede compartir material, realizar pruebas, preguntar dudas…</li>
<li>Calendario: nos permite planificarnos de manera eficaz, de forma que podamos recibir recordatorios de tareas, programar reuniones, etc.</li>
<li><em>Google Drive</em>: permite almacenar y trabajar colaborativamente o compartir documentos, ya que es posible hacerlo desde diferentes lugares, en el mismo documento y en tiempo real, ya sea en <em>Excel</em>, <em>Word</em>, <em>PowerPoint</em> o cualquier otro.</li>
<li><em>Meet</em>: permite la comunicación entre los alumnos y profesores en tiempo real, compartir escritorio, ficheros, grabar las clases, etc.</li>
<li><em>Forms</em>: con los formularios de <em>Google</em> podremos realizar exámenes que se corrijan instantáneamente.</li>
</ul>
<p>Hay muchas más, pero esto es solo una introducción y mi punto de vista al respecto.</p>
<p>Continuaré con más los próximos días.</p>
]]></content:encoded>
      <enclosure url="https://i2.wp.com/carmensallessantafe.es/wp-content/uploads/2019/12/978B9053-E936-461B-B048-47FEFCC0842E.png" type="image/png"/>
    </item>
    <item>
      <title>La importancia de tu proyecto final</title>
      <link>https://joseluisgs.dev/posts/2021/2021-01-26-importancia-proyecto-final.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-01-26-importancia-proyecto-final.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">La importancia de tu proyecto final</source>
      <description>Tu proyecto FCT o de fin de grado es más importante de lo que piensas</description>
      <category>Blog</category>
      <category>Proyectos</category>
      <pubDate>Tue, 26 Jan 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Esta semana han expuesto parte de mi alumnado de 2.º DAM sus proyectos finales. En esta entrada quiero mostrar mis reflexiones de la importancia de este proyecto para proyectarte profesionalmente.</p>
<!-- more -->
<p>Independientemente de lo que estudies, tu proyecto final es parte de tu carta de presentación para tu primera oportunidad profesional. Esto es lo que le repito a mi alumnado todos los días cuando se enfrenta a ellos.</p>
<p>El proyecto no solo sirve para poner en práctica todo lo que has aprendido en tus años de formación. Sirve, además, para estudiar o completar esa formación con temas o tecnologías de interés que te puedan interesar y que estén valoradas en el ámbito profesional. Por lo tanto, mi primer consejo es:</p>
<div class="hint-container tip">
<p class="hint-container-title">Consejos</p>
<p>Si puedes ir más allá y completar/ampliar tu formación, hazlo: es invertir en tu futuro.</p>
</div>
<p>El siguiente paso es abrir tu proyecto al resto de personas, dale visibilidad. Es importante que lo compartas en algún repositorio. De hecho, si tu proyecto se basa o coge otras partes de otros proyectos, no olvides indicarlo. No tengas miedo, estás aprendiendo, nadie te va a juzgar por tu código, pero sí puedes recibir consejos interesantes y, además, mostrar tu progreso día a día. Además, hoy en día hay mucha caza de talentos o estudios de repositorios. Segundo consejo:</p>
<div class="hint-container tip">
<p class="hint-container-title">Consejos</p>
<p>Date visibilidad, muestra tus desarrollos.</p>
</div>
<p>Ahora vamos a lo importante: ¿por qué es una buena baza para buscar empleo? Bueno, siempre lo digo. Tú puedes poner muchas tecnologías en tu CV, pero a la hora de la verdad lo importante es saber si las dominas o no, y esto es lo que intentarán saber en una posible entrevista técnica de trabajo. Tener tus proyectos en un repositorio como GitHub y un buen proyecto final es una excelente carta de presentación. Primero, porque no solo son líneas de un CV, sino hechos, realidades sobre los que puedes hablar. De hecho, si juegas bien tus cartas de presentación, parte de la entrevista sobre una tecnología concreta puede tratar sobre cómo la has usado en tu proyecto.</p>
<p>Pongamos un ejemplo. Varios alumnos míos han trabajado su proyecto de una aplicación móvil usando Flutter, otros Kotlin y de <em>backend</em> Firebase. Esto lo hicieron por un comentario que oyeron en la empresa X sobre tecnologías a usar. Aprovecharon el proyecto para aprender y ponerlo en práctica con excelentes resultados (¡Bien por ellos! 👏👏). En su entrevista no solo hablaron de un par de líneas de su CV, sino que pudieron/pueden mostrar su <em>app</em> y hablar de aspectos de ella. Mejor o peor, les han dado a entender que a nivel inicial conocen y dominan dichas tecnologías. Pasaron a tener un poco de control en la entrevista y sentirse más cómodos en ella. Por otro lado, tener varios proyectos en GitHub permite mostrar lo que han realizado, ya sea como alumnos o particularmente. Hoy todos van a trabajar en dicho campo amortizando su propio proyecto y el esfuerzo que han hecho en él. Solamente por haber aprendido esas tecnologías y amortizarlas ha merecido la pena su esfuerzo. Otro ejemplo: una alumna ha conseguido una nueva oportunidad laboral en otra empresa gracias en parte a lo aplicado en su proyecto y a otros tantos que ha hecho y tiene visibles en GitHub. Tercer consejo:</p>
<div class="hint-container tip">
<p class="hint-container-title">Consejos</p>
<p>Es mejor mostrar que solamente decir. Un código vale más que mil palabras (o líneas de CV).</p>
</div>
<p>Como ves, tus prácticas y proyecto pueden ser algo más si tú quieres. De hecho, te recomiendo que así sea. Mira la oportunidad y lánzate a por ella si quieres/puedes. Muchas veces te darán un tema cerrado, pero eso también puede ser positivo, pues debes mostrar profesionalidad. Invierte en ti e invierte en tu futuro: tu proyecto puede ser tan importante como tú quieras que lo sea y, con él, una excelente carta de presentación para conseguir una oportunidad laboral.</p>
<p>Finalmente te dejo unos consejos útiles para tu proyecto:</p>
<ul>
<li>Usa un repositorio como <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer">Git</a> y uno remoto como <a href="https://github.com/" target="_blank" rel="noopener noreferrer">GitHub</a>.</li>
<li>Sigue un flujo de trabajo como <a href="https://www.atlassian.com/es/git/tutorials/comparing-workflows/gitflow-workflow" target="_blank" rel="noopener noreferrer">GitFlow</a>.</li>
<li>Planifica usando <a href="https://kanbanize.com/es/recursos-de-kanban/primeros-pasos/que-es-kanban" target="_blank" rel="noopener noreferrer">Kanban</a> y herramientas propias de GitHub o <a href="https://trello.com/es" target="_blank" rel="noopener noreferrer">Trello</a>.</li>
<li>Escribe el <a href="https://www.ionos.es/digitalguide/paginas-web/desarrollo-web/clean-code-que-es-el-codigo-limpio/" target="_blank" rel="noopener noreferrer">código más limpio</a> posible: usa <a href="https://refactoring.guru/es/design-patterns" target="_blank" rel="noopener noreferrer">patrones</a>, refactoriza, etc.</li>
<li>Documenta bien, ponle cariño: puedes usar <a href="https://pages.github.com/" target="_blank" rel="noopener noreferrer">GitHub Pages</a> para presentar tu proyecto.</li>
<li><a href="https://es.wikipedia.org/wiki/Prueba_unitaria" target="_blank" rel="noopener noreferrer">Testea</a>: no se trata de presentar cualquier cosa.</li>
<li>Usa, si puedes, tecnologías y técnicas demandadas por las empresas.</li>
<li>Cree en ti mismo y en que eres capaz de todo 👍.</li>
</ul>
<blockquote>
<p>&quot;Primero, ten un ideal práctico claro y definido: una meta, un objetivo.</p>
<p>Segundo, ten los medios necesarios para lograr tus fines: sabiduría, dinero, materiales y métodos.</p>
<p>En tercer lugar, ajusta todos tus medios para ese fin&quot;.</p>
<p>Aristóteles</p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://img.freepik.com/vector-gratis/asistencia-innovacion-portatil_82574-972.jpg" type="image/jpeg"/>
    </item>
    <item>
      <title>El secreto para seguir aprendiendo</title>
      <link>https://joseluisgs.dev/posts/2021/2021-01-18-seguir-aprendiendo.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-01-18-seguir-aprendiendo.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">El secreto para seguir aprendiendo</source>
      <description>Aprender es el camino, que nunca has de dejar de recorrer</description>
      <category>Blog</category>
      <category>Proyectos</category>
      <pubDate>Mon, 18 Jan 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Muchas veces nos paramos a pensar si en el mundo del desarrollo <em>software</em> alguna vez dejamos de aprender. La respuesta es sencilla: No.</p>
<p>Las tecnologías cambian y el mundo también y es por ello que es importante saber adaptarse a los cambios y, si es posible, anticiparse a ellos.</p>
<!-- more -->
<p>Pero de nada sirve ponerse a lo loco si no tenemos una buena base. Es por ello que distintos conceptos del desarrollo del <em>software</em> y de la informática en general son fundamentales para poder entender y dominar las tecnologías actuales, como por ejemplo: tipos de datos abstractos, programación orientada a objetos,
conceptos de asincronía, trabajar en JS a nivel de DOM, fundamentos de HTML y CSS, lenguajes de consultas de datos, etc.</p>
<div class="hint-container tip">
<p class="hint-container-title">Consejos</p>
<p>Una buena casa siempre se construye con unos buenos cimientos 🏠</p>
</div>
<p>También somos humanos: tenemos fortalezas y debilidades. Es por ello que debes reforzar tus debilidades apoyándote en tus fortalezas o en lo que se te da mejor. Así será menos frustrante. Siempre intenta mirar las nuevas tendencias, analizar las que mejor se adaptan a ti y poco a poco irás avanzando. El secreto es tener una buena actitud.</p>
<p>Actitud, paciencia, constancia y tolerar la frustración. Sí, frustración 😠, no siempre sale todo a la primera, pero no por eso hay que rendirse o bajar los brazos. Debes tener un discurso positivo para contigo mismo. Piensa que a veces es complicado encajar todas las piezas del rompecabezas, pero que no hay nada imposible. Pase lo que pase, siempre cree en ti.</p>
<p>Lee y mira tutoriales o participa o comparte proyectos por GitHub, pero siempre siguiendo un plan. Traza tu plan y camina a las metas que te pongas 📝. No tengas miedo de equivocarte: de los errores se aprende y más en el mundo del desarrollo.</p>
<p>Finalmente, no dejes pasar oportunidades. No existen las circunstancias ideales, sino que dichas circunstancias las creas tú. Muchas veces cuesta salir de la zona de confort, pero te aseguro que después de ese &quot;pequeño malestar&quot; te esperan muchas satisfacciones al ver que poco a poco avanzas y progresas. El movimiento es necesario para avanzar.</p>
<p>Siempre le digo a mi alumnado que los apuntes se quedan obsoletos y que hay que desarrollar el autoaprendizaje. Hace mucho que cambié mi metodología a un aprendizaje basado en proyectos o retos, donde es fundamental mirar la documentación de los desarrolladores. Si adquieres esa habilidad, sabrás dónde buscar en un futuro y cómo moverte por las guías que te ofrecen los creadores de las tecnologías. Por supuesto, siempre intento (y hay que) desarrollar y ampliar las bases conceptuales que subyacen en toda tecnología, pues es lo que no cambia y se mantiene constante. Lo sé, te puede gustar más o menos esta forma de impartir la docencia (poco a poco la detallaré en esta web). Pero es el camino que yo he elegido viendo los cambios existentes a nivel profesional. Las modas vienen y van, pero tu habilidad como desarrollador comienza por saber cómo seguir avanzando y adaptándote a los cambios, y saber conocerte y aprender a aprender, en seguir caminando 👣.</p>
<blockquote>
<p>&quot;Dale un pez a un hombre, y comerá hoy. Dale una caña y enséñale a pescar y comerá el resto de su vida&quot;.</p>
<p>Antiguo proverbio chino</p>
</blockquote>
<p>Quiero aprovechar para recomendar el Twitter de Pepe <a href="https://twitter.com/NavCode" target="_blank" rel="noopener noreferrer">@NavasCode</a>, el cual siempre está cargado de buenos consejos y con el que coincido plenamente en casi todo 🙂. Échale un ojo de vez en cuando 👀.</p>
<p>Me despido con esta cita:</p>
<blockquote>
<p>&quot;Somos lo que hacemos día a día. De modo que la excelencia no es un acto, sino un hábito&quot;.</p>
<p>Aristóteles</p>
</blockquote>
]]></content:encoded>
      <enclosure url="https://www.x-cart.com/wp-content/uploads/2017/09/how-to-become-a-web-developer.png" type="image/png"/>
    </item>
    <item>
      <title>¡Hola mundo!</title>
      <link>https://joseluisgs.dev/posts/2021/2021-01-12-hola-mundo.html</link>
      <guid>https://joseluisgs.dev/posts/2021/2021-01-12-hola-mundo.html</guid>
      <source url="https://joseluisgs.dev/rss.xml">¡Hola mundo!</source>
      <description>Comienza la aventura</description>
      <category>Blog</category>
      <category>Personal</category>
      <pubDate>Tue, 12 Jan 2021 00:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Este es mi primer mensaje del blog y de mi web. Poco a poco la iré completando.</p>
<p>Espero que vaya bien. Comenzamos con... 🙂</p>
<!-- more -->
<p>Hace mucho que tenía ganas de tener una web. Una vez la tuve, cuando trabajaba dentro del <a href="https://lsi.ugr.es/lsi/" target="_blank" rel="noopener noreferrer">Departamento de Lenguajes y Sistemas Informáticos</a> de la <a href="https://ugr.es" target="_blank" rel="noopener noreferrer">Universidad de Granada</a>.</p>
<p>Ahora, creo que es el momento de recuperarla poniendo en práctica distintas tecnologías y usarla como ejemplo con mi alumnado para tratar distintos temas:</p>
<ul>
<li><a href="https://vuejs.org/" target="_blank" rel="noopener noreferrer">Vue.js</a> y desarrollo <em>frontend</em> con JavaScript.</li>
<li>Uso de <a href="https://markdown.es/" target="_blank" rel="noopener noreferrer">Markdown</a> como lenguaje para escribir contenido.</li>
<li><a href="https://developer.mozilla.org/es/docs/Web/HTML" target="_blank" rel="noopener noreferrer">HTML</a>, <a href="https://developer.mozilla.org/es/docs/Web/CSS" target="_blank" rel="noopener noreferrer">CSS</a> y <a href="https://sass-lang.com/" target="_blank" rel="noopener noreferrer">Sass</a>.</li>
<li><a href="https://www.aeuroweb.com/que-es-diseno-responsive/" target="_blank" rel="noopener noreferrer">Diseño adaptativo</a> para distintos dispositivos.</li>
<li>Manejo de <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer">Git</a>/<a href="https://github.com/" target="_blank" rel="noopener noreferrer">GitHub</a>.</li>
<li><a href="https://ssr.vuejs.org/" target="_blank" rel="noopener noreferrer"><em>Server Side Rendering</em></a> con Vue.js y <a href="https://vuepress.vuejs.org/" target="_blank" rel="noopener noreferrer">VuePress</a>.</li>
<li><a href="https://www.viewnext.com/integracion-continua/" target="_blank" rel="noopener noreferrer">Integración, desarrollo y despliegue continuo</a> usando <a href="https://pages.github.com/" target="_blank" rel="noopener noreferrer">GitHub Pages</a> y <a href="https://www.netlify.com/" target="_blank" rel="noopener noreferrer">Netlify</a>.</li>
<li><a href="https://developer.mozilla.org/es/docs/Web/Progressive_web_apps" target="_blank" rel="noopener noreferrer"><em>Progressive Web Apps</em></a> (PWA).</li>
<li><a href="https://www.ciudadano2cero.com/que-es-google-analytics/" target="_blank" rel="noopener noreferrer">Google Analytics</a>.</li>
</ul>
<p>Como se puede ver, aparte de una web personal, es un ejemplo de diversas tecnologías que uso a nivel docente o a nivel particular y que se pueden usar como ejemplos. Este ha sido mi verdadero objetivo con esta web. De hecho, todo el código siempre estará disponible en mi <a href="https://github.com/joseluisgs" target="_blank" rel="noopener noreferrer">repositorio</a>.</p>
<p>Espero que poco a poco pueda servir a alguien más. Aún no está todo hecho, pero poco a poco lo estará 💪.</p>
]]></content:encoded>
      <enclosure url="http://elfarolillorojo.es/wp-content/uploads/2020/08/hello-world-1024x544.png" type="image/png"/>
    </item>
  </channel>
</rss>