<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>clase</title><link>https://devtia.com/</link><description>Llevamos empresas a la nueva era digital. Te ayudamos a mejorar tus procesos a través de las métricas.</description><language>es</language><pubDate>Thu, 16 Apr 2026 01:12:43 +0200</pubDate><lastBuildDate>Thu, 16 Apr 2026 01:12:43 +0200</lastBuildDate><generator>DEVTIA</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><ttl>3600</ttl><item><title>Maneja Intellij IDEA como una bestia: refactoring</title><link>https://devtia.com/post/maneja-intellij-idea-como-una-bestia-refactoring</link><description><![CDATA[<div class="alert alert-warning mt-5 mb-5" role="alert">En esta entrada mostramos los atajos por defecto de teclado disponibles para WINDOWS / LINUX. Consulta en la web de Jetbrains los atajos correspondientes a OSX.</div><p><strong>Intellij IDEA</strong> junto con el resto de "sabores" del mismo como es <strong>PHPStorm</strong>, <strong>PyCharm</strong>, <strong>WebStorm</strong> es considerado por muchos <strong>el mejor entorno de desarrollo integrado que existe en la actualidad</strong>.</p><p>Esta entrada forma parte de una serie en la que vamos a aprender a usar este editor de forma profesional, a trav&eacute;s del conocimiento de los diferentes atajos de teclado disponibles, as&iacute; como de otras funcionalidades muy &uacute;tiles para el d&iacute;a a d&iacute;a.</p><p>En esta entrada vamos a centrarnos en una de las caracter&iacute;sticas m&aacute;s potentes del IDE <strong>la refactorizaci&oacute;n</strong>.</p><h2>Recomendaciones previas</h2><p><strong>No es necesario que te aprendas todos el primer d&iacute;a.</strong> Te recomendamos elegir dos o tres atajos de teclado. Los que te parezcan m&aacute;s &uacute;tiles y los apuntes en un posit junto a la pantalla para que no se te olviden. Practica con ellos durante algunos d&iacute;as y una vez que los tengas dominados, contin&uacute;es tu aprendizaje con otro grupo de dos o tres.</p><p>El primer atajo de teclado que vamos a recomendarte en cada una de las entradas, es encontrar una acci&oacute;n.</p><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Encontrar una acci&oacute;n</h3><p><kbd>ALT</kbd> + <kbd>SHIFT</kbd> + <kbd>A</kbd></p><p>Con este atajo podemos lanzar el men&uacute; de selecci&oacute;n de acci&oacute;n. Este atajo es de los m&aacute;s &uacute;tiles ya que te muestra el atajo de la acci&oacute;n que est&aacute;s buscando por lo que puedes utilizarlo cuando no te acuerdes del atajo que quieres lanzar de forma que vas a poder aprenderlos poco a poco.</p></div><div class="col-sm-4"><img alt="encontrar una acci&oacute;n" class="img-fluid" loading="lazy" src="/uploads/content/image/image/d9cc3c9f067bd8216717c701ade184884bede663.gif"></div></div><h2>Refactorizaci&oacute;n con Intellij IDEA</h2><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Renombrar variable, funci&oacute;n, m&eacute;todo o clase</h3><p><kbd>SHIFT</kbd> + <kbd>F6</kbd></p><p>Con este atajo podemos renombrar una variable, una funci&oacute;n o m&eacute;todo o una clase. El entorno de desarrollo buscar&aacute; todas las apariciones de dicho elemento y los actualizar&aacute;. Si se trata de una clase cambiar&aacute; el nombre del fichero que lo contiene.</p></div><div class="col-sm-4"><img alt="renombrar variable, funci&oacute;n, m&eacute;todo o clase" class="img-fluid" loading="lazy" src="/uploads/content/image/image/c4e615b53f8a8c639453f093aabb7c205e8b27d2.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer constante</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>C</kbd></p><p>Con este atajo podemos extraer un valor a una constante de la clase.</p></div><div class="col-sm-4"><img alt="extraer constante" class="img-fluid" loading="lazy" src="/uploads/content/image/image/e840c4447b04571a32c26d5696e5a35aa99d3708.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer propiedad</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>F</kbd></p><p>Con este atajo podemos extraer un valor a una propiedad de la clase.</p></div><div class="col-sm-4"><img alt="extraer propiedad" class="img-fluid" loading="lazy" src="/uploads/content/image/image/59447d269e1d10833af7b01c2b04434c61272fc8.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer variable</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>V</kbd></p><p>Con este atajo podemos extraer un valor a una variable del m&eacute;todo.</p></div><div class="col-sm-4"><img alt="extraer variable" class="img-fluid" loading="lazy" src="/uploads/content/image/image/e2548704fe27c86eb6653f59f89ad908726b54d6.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer parametro</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>P</kbd></p><p>Con este atajo podemos extraer un valor a un par&aacute;metro del m&eacute;todo.</p></div><div class="col-sm-4"><img alt="extraer parametro" class="img-fluid" loading="lazy" src="/uploads/content/image/image/5c3804d89ebb582dea37ff8343c79864495d9779.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Extraer m&eacute;todo</h3><p><kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>M</kbd></p><p>Con este atajo podemos extraer un bloque de c&oacute;digo a un m&eacute;todo de la clase. El entorno de desarrollo buscar&aacute; coincidencias del mismo fragmento de c&oacute;digo y nos ofrecer&aacute; reemplazarlas por el nuevo m&eacute;todo.</p></div><div class="col-sm-4"><img alt="extraer m&eacute;todo" class="img-fluid" loading="lazy" src="/uploads/content/image/image/40dcdc71ccdbc0dd74ebc15cbb4423c3e6cee0b4.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Cambiar la firma de un m&eacute;todo</h3><p><kbd>CTRL</kbd> + <kbd>F6</kbd></p><p>Con este atajo podemos cambiar la firma de un m&eacute;todo. El entorno de desarrollo buscar&aacute; todas las llamadas a ese m&eacute;todo y actualizar&aacute; el c&oacute;digo con la nueva firma.</p></div><div class="col-sm-4"><img alt="cambiar la firma de un m&eacute;todo" class="img-fluid" loading="lazy" src="/uploads/content/image/image/f90e90f2fd7bd0cb2d7d3c4ac7a33113ec7de473.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Mover una clase</h3><p><kbd>F6</kbd></p><p>Este atajo nos va a permitir mover una clase a otro directorio. El entorno de desarrollo actualizar&aacute; el namespace y buscar&aacute; todas las referencias y las actualizar&aacute; tambi&eacute;n.</p></div><div class="col-sm-4"><img alt="mover una clase" class="img-fluid" loading="lazy" src="/uploads/content/image/image/63acca6bd89952ba6f1721cee62d639f33520904.gif"></div></div><p class="mt2"></p>
]]></description><guid>https://devtia.com/post/maneja-intellij-idea-como-una-bestia-refactoring</guid><pubDate>Tue, 27 Oct 2020 12:44:27 +0100</pubDate></item><item><title>Maneja Intellij IDEA como una bestia: navegación</title><link>https://devtia.com/post/maneja-intellij-idea-como-una-bestia-navegacion</link><description><![CDATA[<div class="alert alert-warning mt-5 mb-5" role="alert">En esta entrada mostramos los atajos por defecto de teclado disponibles para WINDOWS / LINUX. Consulta en la web de Jetbrains los atajos correspondientes a OSX.</div><p>En esta entrada vamos a centrarnos en como <strong>navegar de la forma m&aacute;s productiva posible</strong> es decir llegar en el menor tiempo posible al lugar al que queremos llegar, tanto si es a otro fichero como si es a una zona diferente del fichero en el que nos encontramos.</p><h2>Recomendaciones previas</h2><p><strong>No es necesario que te aprendas todos el primer d&iacute;a.</strong> Te recomendamos elegir dos o tres atajos de teclado. Los que te parezcan m&aacute;s &uacute;tiles y los apuntes en un posit junto a la pantalla para que no se te olviden. Practica con ellos durante algunos d&iacute;as y una vez que los tengas dominados, contin&uacute;es tu aprendizaje con otro grupo de dos o tres.</p><p>El primer atajo de teclado que vamos a recomendarte en cada una de las entradas, es encontrar una acci&oacute;n.</p><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Encontrar una acci&oacute;n</h3><p><kbd>ALT</kbd> + <kbd>SHIFT</kbd> + <kbd>A</kbd></p><p>Con este atajo podemos lanzar el men&uacute; de selecci&oacute;n de acci&oacute;n. Este atajo es de los m&aacute;s &uacute;tiles ya que te muestra el atajo de la acci&oacute;n que est&aacute;s buscando por lo que puedes utilizarlo cuando no te acuerdes del atajo que quieres lanzar de forma que vas a poder aprenderlos poco a poco.</p></div><div class="col-sm-4"><img alt="encontrar una acci&oacute;n" class="img-fluid" loading="lazy" src="/uploads/content/image/image/d9cc3c9f067bd8216717c701ade184884bede663.gif"></div></div><h2>Navegaci&oacute;n entre ficheros</h2><p>En el primer bloque vamos a aprender como desplazarnos entre diferentes ficheros para llegar lo antes posible al documento que queremos llegar.</p><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Buscar la clase o archivo</h3><p><kbd>CTRL</kbd> + <kbd>N</kbd> / <kbd>CTRL</kbd> + <kbd>SHIFT</kbd> + <kbd>N</kbd></p><p>Con este atajo podemos buscar la clase o el archivo al que queremos ir respectivamente. El buscador de archivos funciona incluso si el editor est&aacute; indexando el proyecto.</p></div><div class="col-sm-4"><img alt="abrir y cerrar la vista de proyecto" class="img-fluid" loading="lazy" src="/uploads/content/image/image/d1eb67a1f11274b0560499ee808491bdde9e2d43.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Navegar a la referencia</h3><p><kbd>CTRL</kbd> + <kbd>B</kbd> / <kbd>CTRL</kbd> + <kbd>CLICK</kbd></p><p>Con este atajo podemos navegar hacia la definici&oacute;n de una clase o m&eacute;todo. Si existen varias disponibles, nos mostrar&aacute; todas las opciones. Si tienes instalado y configurado el plugin de symfony te permite navegar entre rutas o definiciones de plantillas.</p></div><div class="col-sm-4"><img alt="navegar a la referencia" class="img-fluid" loading="lazy" src="/uploads/content/image/image/3329cd0a5c84db70cf2cecc5ba214c17161deda4.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Navegar a hacia delante o hacia atr&aacute;s</h3><p><kbd>ALT</kbd> + <kbd>SHIFT</kbd> + <kbd>LEFT</kbd> / <kbd>ALT</kbd> + <kbd>SHIFT</kbd> + <kbd>RIGHT</kbd></p><p>Con este atajo podemos navegar hacia delante o hacia atr&aacute;s en la navegaci&oacute;n que hemos realizado.</p></div><div class="col-sm-4"><img alt="navegar a hacia delante o hacia atr&aacute;s" class="img-fluid" loading="lazy" src="/uploads/content/image/image/1d6ec26aadce4e6dc64f994ab18a305c7396cf55.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Ver ficheros visitados recientemente</h3><p><kbd>CTRL</kbd> + <kbd>E</kbd></p><p>Con este atajo podemos navegar entre los ficheros que hemos visitado recientemente.</p></div><div class="col-sm-4"><img alt="ver ficheros abiertos recientemente" class="img-fluid" loading="lazy" src="/uploads/content/image/image/3b981d74b3804e511a3bfee4167a445d31223182.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Ver localizaciones visitadas recientemente</h3><p><kbd>CTRL</kbd> + <kbd>SHIFT</kbd> + <kbd>E</kbd></p><p>Con este atajo podemos navegar entre los bloques de c&oacute;digo que hemos visitado recientemente.</p></div><div class="col-sm-4"><img alt="ver localizaciones visitadas recientemente" class="img-fluid" loading="lazy" src="/uploads/content/image/image/f208e93171b0c93bfc0cf7fa8bfc2960dfa3a2b4.gif"></div></div><h2>Navegaci&oacute;n dentro de un fichero</h2><p>A continuaci&oacute;n vamos a ver los atajos que consideramos m&aacute;s interesantes y que nos van a permitir navegar dentro de un mismo fichero muy r&aacute;pidamente y sin utilizar el rat&oacute;n.</p><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Ver estructura</h3><p><kbd>CTRL</kbd> + <kbd>F12</kbd></p><p>Con este atajo nos abre la vista de estructura para poder navegar f&aacute;cilmente entre los diferentes m&eacute;todos de una clase.</p></div><div class="col-sm-4"><img alt="ver estructura" class="img-fluid" loading="lazy" src="/uploads/content/image/image/64982115b681a1ffc44f28c0755503d6a299c9ce.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">M&eacute;todo anterior o siguiente</h3><p><kbd>ALT</kbd> + <kbd>UP</kbd> / <kbd>ALT</kbd> + <kbd>DOWN</kbd></p><p>Con este atajo podemos desplazarnos entre los diferentes m&eacute;todos de la clase.</p></div><div class="col-sm-4"><img alt="m&eacute;todo anterior o siguiente" class="img-fluid" loading="lazy" src="/uploads/content/image/image/4e54af804744ecae1d58c316a245eaf67c9dd981.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Ir al siguiente error</h3><p><kbd>F2</kbd></p><p>Con este atajo podemos desplazarnos al siguiente error dentro de un fichero.</p></div><div class="col-sm-4"><img alt="ir al siguiente error" class="img-fluid" loading="lazy" src="/uploads/content/image/image/97b5f7df8dba8f16032d8cfa9787f216f5eca48b.gif"></div></div><div class="row mt-5"><div class="col-sm-8"><h3 class="mt-0">Saltar a la l&iacute;nea</h3><p><kbd>CTRL</kbd> + <kbd>G</kbd></p><p>Con este atajo podemos desplazarnos directamente a la l&iacute;nea a la que nos queremos mover.</p></div><div class="col-sm-4"><img alt="saltar a la l&iacute;nea" class="img-fluid" loading="lazy" src="/uploads/content/image/image/1398eb9077ccadb35492a8dcc5e52ac880e0aab6.gif"></div></div><p class="mt-5">Te recomendamos que leas todos los atajos disponibles tanto en la web de jetbrains como en la configuraci&oacute;n de atajos de teclado de tu editor para que descubras por t&iacute; mismo todos los atajos disponibles.</p><p>Si te gustar&iacute;a recibir una entrada como esta cada semana en tu bandeja de entrada: ap&uacute;ntate a nuestra academia.</p>
]]></description><guid>https://devtia.com/post/maneja-intellij-idea-como-una-bestia-navegacion</guid><pubDate>Sun, 25 Oct 2020 18:00:38 +0100</pubDate></item><item><title>Código limpio: Organiza tus clases</title><link>https://devtia.com/post/codigo-limpio-organiza-tus-clases</link><description><![CDATA[<p>En la &uacute;ltima entrada realizamos una introducci&oacute;n a las <a href="/post/codigo-limpio-pruebas-automaticas">pruebas autom&aacute;ticas</a>. En esta entrada vamos a ver las reglas que utilizamos nosotros para mantener organizadas las clases que escribimos.</p><h2>Elige tu estilo</h2><p>En mi opini&oacute;n no tan importante que reglas utilices para organizar tus clases. Lo que s&iacute; es muy importante, es que <strong>elijas unas y te comprometas con ellas</strong>. Tambi&eacute;n es muy importante que <strong>no seas dogm&aacute;tico</strong> y que <strong>est&eacute;s dispuesto a a&ntilde;adir o modificar tus reglas si es que aparecen otras mejores</strong>.</p><p>No hay nada m&aacute;s frustrante que tratar de explicar a un desarrollador una forma mejor de hacer las cosas, y que no sea capaz de salir de sus trece, pero que tampoco sea capaz de argumentar un motivo.</p><p>Dentro de las reglas que elijas ten en cuenta <strong>que nada debe quedar al azar</strong>, si no que se note que <strong>todo est&aacute; de una forma determinada por un motivo determinado</strong> y no simplemente por que ha quedado as&iacute;.</p><h2>Nuestro estilo</h2><p>Nosotros utilizamos la <a href="https://symfony.com/doc/current/contributing/code/standards.html">guia de estilo</a> oficial para symfony ya que es el framework con el que trabajamos. Adem&aacute;s es muy sencillo configurar el editor para que las aplique autom&aacute;ticamente.</p><p>A continuaci&oacute;n vamos a explicar el resto de reglas que utilizamos nosotros y que no son exactamente iguales a las que describe el autor en el libro. Pero que vamos a argumentarte los motivos que hemos tenido en cuenta para elegirlas.</p><p>Para ello vamos a analizar la clase <code>Rectangle</code> que ya conocemos de entradas anteriores.</p><p>Casi todo lo que vamos a ver son esos peque&ntilde;os detalles que forman parte de nuestra pol&iacute;tica de no dejar nada al azar, y que probablemente <strong>no supongan una gran diferencia por s&iacute; s&oacute;los</strong>, pero que <strong>en conjunto y adoptadas por todo el equipo</strong>, nos permiten <strong>generar una base de c&oacute;digo de calidad</strong> en todos nuestros proyectos.</p><p>Vamos a describir las reglas leyendo la clase de arriba a abajo.</p><h3>Cabecera</h3><p>La cabecera es normalmente un fragmento de c&oacute;digo comentado que se pone encima de cada fichero. Deber&iacute;a indicar a que proyecto pertenece, que tipo de licencia aplica y que personas han intervenido en ella.</p><p>Lo m&aacute;s habitual es utilizar alguna herramienta para <strong>asegurar que todos los ficheros tienen la cabecera correcta</strong> y esta est&aacute; actualizada a la &uacute;ltima versi&oacute;n.</p><pre><code class="php">/*
 * This file is part of the clean code example package.
 *
 * Copyright (c) 2016-2020 Devtia Soluciones.
 * All rights reserved.
 *
 * @author Daniel Gonz&aacute;lez &lt;daniel@devtia.com&gt;
 */
final class Rectangle
{
    //..
}</code></pre><h3>La definici&oacute;n de la clase</h3><p>Nosotros <strong>intentamos por defecto ser lo m&aacute;s restrictivos posible</strong>, y luego ir abriendo la visibilidad seg&uacute;n sea necesario. Por eso siempre declaramos nuestras clases por defecto como <code>final</code>. Una palabra reservada que nos permite indicar que la clase no puede extenderse.</p><h3>Las constantes de clase</h3><p>Nosotros habitualmente <strong>separamos las constantes en clases independientes</strong>. Por ejemplo, si quisi&eacute;ramos poner los colores como constantes, es mejor crear una clase diferente para contenerlos. De esta forma mejoramos la sem&aacute;ntica del dominio.</p><pre><code class="php">final class Color
{
    const WHITE = 'FFFFFF';
    const BLACK = '000000';
}</code></pre><h3>Atributos</h3><p>Como norma general declaramos todos atributos como <code>private</code>. Si fuera necesario cuando realizamos un dise&ntilde;o, algunos de estos los declaramos como <code>protected</code> y evitamos siempre los atributos <code>public</code> salvo cuando se trata de DTOs.</p><p>Si tenemos atributos de diferente visibilidad dentro de una clase los agrupamos seg&uacute;n su visibilidad.</p><p>Dentro de cada grupo, <strong>ordenamos los atributos por orden de importancia</strong>. Los m&aacute;s importantes arriba. <strong>Tambi&eacute;n tratamos de agruparlos por tipo</strong>, para que est&eacute;n todas las fechas juntas, todos las colecciones juntas, ...</p><pre><code class="php">final class Rectangle
{
    /* @var Point */
    private $topLeft;

    /** @var Color */
    private $backgroundColor;

    /** @var Color */
    private $borderColor;

    /* @var float */
    private $border;

    /* @var float */
    private $height;

    /* @var float */
    private $width;

    //..
}</code></pre><h3>El constructor y el resto m&eacute;todos m&aacute;gicos</h3><p>A continuaci&oacute;n de los atributos <strong>escribimos el constructor de la clase</strong>. <strong>Una clase no deber&iacute;a encontrarse en una situaci&oacute;n inconsistente</strong>. Por eso <strong>el constructor deber&iacute;a recibir todo aquello que la clase necesita para existir</strong>. De esta forma evitamos por ejemplo la existencia de un <code>Rectangle</code> cuya altura est&aacute; sin definir.</p><pre><code class="php">final class Rectangle
{
    //..
    public function __construct(Point $topLeft, float $height, float $width)
    {
        $this-&gt;topLeft = $topLeft;
        $this-&gt;height = $height;
        $this-&gt;width = $width;
    }
    //..
}</code></pre><p>Los metodos m&aacute;gicos son m&eacute;todos que est&aacute;n definidos para todas las clases en php, y que son invocados cuando ocurre una circustancia concreta. Nosotros los definimos por orden alfab&eacute;tico despu&eacute;s del constructor.</p><h3>Los m&eacute;todos p&uacute;blicos</h3><p>Nosotros ordenamos los m&eacute;todos agrup&aacute;ndolos por visibilidad. Arriba del todos los m&eacute;todos <code>public</code> luego todos los <code>protected</code> y finalmente todos los <code>private</code>.</p><p>El autor nos indica que se deber&iacute;an ordenar seg&uacute;n su importancia y mantener los m&eacute;todos relacionados cerca entre s&iacute;. Nosotros creemos que esto podr&iacute;a tener sentido en el momento de publicar el libro, pero es una regla dificil de aplicar, cuando por ejemplo un mismo m&eacute;todo se llama desde diferentes m&eacute;todos, &iquest;junto a cual lo pones?</p><p>Los editores modernos nos permiten navegar con facilidad entre m&eacute;todos a&uacute;nque no est&eacute;n cercanos entre s&iacute;, asi que nosotros <strong>simplemente los ordenamos alfabeticamente</strong> y <strong>configuramos el editor para que haga esto autom&aacute;ticamente</strong> por nosotros.</p><h3>El resto de m&eacute;todos</h3><p>Los m&eacute;todos que no son <code>public</code> deber&iacute;an declararse <code>private</code> a menos que exista una buena raz&oacute;n para ello. Los ordenamos tambi&eacute;n por orden alfab&eacute;tico y dejamos que el editor se encarge de esta tarea.</p><h3>Bloques de c&oacute;digo</h3><p>Para explicar esto es necesario traer un ejemplo un poco m&aacute;s complejo. Veamos un ejemplo real de uno de nuestros proyectos.</p><pre><code class="php">final class ProposalController extends AbstractProposalController
{
    //..
    public function copyAction(Project $proposal)
    {
        $this-&gt;throwNotFoundExceptionIfNotIsPublicProposal($proposal);

        $copyManager = $this-&gt;get('app.service.project_copy_manager');
        $publicProposal = $copyManager-&gt;generatePublicProposal(
            $proposal,
            null,
            \AppBundle\Entity\Project\Type::PUBLIC_PROPOSAL_CLIENT_VERSION
        );

        $manager = $this-&gt;get('app.service.project_manager');
        $manager-&gt;update($publicProposal);

        $this-&gt;addFlash('success', 'proposal successfully updated');

        return $this-&gt;redirectToRoute('_public.proposal.view', ['hash' =&gt; $publicProposal-&gt;getHash()]);
    }
    //..
}</code></pre><p>Si ves el ejemplo anterior puedes ver que <strong>cada concepto est&aacute; separado por un salto de l&iacute;nea</strong>. Lo primero que hacemos es la validaci&oacute;n o cl&aacute;usulas de guarda, de las que hablaremos en otra entrada m&aacute;s adelante.</p><p>Lo siguiente que hacemos es obtener una copia de la propuesta a trav&eacute;s del servicio correspondiente. Despu&eacute;s actualizamos esa copia, mensaje de &eacute;xito y redirecci&oacute;n a la nueva propuesta.</p><p>Cada concepto est&aacute; separado por un salto de l&iacute;nea facilitando su entendimiento al lector.</p><h3>Ni un salto de l&iacute;nea de m&aacute;s</h3><p>El &uacute;ltimo detalle. Ya hemos visto donde s&iacute; debemos poner un salto de l&iacute;nea. En nuestra opini&oacute;n <strong>no se deben a&ntilde;adir saltos de l&iacute;nea adicionales</strong>, porque dan la sensaci&oacute;n de estar ah&iacute; por que s&iacute;, en lugar de porque tienen que estar.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-organiza-tus-clases</guid><pubDate>Thu, 24 Sep 2020 13:45:45 +0200</pubDate></item><item><title>Código limpio: Pruebas automáticas</title><link>https://devtia.com/post/codigo-limpio-pruebas-automaticas</link><description><![CDATA[<p>En la &uacute;ltima entrada repasamos qu&eacute; deber&iacute;as tener en cuenta a la hora de realizar una correcta <a href="/post/codigo-limpio-gestion-de-errores">gesti&oacute;n de errores</a>. En esta entrada vamos a realizar una peque&ntilde;a intruducci&oacute;n a las pruebas autom&aacute;ticas, &iquest;qu&eacute; son? &iquest;para qu&eacute; sirven? y sobre todo que caracter&iacute;siticas tienen que tener para ser realmente &uacute;tiles.</p><h2>&iquest;Pruebas autom&aacute;ticas?</h2><p>Las pruebas o la bater&iacute;a de pruebas es un software que debe escribirse en paralelo a nuestro propio software y cuya responsabilidad es comprobar que nuestro software funciona correctamente. B&aacute;sicamente es <strong>software que prueba nuestro software</strong>.</p><p>Puede que intuitivamente pienses que construir una bater&iacute;a de pruebas, es trabajo adicional, ya que probablemente tardar&aacute;s m&aacute;s tiempo y por lo tanto tu trabajo ser&aacute; m&aacute;s caro para tus clientes pero esto no es as&iacute;.</p><p>Cuando tienes una buena bater&iacute;a de pruebas, la primera "derivada" es que vas a cometer menos errores. Menos errores supone menos tiempo arreglando errores y m&aacute;s tiempo programando. Adem&aacute;s supone menos frustraci&oacute;n en los usuarios.</p><p>La segunda derivada es que puedes hacer cambios con mayor confianza, y esto te hace ir m&aacute;s r&aacute;pido. Las pruebas detectar&aacute;n que has roto, y simplemente tendr&aacute;s que arreglarlo, un equipo con una buena bater&iacute;a de pruebas termina trabajando mucho m&aacute;s r&aacute;pido que un equipo que no las tiene.</p><p>Hay una tercera derivada y es que al desarrollar tu bater&iacute;a de pruebas te va a permitir dise&ntilde;ar mejor software, pero esto es algo en lo que profundizaremos en una entrada m&aacute;s adelante.</p><h2>&iquest;Si es tan bonito por que no todo el mundo hace pruebas autom&aacute;ticas?</h2><p>Cuando escribes pruebas debes ser tan cuidadoso c&oacute;mo con el c&oacute;digo de producci&oacute;n. Todo lo que hemos comentado ya en esta serie sobre elegir buenos <a href="/post/codigo-limpio-nombres">nombres</a>, <a href="/post/codigo-limpio-funciones">funciones</a>, <a href="/post/codigo-limpio-argumentos">argumentos</a>, etc son cosas que tambi&eacute;n aplican a las pruebas autom&aacute;ticas.</p><p>Escribir buenas pruebas requiere cierta t&eacute;cnica y ocurre que muchos programadores o equipos comienzan haciendo pruebas, y al no ser pruebas de calidad terminan teniendo una bater&iacute;a de pruebas que supone m&aacute;s un problema que una soluci&oacute;n. Tras esta mala experiencia desisten, por eso en esta entrada vamos a explicarte c&oacute;mo escribir buenas pruebas.</p><h2>Escribe tus pruebas "PRIMERO"</h2><p><img alt="Escribe pruebas r&aacute;pido" loading="lazy" src="/cache/thumb_1200_0400/uploads/content/image/image/dcc63077554f52edf72fcc4d5c15300636e851dc.jpg" title="Escribe pruebas r&aacute;pido"></p><p><em>FIRST</em> o PRIMERO es un acr&oacute;nimo que se utiliza para describir las caracter&iacute;sticas que deber&iacute;a tener una prueba.</p><h3><em>Fast</em> o R&aacute;pidas</h3><p>Una prueba, as&iacute; como la bater&iacute;a de pruebas a la que pertenecen deber&iacute;an ser tan r&aacute;pidas c&oacute;mo sea posible. &iquest;Cu&aacute;nto? Pues depende del tipo de prueba y del proyecto. El autor no se moja en este aspecto, pero en mi opini&oacute;n deber&iacute;as mantener cada prueba por debajo de 1 segundo.</p><h3><em>Independent</em> o independientes</h3><p>Unas pruebas no deber&iacute;an depender de otras. Esto te permitir&aacute; ejecutar aisladamente las pruebas que fallen en lugar de tener que ejecutar toda la bater&iacute;a o un conjunto de pruebas.</p><h3><em>Repeatable</em> o repetibles</h3><p>El resultado de una prueba deber&iacute;a ser siempre el mismo y no variar si la ejecutas varias veces. De esta forma, podr&aacute;s ejecutar una prueba una y otra vez, hasta que arregles el error. De lo contrario tendr&iacute;as continuamente errores que luego no vas a poder reproducir.</p><h3><em>Self-validating</em> o validaci&oacute;n autom&aacute;tica</h3><p>Lo que quiere decir esta regla es que las pruebas no deber&iacute;an depender de una revisi&oacute;n, por ejemplo que el resultado fuera un texto que luego necesitar&aacute;s leer para saber si es correcto o no. La prueba debe pasar o no pasar, no deber&iacute;a haber otro resultado posible.</p><h3><em>Timely</em> o en el momento correcto</h3><p>El autor introduce en este cap&iacute;tulo las reglas del ciclo de TDD: rojo, verde y refactorizaci&oacute;n, pero no quiero profundizar mucho sobre este aspecto aqu&iacute;, porque tendremos el libro "Test Driven Development: By Example" por en este blog y en el canal de youtube m&aacute;s pronto que tarde.</p><h2>Show me the code</h2><p>Supongamos que tenemos la clase rectangulo que vimos en el cap&iacute;tulo de <a href="/post/codigo-limpio-estructuras-vs-objetos">estructuras vs objetos</a>.</p><pre><code class="php">class Rectangle
{
    private $height;
    private $width;

    public function __construct(float $height, float $width)
    {
        $this-&gt;height = $height;
        $this-&gt;width = $width;
    }

    public function area(): float
    {
        return round($this-&gt;height * $this-&gt;height, 2);
    }
}</code></pre><p>Escribir una prueba es una tarea muy sencilla, normalmente es simplemente una clase que tiene que extender de alg&uacute;n tipo de framework de pruebas, as&iacute; es como se hace con <code>phpunit</code></p><pre><code class="php">class RectangleTest extends \PHPUnit\Framework\TestCase
{
    public function test_area()
    {
        $rectangle = new Rectangle(1.11, 2.22);
        $this-&gt;assertSame(2.46, $rectangle-&gt;area());
    }
}</code></pre><p>F&aacute;cil, pero &iquest;que ocurre al ejecutarlo?</p><div class="alert alert-danger" role="alert">Failed asserting that 1.23 is identical to 2.46.</div><p>Efectivamente hab&iacute;a un error en el c&oacute;digo. En este caso lo hemos introducido a posta, pero es un tipo de error que se puede cometer f&aacute;cilmente.</p><p>&iquest;Has visto cual?</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-pruebas-automaticas</guid><pubDate>Sat, 29 Aug 2020 11:32:50 +0200</pubDate></item><item><title>Código limpio: Gestión de errores</title><link>https://devtia.com/post/codigo-limpio-gestion-de-errores</link><description><![CDATA[<p>En la &uacute;ltima entrada vimos cual es la <a href="/post/codigo-limpio-no-hables-con-estranos">ley de demeter</a>, para que sirve y c&oacute;mo resolver el problema del acoplamiento a una estructura de clases a trav&eacute;s de la inyecci&oacute;n de dependencias. En esta entrada vamos a ver que deber&iacute;as tener en cuenta para mejorar la gesti&oacute;n de errores, como por ejemplo evitar el uso de c&oacute;digos de error, separar la l&oacute;gica de la gesti&oacute;n de errores y por supuesto evitar conocer los detalles de implementaci&oacute;n de las clases que hay por debajo. Cuando programamos adem&aacute;s de que nuestro c&oacute;digo sea limpio, vamos a tener que enfrentarnos a la sucia tarea de la gesti&oacute;n de errores.</p><p>Veamos un ejemplo con la clase responsable de notificar a los usuarios de <a href="/academy/">la academia</a> que hay un nuevo contenido disponible. En el m&eacute;todo <code>send</code> recibiremos un usuario y la entrada que debe serle notificada.</p><p>Partiremos de este primer ejemplo donde la gesti&oacute;n de errores ensucia el c&oacute;digo para ver cuales son las diferentes mejoras que podemos ir realizando.</p><pre><code class="php">class SendCommand extends AbstractCommand
{
    private function send(User $user, Post $post): void
    {
        $code = $this-&gt;mailer-&gt;sendTemplate(
            [$user-&gt;getEmail() =&gt; $user-&gt;getName()],
            $post-&gt;getName(),
            '@App/Command/Academy/Mailing/Post/post.html.twig',
            [
                'user' =&gt; $user,
                'post' =&gt; $post,
            ]
        );

        if (Code::TIMEOUT == $code) {
            sleep(20);
            $this-&gt;send($user, $post);
        } elseif (Code::ADDITIONAL_DATA_REQUIRED == $code) {
            $this-&gt;log(
                sprintf(
                    'message [%06d] for user "%s" could not be sent because was unavailable',
                    $post-&gt;getId(),
                    $user-&gt;getEmail()
                )
            );
        } elseif (Code::ADDITIONAL_DATA_REQUIRED == $code) {
            $this-&gt;log(
                sprintf(
                    'message [%06d] for user "%s" could not be sent because additional space was needed',
                    $post-&gt;getId(),
                    $user-&gt;getEmail()
                )
            );
        } elseif (Code::INBOX_IS_FULL == $code) {
            $this-&gt;log(
                sprintf(
                    'message [%06d] for user "%s" could not be sent because the inbox was full',
                    $post-&gt;getId(),
                    $user-&gt;getEmail()
                )
            );
        }
    }
}
</code></pre><p>Como se puede ver este ejemplo inicial tiene bastante que mejorar.</p><h2>Evita utilizar c&oacute;digos de error</h2><p>Los c&oacute;digos de error son <strong>dif&iacute;ciles de manejar</strong>. En general van a requerir un mont&oacute;n de estructuras if / else para poder lidiar con los diferentes c&oacute;digos de error.</p><p>Adem&aacute;s siempre cabe la posibilidad de que se a&ntilde;ada un nuevo c&oacute;digo de error que nosotros no tengamos capturado. En lugar de c&oacute;digos de error ser&iacute;a conveniente utilizar excepciones.</p><p>A continuaci&oacute;n vemos exactamente el mismo c&oacute;digo pero utilizando excepciones:</p><pre><code class="php">class SendCommand extends AbstractCommand
{
    private function send(User $user, Post $post): void
    {
        try {
            $this-&gt;mailer-&gt;sendTemplate(
                [$user-&gt;getEmail() =&gt; $user-&gt;getName()],
                $post-&gt;getName(),
                '@App/Command/Academy/Mailing/Post/post.html.twig',
                [
                    'user' =&gt; $user,
                    'post' =&gt; $post,
                ]
            );
        } catch (TimeoutException $e) {
            sleep(20);
            $this-&gt;send($user, $post);
        } catch (AdditionalDataRequiredException $e) {
            $this-&gt;log(
                sprintf(
                    'message [%06d] for user "%s" could not be sent because was unavailable',
                    $post-&gt;getId(),
                    $user-&gt;getEmail()
                )
            );
        } catch (ServiceUnavailableException $e) {
            $this-&gt;log(
                sprintf(
                    'message [%06d] for user "%s" could not be sent because additional space was needed',
                    $post-&gt;getId(),
                    $user-&gt;getEmail()
                )
            );
        } catch (InboxIsFullException $e) {
            $this-&gt;log(
                sprintf(
                    'message [%06d] for user "%s" could not be sent because the inbox was full',
                    $post-&gt;getId(),
                    $user-&gt;getEmail()
                )
            );
        }
    }
}</code></pre><p>De momento no hemos mejorado mucho y nuestro c&oacute;digo sigue teniendo una pinta terrible, pero estamos un paso m&aacute;s cerca de tener nuestro c&oacute;digo bastante m&aacute;s elegante.</p><h2>Separar l&oacute;gica y gesti&oacute;n de errores</h2><p>Lo que vamos a hacer ahora es separar la responsabilidad en dos m&eacute;todos. Uno de ellos contendr&aacute; la l&oacute;gica de negocio y el otro contendr&aacute; la gesti&oacute;n de errores. Este sencillo cambio hace nuestro c&oacute;digo mucho m&aacute;s legible y f&aacute;cil de mantener.</p><pre><code class="php">class SendCommand extends AbstractCommand
{
    private function handleSendException(User $user, Post $post, \Exception $exception): void
    {
        try {
            throw $exception;
        } catch (TimeoutException $e) {
            // ..
        } catch (AdditionalDataRequiredException $e) {
            // ..
        } catch (ServiceUnavailableException $e) {
            // ..
        } catch (InboxIsFullException $e) {
            // ..
        }
    }

    private function send(User $user, Post $post): void
    {
        try {
            $this-&gt;mailer-&gt;sendTemplate(
                [$user-&gt;getEmail() =&gt; $user-&gt;getName()],
                $post-&gt;getName(),
                '@App/Command/Academy/Mailing/Post/post.html.twig',
                [
                    'user' =&gt; $user,
                    'post' =&gt; $post,
                ]
            );
        } catch (\Exception$exception) {
            $this-&gt;handleSendException(user, $post, $exception);
        }
    }
}</code></pre><h2>Evita los detalles de implementaci&oacute;n</h2><p>Nuestro c&oacute;digo ha mejorado bastante, pero todav&iacute;a tiene un problema. Nuestro comando <strong>conoce los detalles de implementaci&oacute;n del sistema de env&iacute;o de correo</strong>. Esto supone un problema, si en el futuro quisi&eacute;ramos enviar el correo o las notificaciones con otra librer&iacute;a o a trav&eacute;s de otro medio, nuestro comando dejar&iacute;a de funcionar.</p><p>Para ello es conveniente evitar conocer los detalles de implementaci&oacute;n ( las excepciones ) que contiene el sistema de env&iacute;o, con lo que nos deber&iacute;a quedar algo as&iacute;.</p><pre><code class="php">class SendCommand extends AbstractCommand
{
    private function handleSendException(User $user, Post $post, \Exception $exception): void
    {
        $this-&gt;log(
            sprintf(
                'message [%06d] for user "%s" with message "%s"',
                $post-&gt;getId(),
                $user-&gt;getEmail(),
                $exception-&gt;getMessage()
            )
        );
    }

    private function send(User $user, Post $post): void
    {
        try {
            $this-&gt;mailer-&gt;sendTemplate(

                [$user-&gt;getEmail() =&gt; $user-&gt;getName()],
                $post-&gt;getName(),
                '@App/Command/Academy/Mailing/Post/post.html.twig',
                [
                    'user' =&gt; $user,
                    'post' =&gt; $post,
                ]
            );
        } catch (\Exception$exception) {
            $this-&gt;handleSendException($user, $post, $exception);
        }
    }
}</code></pre>
]]></description><guid>https://devtia.com/post/codigo-limpio-gestion-de-errores</guid><pubDate>Sat, 29 Aug 2020 11:31:17 +0200</pubDate></item><item><title>Código limpio: No hables con extraños</title><link>https://devtia.com/post/codigo-limpio-no-hables-con-extranos</link><description><![CDATA[<p>En la &uacute;ltima entrada repasamos qu&eacute; son <a href="/post/codigo-limpio-estructuras-vs-objetos">las estructuras de datos</a>, en que se diferencian de los objetos y cu&aacute;ndo conviene m&aacute;s utilizar una aproximaci&oacute;n u otra. En esta entrada vamos a explicar cual es la ley de demeter, que problemas puedes tener si es que no la cumples y que opciones tienes para cumplirla.</p><p>Empezemos describiendo los motivos por los que <strong>no deber&iacute;as hablar con extra&ntilde;os:</strong> muchas veces cuando tenemos una clase que a su vez est&aacute; formada por objetos de otras clases y estas a su vez por otras, tenemos una estructura de clases. Hasta aqu&iacute; todo bien, pero: &iquest;qu&eacute; ocurre cuando estas estructuras se vuelven complejas? Veamos un ejemplo que podr&iacute;a darse en un comando de symfony.</p><pre><code class="php">class CustomCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this-&gt;getApplication()-&gt;getKernel()-&gt;getContainer()-&gt;getCustomService()-&gt;getManager()-&gt;execute();

        return 1;
    }
}</code></pre><p>Como puedes ver, estamos <strong>acoplando de forma innecesaria</strong> nuestro c&oacute;digo a todas las clases que aparecen en la cadena de llamadas. Cualquier cambio en estas clases o en las relaciones entre ellas podr&iacute;a suponer que nuestro c&oacute;digo dejase de funcionar. Para que esto no suceda viene al rescate la ley de demeter.</p><h2>La ley de demeter</h2><p><img alt="La ley de demeter" loading="lazy" src="/cache/thumb_1200_0200/uploads/content/image/image/f0a9a0fb4bc402545242cec37f8a0821462dce05.jpg"></p><p>La ley de demeter fue descrita por primera vez dentro del proyecto demeter en 1987. Como curiosidad quiero contarte que Demeter, hace referencia a la diosa griega de la agricultura. Querian hacer referencia a algo as&iacute; como "cultivar" software en contraposici&oacute;n a "construir" el software.</p><p>La ley indica que desde un m&eacute;todo <strong>solo deber&iacute;as "hablar" con aquellos a quien conozcas bien</strong>. Y estos son:</p><ul><li>M&eacute;todos de la misma clase.</li><li>M&eacute;todos de los objetos de la clase.</li><li>M&eacute;todos de los objetos creados en la misma funci&oacute;n.</li><li>M&eacute;todos de los objetos pasados como argumento a la funci&oacute;n.</li></ul><h2>&iquest;Y c&oacute;mo lo soluciono?</h2><p>Ya hemos detectado que es lo que no debes hacer, te voy a proponer ahora tres opciones a tener en cuenta cuando detectes este problema en tu c&oacute;digo.</p><h3>No hacer nada</h3><p>La primera es no hacer nada. Es la soluci&oacute;n m&aacute;s sencilla, y <strong>la peor de las tres</strong> que vamos a ver, pero como ya vimos en la entrada sobre <a href="/post/los-dogmas-en-el-desarrollo-de-software">los dogmas en el desarrollo de software</a>, si crees que las clases que est&aacute;s llamando y sus relaciones est&aacute;n muy consolidadas, podr&iacute;as permitirte el lujo de realizar este tipo de llamadas en cadena.</p><h3>Usar atajos</h3><p>La segunda consiste en hacer atajos que permitan evitar estas llamadas encadenadas. Esta es una soluci&oacute;n bastante r&aacute;pida, que te puede permitir salir del paso en alg&uacute;n momento.</p><p>Por ejemplo en <code>CustomService</code> podr&iacute;amos a&ntilde;adir el m&eacute;todo <code>executeManager</code>.</p><pre><code class="php">class CustomService
{
    public function executeManager(): void
    {
        $this-&gt;getManager()-&gt;execute();
    }
}</code></pre><p>Esta soluci&oacute;n como puedes imaginarte tampoco es la mejor. Te permite abstraer parte de esa cadena de llamadas y por lo tanto es una opci&oacute;n mejor que al principio, ya que si la relacion cambia s&oacute;lo tendr&aacute;s que cambiar en este punto las llamadas.</p><p>El problema de esta soluci&oacute;n es que puedes encontrarte con tu c&oacute;digo con un mont&oacute;n de estos m&eacute;todos atajos, que realmente por dise&ntilde;o no deber&iacute;an encontraser ah&iacute;, pero est&aacute;n simplemente para hacer cumplir la ley de demeter.</p><h3>Inyectar dependencias</h3><p>La soluci&oacute;n buena, como probablemente ya te ven&iacute;as imaginando es <strong>inyectar las dependencias</strong>. Veamos un ejemplo.</p><pre><code class="php">class CustomCommand extends Command
{
    public function setCustomManager(CustomManager $customManager)
    {
        $this-&gt;manager = $customManager;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this-&gt;manager-&gt;execute();
    }
}</code></pre><p>Hemos a&ntilde;adido como dependencia la clase <code>CustomManager</code> que era la que realmente necesitamos, y se la hemos inyectado a trav&eacute;s de un setter, pero hemos eliminado toda la cadena de dependencias y las relaciones entre s&iacute;.</p><p>Con este sencillo cambio hemos podido eliminar f&aacute;cilmente todas las dependencias sobrantes.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-no-hables-con-extranos</guid><pubDate>Sat, 29 Aug 2020 10:49:10 +0200</pubDate></item><item><title>Código limpio: Estructuras vs objetos</title><link>https://devtia.com/post/codigo-limpio-estructuras-vs-objetos</link><description><![CDATA[<p>En esta entrada vamos a ver que son las estructuras de datos y los objetos, y que ventajas puede tener el uso de uno u otro tipo. Vamos a implementar exactamente lo mismo con las dos aproximaciones para ver c&oacute;mo se comporta cada una.</p><h2>Estructuras de datos</h2><p>Una estructura de de datos, es una clase o un objeto que expone la informaci&oacute;n que contiene. En el ejemplo hemos evitado el uso de getters y setter, pero ser&iacute;a exactamente lo mismo si los tuviera, lo consideraremos una estructura, porque expone su informaci&oacute;n. Otras clases ser&aacute;n responsables de realizar las funcionalidad que necesitemos.</p><pre><code class="php">class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        return $shape-&gt;radius * $shape-&gt;radius * Geometry::PI;
    }
}</code></pre><h2>Objetos</h2><p>Con el mismo ejemplo si usaramos el paradigma de la orientaci&oacute;n a objetos, debemos ocultar cu&aacute;l es la informaci&oacute;n y la estructura de los datos internos, y debemos exponer la funcionalidad que queremos.</p><pre><code class="php">class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this-&gt;radius * $this-&gt;radius * self::PI;
    }
}</code></pre><p>Aunque el ejemplo es muy sencillo nos permite entender la diferencia. Una estructura de datos es un elemento sencillo que solamente contiene informaci&oacute;n, mientras que la l&oacute;gica se encuentra en otras clases. Un objeto es una abstracci&oacute;n que expone su funcionalidad.</p><h2>A&ntilde;adir nuevas estructuras de datos / objetos</h2><p>Vamos a&ntilde;adir un nuevo tipo: <code>Rectangle</code> y vemos a ver que cambios son necesarios en cada aproximaci&oacute;n.</p><div class="row mb40 mt40"><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    public $topLeft;
    public $height;
    public $width;
}

class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        if ($shape instanceof Rectangle) {
            return $shape-&gt;height * $shape-&gt;width;
        }

        if ($shape instanceof Circle) {
            return $shape-&gt;radius * $shape-&gt;radius * Geometry::PI;
        }

        throw new InvalidArgumentException();
    }
}</code></pre>
Estructura de datos</div><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    private $topLeft;
    private $height;
    private $width;

    public function area(): float
    {
        return $this-&gt;height * $this-&gt;width;
    }
}

class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this-&gt;radius * $this-&gt;radius * self::PI;
    }
}





    </code></pre>
Orientado a objetos</div></div><p>&iquest;Que diferencia ves entre ambos?. En la primera aproximaci&oacute;n adem&aacute;s de a&ntilde;adir la nueva estructura, hemos tenido que modificar <code>Geometry::area()</code>. En el ejemplo es un cambio muy sencillo, pero nos sirve para identificar que cuando a&ntilde;adimos nuevas estructuras vamos a tener que revisar el c&oacute;digo que las utiliza para que sea capaz de manejar esta nueva estrucutra, lo cual nos puede llevar a a&ntilde;adir errores en un c&oacute;diugo que ya estaba funcionando.</p><p>En cambio en la aproximaci&oacute;n orientada a objetos no hemos tenido que modificar ni una coma del c&oacute;digo que ya ten&iacute;amos, y simplemente hemos tenido que a&ntilde;adir un nuevo objeto.</p><h2>A&ntilde;adir funcionalidad</h2><p>Vamos a a&ntilde;adir una nueva funcionalidad, el c&aacute;lculo del per&iacute;metro: <code>perimeter</code> a ver que tal se comporta cada una de nuestras implementaciones.</p><div class="row mb40 mt40"><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    public $topLeft;
    public $height;
    public $width;
}

class Circle
{
    /** @var Point */
    public $center;
    public $radius;
}

class Geometry
{
    const PI = 3.141592653589793;

    public function area($shape): float
    {
        if ($shape instanceof Rectangle) {
            return $shape-&gt;height * $shape-&gt;width;
        }

        if ($shape instanceof Circle) {
            return $shape-&gt;radius * $shape-&gt;radius * Geometry::PI;
        }

        throw new InvalidArgumentException();
    }

    public function perimeter($shape): float
    {
        if ($shape instanceof Rectangle) {
            return 2 * $shape-&gt;height + 2 * $shape-&gt;width;
        }

        if ($shape instanceof Circle) {
            return 2 * Geometry::PI * $shape-&gt;radius;
        }

         throw new InvalidArgumentException();
    }
}</code></pre>
Estructuras de datos</div><div class="col-sm-6"><pre><code class="php">class Rectangle
{
    /** @var Point */
    private $topLeft;
    private $height;
    private $width;

    public function area(): float
    {
        return $this-&gt;height * $this-&gt;width;
    }

    public function perimeter(): float
    {
        return 2 * $this-&gt;height + 2 * $this-&gt;width;
    }
}

class Circle
{
    const PI = 3.141592653589793;

    /** @var Point */
    private $center;
    private $radius;

    public function area(): float
    {
        return $this-&gt;radius * $this-&gt;radius * self::PI;
    }

    public function perimeter(): float
    {
        return 2 * self::PI * $this-&gt;radius;
    }
}








    </code></pre>
Orientado a objetos</div></div><p>En este caso, nuestra primera aproximaci&oacute;n ha sido m&aacute;s sencilla. No hemos tenido que tocar el c&oacute;digo que ya ten&iacute;amos funcionando y s&oacute;lo hemos tenido que a&ntilde;adir una nueva funci&oacute;n.</p><p>En cambio en la aproximaci&oacute;n orientada a objetos hemos tenido que tocar cada una de las clases que ten&iacute;amos para a&ntilde;adirles esta funcionalidad. El ejemplo es muy sencillo, pero nos sirve para identificar el problema.</p><h2>Conclusiones</h2><p>A nivel de l&iacute;neas de c&oacute;digo no existe una diferencia sustancial. Tampoco es razonablemente m&aacute;s sencillo de implementar o de mantener una u otra aproximaci&oacute;n, pero cuando utilizamos estructuras, tendremos cierta facilidad cuando queramos a&ntilde;adir nueva funcionalidad, mientras que cuando utilizamos objetos tendremos facilidades para a&ntilde;adir nuevos tipos.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-estructuras-vs-objetos</guid><pubDate>Sun, 19 Jul 2020 15:29:55 +0200</pubDate></item><item><title>Código limpio: Argumentos</title><link>https://devtia.com/post/codigo-limpio-argumentos</link><description><![CDATA[<p>En esta entrada vamos a profundizar en la importancia que tienen los argumentos y ver algunas reglas para elegirlos y ordenarlos correctamente.</p><h2>N&uacute;mero de argumentos</h2><p>Seg&uacute;n el autor podr&iacute;amos clasificar las funciones o los m&eacute;todos seg&uacute;n el n&uacute;mero de argumentos posibles.</p><p><strong>Mon&aacute;dicaos</strong> tienen un s&oacute;lo argumento, son f&aacute;ciles de testear y f&aacute;ciles de entender. No hay lugar a dudas en cuanto a que es lo que hace y probablemente ser&aacute; bastante sencillo crear un test unitario para la misma. Siempre que podamos debemos basar nuestra api en este tipo de funciones.</p><p><strong>Di&aacute;dicos</strong> tienen dos argumentos. Siguen siendo relativamente f&aacute;ciles de entender, y probablemente seguir&aacute;n siendo f&aacute;ciles de testear.</p><p><strong>Triadicos </strong>son las que tienen tres argumentos, comienza a complicarse la forma de testear, aumenta la complejidad ciclom&aacute;tica o n&uacute;mero de "caminos" posibles dentro de la funci&oacute;n, y se complica recordar los argumentos y su orden. Este tipo de funciones deber&iacute;an evitarse si es posible.</p><p><b>Cuatro o m&aacute;s argumentos</b> Este tipo de funciones tienen todos los problemas que ya hemos comentado pero en mayor expotente. Veamos un ejemplo a continuaci&oacute;n la funci&oacute;n que ejecuta el filtro <code>localizeddate</code> de twig.</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $date,
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $locale = null,
    $timezone = null,
    $format = null,
    $calendar = 'gregorian'
) {
    //..
}</code></pre><p>Habitualmente usamos esta funci&oacute;n de esta forma lo que la hace muy c&oacute;moda de usar</p><pre><code class="twig">{{ item.date|localizeddate }}
{{ item.date|localizeddate('short', 'none') }} {# el uso de las constantes es muy c&oacute;modo en el d&iacute;a a d&iacute;a. #}
</code></pre><p>El problema viene cuando queremos pasarle un formato personalizado. Es muy dificil de recordar cuales son los argumentos, y tenemos que ir a la documentaci&oacute;n para comprobarlo. Adem&aacute;s los primeros dos argumentos son ignorados lo que la vuelve un poco confusa.</p><pre><code class="twig">{{ item.date|localizeddate('none', 'none', null, null, 'MMMM y') }}
{{ item.date|localizeddate('full', 'full', null, null, 'MMMM y') }} {# ambas hacen lo mismo. #}</code></pre><p>&iquest;Que os parece si cambiamos esta funci&oacute;n por dos? En la primera usaremos las constantes definidas para <code>$dateFormat</code> y <code>$timeFormat</code> mientras que en la segunda usaremos un formato personalizado.</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $date,
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $locale = null,
    $timezone = null,
    $calendar = 'gregorian'
) {
//..
}

function twig_localized_date_filter_by_format(
    Twig_Environment $env,
    $date,
    $customFormat = null,
    $locale = null,
    $timezone = null,
    $calendar = 'gregorian'
) {
//..
}</code></pre><p>Hemos reducido un poco el n&uacute;mero de argumentos y hemos facilitado su utilizaci&oacute;n, ya que el uso de la segunda funci&oacute;n quedar&iacute;a en algo parecido a esto.</p><p class="text-muted">Nota: Aunque todav&iacute;a son muchos argumentos el primero es injectado por <code>twig</code> asi que contar&iacute;a uno menos.</p><pre><code class="twig">{{ item.date|localizeddate('da_igual_lo_que_pongas', 'da_igual_lo_que_pongas', null, null, 'MMMM y') }} {# antes #}
  {{ item.date|localizeddate_by_format('MMMM y') }} {# mejor esta no? #}</code></pre><h2>Grupos de argumentos</h2><p>En el caso en el que tenemos una funci&oacute;n con un grupo de argumentos relacionados entre s&iacute;, <strong>podemos agruparlos todos ellos en una clase</strong> de forma que nuestro c&oacute;digo mejora en su legibilidad. Es lo que han hecho aqu&iacute;.</p><p>&iquest;Que os parece si crearamos una clase para almacenar la configuraci&oacute;n de la localizaci&oacute;n <code>$locale</code>, <code>$timezone</code>, <code>$calendar</code>. Es muy probable que este grupo de argumentos se repitan en otras funciones.</p><pre><code class="php">class Localization
{
    private $locale = null;
    private $timezone = null;
    private $calendar = 'gregorian';

    public function __construct($locale, $timezone, string $calendar)
    {
        $this-&gt;locale = $locale;
        $this-&gt;timezone = $timezone;
        $this-&gt;calendar = $calendar;
    }

}</code></pre><p>Con esto mejoramos mucho la legibilidad del c&oacute;digo, veamos c&oacute;mo quedar&iacute;an nuestras funciones anteriores</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $date,
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $localization
) {
//..
}

function twig_localized_date_filter_by_format(
    Twig_Environment $env,
    $date,
    $customFormat = null,
    $localization
) {
//..
}</code></pre><h2>Orden de argumentos</h2><p>A partir de este punto todo lo que vamos a ver no aparece en el libro, pero si que hemos querido completarlo con algunos criterios adicionales. Deber&iacute;as tener en cuenta las siguientes condiciones a la hora de elegir el orden de los argumentos.</p><ul><li>Los m&aacute;s importantes primero.</li><li>Utiliza el contexto para que el lector sepa cual es el primer argumento.</li><li>Trata de agrupar por tipos.</li><li>Argumentos opcionales al final.</li></ul><h2>Nombres</h2><p>Los nombres de tus argumentos son parte de tu funci&oacute;n. Trata de que s&eacute;an lo m&aacute;s expec&iacute;ficos posibles, ayuda al lector a entender tu funci&oacute;n y lo que pueda esperar de ella, a trav&eacute;s de esos nombres. &iquest;Que te parece este cambio?</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    $dateTime, // $date no era fiel a la realidad, ya que en ese argumento no importa s&oacute;lo la fecha, si no tambi&eacute;n la hora.
    $dateFormat = 'medium',
    $timeFormat = 'medium',
    $localization
) {
//..
}</code></pre><h2>Tipado</h2><p>El &uacute;ltimo apartado que me gustar&iacute;a ver en esta entrada es el tipado. La regla es muy sencilla. Tipa siempre que puedas.</p><pre><code class="php">function twig_localized_date_filter(
    Twig_Environment $env,
    \DateTime $date,
    string $dateFormat = 'medium',
    string $timeFormat = 'medium',
    Localization $localization = null
) {
//..
}

function twig_localized_date_filter_by_format(
    Twig_Environment $env,
    \DateTime $date,
    string $customFormat,
    Localization $localization = null
) {
//..
}</code></pre><p>El tipado tambi&eacute;n forma parte de tu funci&oacute;n, quiz&aacute; no tengas claro que es exactamente <code>$localization</code>, pero el tipado te ayuda a entender que se trata de una clase.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-argumentos</guid><pubDate>Sat, 04 Jul 2020 09:56:47 +0200</pubDate></item><item><title>Código limpio: Funciones</title><link>https://devtia.com/post/codigo-limpio-funciones</link><description><![CDATA[<p>En esta entrada vamos a ver qu&eacute; caracter&iacute;sticas tienen o no tienen que tener <strong>nuestras funciones</strong> para que puedan considerase <strong>c&oacute;digo limpio</strong>. Para ello vamos a analizar el componente Lock de Symfony por ser un componente bastante sencillo de entender. Este componente crea y administra bloqueos, un mecanismo para proporcionar acceso exclusivo a un recurso compartido al m&aacute;s puro estilo de los sem&aacute;foros en el lenguaje de programaci&oacute;n c.</p><h2>Hacer una &uacute;nica cosa</h2><p>Una funci&oacute;n solo deber&iacute;a hacer una &uacute;nica cosa, y adem&aacute;s deber&iacute;a de ser ser capaz de hacerla bien. Este concepto a la vez que es sencillo de entender es bastante complejo de explicar, as&iacute; que vamos a definir algunas caracter&iacute;sticas que pueden indicarte que una de tus funciones hacen m&aacute;s de una cosa.</p><h3>Demasiado larga</h3><p>Si una funci&oacute;n tiene <strong>demasiadas l&iacute;neas de c&oacute;digo</strong>, es muy probable que est&eacute; haciendo varias cosas a la vez. Deber&iacute;as tratar de encontrar bloques dentro de esa misma funci&oacute;n que hagan una cosa concreta e ir extrayendolas en funciones m&aacute;s peque&ntilde;as.</p><p>El l&iacute;mite exacto para considerarse demasiado larga es un poco difuso, pero por encima de 20 l&iacute;neas deber&iacute;as al menos plantearte si tu funci&oacute;n es demasiado larga.</p><h3>Secciones</h3><p>Como continuaci&oacute;n a lo anterior, <strong>si una funci&oacute;n puede dividirse en secciones</strong>, nos encontramos ante un caso claro de que esa funci&oacute;n hace m&aacute;s de una cosa.</p><p>A continuaci&oacute;n vemos la funci&oacute;n <code>Lock::acquire</code></p><pre><code class="php">/**
 * {@inheritdoc}
 */
public function acquire(bool $blocking = false): bool
{
    try {
        if ($blocking) {
            if (!$this-&gt;store instanceof BlockingStoreInterface) {
                throw new NotSupportedException(sprintf('The store "%s" does not support blocking locks.', get_debug_type($this-&gt;store)));
            }
            $this-&gt;store-&gt;waitAndSave($this-&gt;key);
        } else {
            $this-&gt;store-&gt;save($this-&gt;key);
        }

        $this-&gt;dirty = true;
        $this-&gt;logger-&gt;info('Successfully acquired the "{resource}" lock.', ['resource' =&gt; $this-&gt;key]);

        if ($this-&gt;ttl) {
            $this-&gt;refresh();
        }

        if ($this-&gt;key-&gt;isExpired()) {
            try {
                $this-&gt;release();
            } catch (\Exception $e) {
                // swallow exception to not hide the original issue
            }
            throw new LockExpiredException(sprintf('Failed to store the "%s" lock.', $this-&gt;key));
        }

        return true;
    } catch (LockConflictedException $e) {
        $this-&gt;dirty = false;
        $this-&gt;logger-&gt;notice('Failed to acquire the "{resource}" lock. Someone else already acquired the lock.', ['resource' =&gt; $this-&gt;key]);

        if ($blocking) {
            throw $e;
        }

        return false;
    } catch (\Exception $e) {
        $this-&gt;logger-&gt;notice('Failed to acquire the "{resource}" lock.', ['resource' =&gt; $this-&gt;key, 'exception' =&gt; $e]);
        throw new LockAcquiringException(sprintf('Failed to acquire the "%s" lock.', $this-&gt;key), 0, $e);
    }
}</code></pre><p>Podemos observar 3 secciones principales. En la primera trata de adquirir el bloqueo, en la segunda comprueba si el bloqueo deber&iacute;a haber expirado y en el &uacute;ltimo bloque realiza el manejo de errores. Podr&iacute;amos facilitarle mucho la vida al lector si extrajeramos esos bloques dej&aacute;ndolos en algo similar a esto.</p><pre><code class="php">/**
 * {@inheritdoc}
 */
public function acquire(bool $blocking = false): bool
{
    try {
        $this-&gt;tryAcquire($blocking);
        $this-&gt;releaseIfExpired();

        return true;
    } catch (\Exception $e) {
        return $this-&gt;handleException($e);
    }
}</code></pre><h3>switch</h3><p>Un bloque switch es probablemente un indicador de que <strong>una funci&oacute;n hace varias cosas</strong>. La forma de solucionar esto es extraer ese bloque switch para que la &uacute;nica cosa que haga nuestra funci&oacute;n es decidir qu&eacute; hacer en funci&oacute;n del valor que le pasemos a switch.</p><p>A continuaci&oacute;n vemos un caso v&aacute;lido de un bloque switch dentro de la clase <code>Store\StoreFactory</code>, ya que aunque tiene un elemento switch la funci&oacute;n hace una &uacute;nica cosa, en este caso devolver la instancia de <code>Store\PersistingStoreInterface</code> correcta en funci&oacute;n del tipo de conexi&oacute;n que estemos utilizando.</p><pre><code class="php">/**
     * @param \Redis|\RedisArray|...
     *
     * @return PersistingStoreInterface
     */
    public static function createStore($connection)
    {
        if (!\is_string($connection) &amp;&amp; !\is_object($connection)) {
            throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be [...], "%s" given.');
        }

        switch (true) {
            case $connection instanceof \Redis:
            case $connection instanceof \RedisArray:
            case $connection instanceof \RedisCluster:
            case $connection instanceof \Predis\ClientInterface:
            case $connection instanceof RedisProxy:
            case $connection instanceof RedisClusterProxy:
                return new RedisStore($connection);
            // en total hay 32 casos diferentes.
        }

        throw new InvalidArgumentException(sprintf('Unsupported Connection: "%s".', $connection));
    }</code></pre><h2>Un s&oacute;lo nivel de abstracci&oacute;n</h2><p>Dentro de una misma funci&oacute;n debemos mantenernos siempre dentro del mismo nivel de abstracci&oacute;n.</p><p>Por ejemplo si tuvi&eacute;ramos una capa de dominio de negocio y una capa de persistencia de datos, dentro de una misma funci&oacute;n <strong>no deber&iacute;amos pasar de un nivel a otro</strong>, por lo cual tendr&iacute;amos funciones con l&oacute;gica de negocio y funciones con l&oacute;gica de persistencia de datos.</p><h2>Procesamiento de errores</h2><p><img alt="procesamiento de errores" loading="lazy" src="/cache/thumb_1200_0400/uploads/content/image/image/cacadb988f34f4c8084fcdecd5efc0ff23e25a7e.jpg" title="procesamiento de errores"></p><p>Empezaremos indicando que el procesamiento de errores es mucho m&aacute;s sencillo <strong>a trav&eacute;s de excepciones</strong> que de c&oacute;digos de error, pero esto lo veremos mucho m&aacute;s en profundidad en el cap&iacute;tulo de procesamiento de errores.</p><p>El problema que tienen los bloques <code>try / catch</code> es que dificultan mucho la lectura.Si tenemos una funci&oacute;n donde adem&aacute;s de la l&oacute;gica de la funci&oacute;n tiene uno o m&aacute;s bloques <code>try / catch</code>, probablemente deber&iacute;amos extraer la parte de la l&oacute;gica de negocio y dejar esa funci&oacute;n s&oacute;lamente con los bloques try catch tal y c&oacute;mo hicimos con la funci&oacute;n <code>Lock::acquire</code> en el ejemplo anterior.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-funciones</guid><pubDate>Wed, 03 Jun 2020 10:42:06 +0200</pubDate></item><item><title>Código limpio: Nombres</title><link>https://devtia.com/post/codigo-limpio-nombres</link><description><![CDATA[<p>En esta entrada vamos a entender y profundizar en la importancia de elegir buenos nombres. Es muy importante que te tomes el tiempo necesario para encontrar un buen nombre, y no debes tener ning&uacute;n miedo a cambiarlo si es que encuentras otro mejor.</p><h2>Evita el uso de contracciones</h2><p>Esta es la primera regla y la m&aacute;s sencilla de todas. Cuando programamos, podemos caer en la tentaci&oacute;n de crear peque&ntilde;as contracciones, dentro de la api de linux por ejemplo nos encontramos con un mont&oacute;n de ellas como por ejemplo <code>mv</code>, <code>cp</code>, <code>chmod</code> y muchas m&aacute;s.</p><p>Cuando escribes una contracci&oacute;n puede que en ese momento a t&iacute; te parezca obvia, pero es posible que para otra persona en otro contexto no lo sea tanto, as&iacute; que no merece la pena ahorrarse unos caracteres a cambio de los problemas que puedas generar en el futuro.</p><p>Adem&aacute;s esta regla te permitir&aacute; no entrar en conflicto con la siguiente que es la consistencia. Si usas una contracci&oacute;n en una variable o m&eacute;todo, &iquest;Por qu&eacute; no usarlo en todas las dem&aacute;s? &iquest;En cu&aacute;les s&iacute; y en cu&aacute;les no? Acabas de crear un problema donde no lo hab&iacute;a.</p><h2>S&eacute; consistente</h2><p><img alt="S&eacute; consistente" loading="lazy" src="/cache/thumb_1200_0400/uploads/content/image/image/17b93e4abdd2e6b2bed5b34fbe251abed368da8d.jpg" title="S&eacute; consistente"></p><p>Una de las caracter&iacute;sticas que vimos del c&oacute;digo limpio es que no es f&aacute;cil de mejorar. Eso significa que el c&oacute;digo es consistente. Si has decidido por ejemplo llamar <code>Lesson</code> a la entidad que representa a un profesor, una asignatura, una colecci&oacute;n de alumnos y un momento concreto en el tiempo, no deber&iacute;as utilizar ning&uacute;n tipo de sin&oacute;nimos.</p><p>Otro elemento que debes tener en cuenta a la hora de producir nombres consistentes es utilizar siempre los mismos verbos para una determinada tarea. Por ejemplo si has decidido que cuando accedas a un repositorio y recuperes un conjunto de lecciones vas a utilizar el verbo <code>get</code> generando nombres del tipo <code>getByUser()</code>, <code>getByState()</code>, <code>getByDate()</code> deber&iacute;as evitar usar sin&oacute;nimos del verbo <code>get</code>, como por ejemplo <code>fetchByUser()</code>, <code>findByState()</code>, <code>retrieveByDate()</code>.</p><h2>Utiliza el contexto</h2><p>Evita repetir conceptos que ya est&aacute;n claros a trav&eacute;s del contexto. Continuando con el ejemplo anterior, si est&aacute;s escribiendo un m&eacute;todo dentro de <code>LessonRepository</code> para buscar lecciones por su estado no necesitas indicar que est&aacute;s buscando lecciones <code>getLessonsByState</code>. Ya te encuentras dentro de <code>LessonRepository</code> as&iacute; que todos sus m&eacute;todos deber&iacute;an devolver por colecciones de lecciones. Debes evitar repetir "Lessons" en cada nombre, siendo <code>getByState()</code> m&aacute;s adecuado./p&gt;</p><p>Deber&iacute;as evitar usar nombres demasiado largos, as&iacute; como deber&iacute;as evitar usar nombres demasiado parecidos. Si te encuentras en una situaci&oacute;n en la que est&aacute;s escribiendo un nombre demasiado largo deber&iacute;as evaluar si puedes utilizar el contexto para generar un nombre m&aacute;s corto.</p><p>Por ejemplo si tienes la clase <code>UniversityLessonState</code> que contiene los estados posibles de una lecci&oacute;n de la universidad, podr&iacute;as generar un nuevo namespace del tipo <code>University\Lesson\State</code> obteniendo un nombre de clase mucho m&aacute;s corto y f&aacute;cil de leer, pero sin haber perdido ning&uacute;n tipo de informaci&oacute;n.</p><p>Si necesitas dos palabras para definir el nombre de una clase es probable que necesites crear un nuevo contexto, y si necesitas tres palabras definitivamente deber&iacute;as crear un contexto nuevo.</p><h2>Hace lo que dice que hace</h2><p><img alt="Hace lo que dice que hace" loading="lazy" src="/cache/thumb_1200_0400/uploads/content/image/image/6e032f2cbbc3defe991d6878c01e6298fa24ed2f.jpg" title="Hace lo que dice que hace"></p><p>Entramos en la parte final de las recomendaciones. Sin embargo estas dos &uacute;ltimas son las m&aacute;s importante de todas. Cuando eliges un nombre lo m&aacute;s importante es ser sincero. El nombre que est&aacute;s eligiendo tiene que servir para identificar qu&eacute; es lo que contiene.</p><p>En general debemos evitar nombres gen&eacute;ricos como <code>data</code>, <code>info</code>, <code>resume</code>, <code>items</code>. En algunos momentos muy concretos puede que tengan sentido, &iquest;pero que contienen exactamente?. Quiz&aacute; el autor no sab&iacute;a que nombre poner y eligi&oacute; uno de estos.</p><p>Si lo que quer&iacute;amos crear era por ejemplo un conjunto de lecciones filtradas por algunos criterios, podr&iacute;amos haber eleg&iacute;do <code>lessons</code> o <code>filteredLessons</code> al menos indica que contiene un conjunto de lecciones.</p><p>Debemos tambi&eacute;n evitar nombres que definan qu&eacute; es lo que necesita el autor en lugar de que es lo que hace un m&eacute;todo o clase. Por ejemplo <code>LessonRepository::getForDashboard</code>. Tenemos claro que el autor necesitaba una colecci&oacute;n de lecciones para mostrarlas en el dashboard, pero no tenemos ni idea de que criterios tiene en cuenta. Si el criterio para mostrar las lecciones en el dashboard fuera que pertenecieran a un determinado usuario, que estuvieran en un rango determinado de fechas, como por ejemplo los pr&oacute;ximos 15 d&iacute;as, y se encontraran en un determinado estado, podr&iacute;amos haber elegido <code>LessonRepository::getByUserFromToAndState</code>.</p><h2>Explica c&oacute;mo debe usarse</h2><p>Llegamos al &uacute;ltimo punto. Una vez que has encontrado el nombre adecuado que cumple todos los anteriores requisitos debes plantearte una &uacute;ltima pregunta.</p><p>&iquest;Explica mi nombre c&oacute;mo debe ser utilizado? Veamos un ejemplo supongamos que tenemos una clase que se encarga de realizar cambios sobre las lecciones <code>LessonManager::changeDate</code>, el contexto <code>LessonManager</code> nos indica que la entidad que va a modificar es una <code>Lesson</code> y el nombre nos revela que la va a cambiar de estado.</p><p>Por coherencia los argumentos deber&iacute;an ser <code>Lesson</code>, <code>Date</code> en este orden. Sin embargo podemos ayudar a evitar malentendidos, nombrando correctamente los argumentos. <code>LessonManager::changeDate($lesson, $toDate)</code> no deja lugar a ning&uacute;n tipo de equivocaci&oacute;n.</p><h2>Conclusiones</h2><p>Cuando escribimos c&oacute;digo limpio debemos tener en cuenta a la persona que lo va a leer, y cuando elegimos nombres debemos elegirlos pensando primero en esa persona en hacerle la vida lo m&aacute;s f&aacute;cil posible.</p>
]]></description><guid>https://devtia.com/post/codigo-limpio-nombres</guid><pubDate>Wed, 03 Jun 2020 10:39:13 +0200</pubDate></item></channel></rss>