portrait

Keyvan Akbary

Developer at MyBuilder
Co-Founder of Funddy
Aspiring Craftsman
Living in London

Principio de Sustitución de Liskov

El Principio de Sustitución de Liskov, o LSP (Liskov Substitution Principle), corresponde a la sigla L dentro de los 5 principios SOLID para la programación orientada a objetos.

Los subtipos deberían poder ser reemplazables por sus tipos base

La importancia de este principio se hace evidente cuando pensamos en las consecuencias de violarla.

Piensa en que tenemos una función f que toma como argumento un tipo base B. Considera que el tipo B tiene un derivado D, que cuando se pasa a f se comporta de forma errónea. Es en este caso cuando podemos asegurar que D viola LSP.

Ejemplo de violación de LSP

Considera el siguiente código como parte de una aplicación

class Rectangle {
    private $width;
    private $height;

    public function setWidth($width) {
        $this->width = $width;
    }

    public function setHeight($height) {
        $this->height = $height;
    }

    public function getWidth() {
        return $this->width;
    }

    public function getHeight() {
        return $this->height;
    }

    public function area() {
        return $this->width * $this->height;
    }
}

Supongamos que el código funciona correctamente en diversos lugares de la aplicación. Alguien reclama una nueva funcionalidad, ahora existe la posibilidad de manipular cuadrados además de rectángulos.

A menudo en el mundo de la POO la herencia se interpreta a través de relaciones del tipo ES-UN. Miremos como lo miremos, un cuadrado matemáticamente es un rectángulo. No es descabellado llegar a la conclusión de que en nuestro modelo un cuadrado debe extender de rectángulo.

class Square extends Rectangle {
    //...
}

Sin embargo, esta forma de pensar puede conducirnos a grandes quebraderos de cabeza. En nuestro ejemplo, es fácil darnos cuenta de que Square no necesita de ambas propiedades $width o $height, nos bastaría con sencillo $size dado que el alto y ancho de un cuadrado se mantienen iguales. Imaginemos una aplicación con millones de estos objetos, un gasto significativo de memoria sin lugar a dudas.

Asumamos que por ahora no nos importa mucho el consumo de memoria. Al heredar de Rectangle heredamos inmediatamente sus métodos setWidth y setHeight que introducen un punto de incongruencia a nuestro código dado que no existe la posibilidad de tener ancho y alto diferentes. Sin embargo podemos esquivar el problema sobreescribiendo los métodos de la siguiente forma:

class Square extends Rectangle {
    public function setWidth($width) {
        parent::setWidth($width);
        parent::setHeight($width);
    }

    public function setHeight($height) {
        parent::setHeight($height);
        parent::setWidth($height);
    }
}

Ahora cuando alguien utilice las propiedades setWidth o setHeight se mantendrá consistente con la definición de cuadrado.

$square = new Square;
$square->setWidth(1);//width, height = 1
$square->setheight(2);//height, width = 2

El problema

Ahora que el código es consistente con la definición matemática de cuadrado, considera que pasamos un cuadrado a la siguiente función

function g(Rectangle $r) {
    $r->setWidth(5);
    $r->setHeight(4);
    assert($r->area() == 20);
}

Aunque el cuadrado sea consistente con su definición, no lo es respecto a todos los casos y usuarios. Este código es una clara violación del LSP.

ES-UN se trata de comportamiento

Entonces, ¿cual fué el problema? ¿acaso un cuadrado no es un rectángulo?

Desde el punto de vista del autor de la función g parece que no. Desde el punto de vista de g el comportamiento del objeto Square no es consistente con el comportamiento de un objeto Rectangle. Desde el punto de vista del comportamiento Square definitivamente no es un Rectangle.

Leer más

De que va esto del Software Craftsmanship

Me he tomado la libertad de traducir muy alegremente y con algunos cambios el artículo de Uncle Bob sobre este tema.


¿Y que es esto del movimiento software craftsmanship? ¿Que lo motiva? ¿Cual es el objetivo? Una cosa, solo una.

Estamos hasta los cojones de escribir mierda

Eso es todo. Sin medias tintas. Buenas noches. Cambio y corto.

Estamos cansados de escribir mal código. Estamos cansados de avergonzarnos de nosotros mismos ante nuestra empresa por escribir código de mierda. Hemos cubierto el cupo de llamadas a media noche a nuestros clientes para reiniciar los sistemas. No queremos listas de bugs infinitas. No queremos contribuir a enmarañar y ofuscar el código aún más. Estamos cansados de hacer un trabajo mediocre. Queremos empezar a hacerlo bien.

Eso... es... todo... Nada más.

Que es lo que no estamos haciendo:

  • No situamos al código en el centro de todo
  • No le damos la espalda al negocio o al cliente
  • No nos miramos el ombligo
  • No ofrecemos certificaciones baratas
  • No nos olvidamos de que nuestro trabajo es deleitar a nuestros clientes

Que es lo que no vamos a hacer más:

  • No vamos a hacer líos con la planificación
  • No vamos a aceptar la estúpida mentira de “lo arreglaré luego”
  • No creemos en “rápido” como sinónimo de “sucio”
  • No aceptamos la opción de hacerlo mal
  • No permitimos que nadie nos fuerce a comportarnos de forma no profesional

Que es lo que vamos a hacer a partir de ahora:

  • Vamos a cumplir con la planificación sabiendo que la única forma de ir rápido es haciendolo bien
  • Vamos a deleitar a nuestros clientes escribiendo el mejor código que podamos
  • Vamos a honrar a nuestras empresas con los mejores diseños que podamos
  • Vamos a honrar a nuestro equipo testeando todo lo que se pueda testear
  • Vamos a ser humildes, escribiremos los test primero
  • Vamos a practicar para ser mejores

Recordemos lo que nuestros queridos abuelos nos decían:

Todo lo que vale la pena, vale la pena hacerlo bien

Vísteme despacio, que tengo prisa

Mide dos veces, corta solo una

Practica, practica, practica

Algunos nos miran escépticos ante nuestras katas, code retreats o sesiones prácticas. Habrá quien piense que esto va únicamente en nuestro propio beneficio y en detrimento del cliente. Es posible que crean que hemos abandonado el mundo real con el único fin de entretenernos. Los hay que llegan a estas conclusiones continuamente.

Conclusiones tan erróneas como que el día es infinito. Estamos haciendo esto precisamente porque nos preocupamos por el cliente. Estamos dedicando tiempo y esfuerzo a hacer el mejor trabajo posible para que las empresas donde trabajamos puedan sacarnos el máximo provecho.

¿Piensas que los músicos solo tocan instrumentos cuando están sobre el escenario? ¿Piensas que los futbolistas juegan al futbol únicamente cuando juegan un partido de liga? ¿Acaso piensas que los abogados echan el cierre cuando acaba el juicio? Obviamente no. Todas estas personas son profesionales, los profesionales practican. Los profesionales se las saben todas. Conocen la historia, las teorías y anécdotas. Conocen las técnicas y métodos. Conocen las buenas y malas opciones, precisamente saben como evitar las malas. Y todo ello lo saben porque practican, practican y practican.

Así que, cuando veas a alguien que lleve una pulsera verde en la que se lea “Clean Code” o “Test First” o “Test Obsessed”, no es porque formen parte de una secta, hayan firmado un manifiesto o simplemente se crean superiores a los demás. No son estandartes en una guerra santa. No están intentando unirse a una tribu con la que hacer un corro alrededor de una fogata. La pulsera es una elección personal. Es una promesa hecha a uno mismo: “Voy a hacer un buen trabajo. No tengo prisa. Escribiré tests. Iré rápido a base de hacerlo bien. No escribiré mierda. Voy a practicar, practicar y practicar porque eso es lo que hace un profesional”

Leer más

Principio de responsabilidad única

El principio de responsabilidad única es el primer principio del acrónimo SOLID para la programación Orientada a Objetos.

Un módulo o una función debe tener una y solo una responsabilidad, o lo que es lo mismo, debe tener una y solo una razón para cambiar.

Más de una responsabilidad hace que el código sea dificil de leer, de testear y mantener. Es decir, hace que el código sea menos flexible, más rígido.

 ¿Y qué es una responsabilidad?

Se trata de la audiencia de un determinado módulo o función, actores que reclaman cambios al software. Las responsabilidades son básicamente familias de funciones que cumplen las necesidades de dichos actores.

En el siguiente ejemplo podemos identificar hasta 3 responsabilidades diferentes

class Employee
{
    public function calculatePay() {  } //Policy
    public function save() {  } //Architecture
    public function describeEmployee() {  } //Operations
}
  1. Política de cálculos de salario
  2. Lógica de persitencia en la base de datos
  3. Cómputo de horas de trabajo

Cuando alguno de los roles involucrados en dichas responsabilidades decida cambiar o agregar funcionalidad, va a tener que cambiar dicha clase. Aumentando la probabilidad de colisión, complejidad y posibles bugs.

Los dos valores del software

El valor secundario del software, aquel que tendemos a pensar que és el más importante, es el de comportamiento. Si el software se comporta como debería y no posee bugs este valor se mantiene alto.

Por desgracia, las necesidades cambian y los usuarios también, es por eso que el primer valor del software es la facilidad al cambio. Es más importante permitir que el software sea lo suficientemente flexible como para poder adaptarse a nuevos usuarios y necesidades que satisifacerlas en un momento concreto. Sin este valor, el secundario es dificil de acometer a largo plazo.

El principio de reponsabilidad única permite que nuestro código sea más flexible al cambio y por tanto nos ayuda a mantener el primer valor del software, el de la facilidad al cambio, alto.

Leer más

¡No comentes tu código!

Every time you write a comment, you should grimace and feel the failure of your ability of expression - Robert C. Martin

La causa mas común detrás de un comentario es un código poco expresivo y pobre. Limpiar tu código siempre te va a llevar menos tiempo que comentar lo que hace para que se entienda.

Explícalo con código

En algunas ocasiones es difícil comunicar a través del código. Por desgracia, muchos programadores han llegado a la conclusión de que la única manera de solventarlo es comentándolo. Este hecho es completamente falso, como puedes ver ¿prefieres esto?

// Is a teenager
if ($user->getAge() > 12 && $user->getAge() < 20)

¿O esto?

if ($user->isTeenager())

Es mucho mas fácil e intuitivo expresar tu intención con código. En la mayoría de los casos basta con crear una función con un nombre suficientemente descriptivo.

Malos comentarios

Por lo general, todos los comentarios entran dentro de esta categoría. La mayor parte son monólogos por parte del programador hacia si mismo.

Ruido

// The name
private $name;

// The birth date;
private $birthDate;

// Default constructor
public function __construct()

Algunas veces verás comentarios que no son otra cosa que ruido. Resaltan lo obvio y no proporcionan información útil.

PHPDoc en APIs no públicas

/**
 * Sums two numbers
 * @param int num1
 * @param int num2
 * @return int
 */
function sum($num1, $num2)

Generar PHPDoc para clases y métodos internos a un sistema no es útil, además el formato PHPDoc es engorroso y hace poco más que añadir distracción.

Código comentado

Pocas prácticas son peores que comentar código. Simplemente no lo hagas.

$pos = count($array);
//$pos += 1;
if ($pos > 30) {
    //$result = $pos + 2;
    $result = $pos + 3;
}

Algunos leerán el código y no tendrán el coraje de borrarlo - si está ahí será por algo ¿no? - Gracias al control de versiones puedes recuperar el código así que no tengas miedo a eliminarlo.

Marcas de posición

Algunas veces a los programadores les gusta marcar ciertas posiciones del código.

// Utility functions ///////////

Muy pocas veces tiene sentido utilizarlas, especialmente los últimos caracteres. Son un tipo de ruido que normalmente se ignora.

Atribuciones

// By Manolo

El control de versiones ya ofrece un historial suficientemente detallado como para averiguar el autor de cada pieza de código. No es necesario indicarlo con comentarios.

Auto-completado en IDE

/** @var Domain/Entities/User */
private $user;

Que tu IDE preferido determine el tipo de los objetos gracias a estos comentarios no lo hace útil para el resto de tus compañeros. Hay que mantenerlos y en un futuro es muy posible que mientan y confundan sobre su propósito.

Registro de cambios

Antiguamente se podía tener constancia de los cambios en un sistema gracias a un registro en los comentarios y era una práctica útil.

/*
 * Changes (from 30-Aug-2013)
 * --------------------------
 * 30-Aug-2013 : Added a description for the footer
 * 17-Aug-2013 : Removed UserFactory
 * 05-Aug-2013 : Added Facebook login
 */

Hoy en día con el control de versiones ha dejado de tener sentido.

Anotaciones

/**
 * @Template("BlogBundle:Blog:home.html.twig")
 */
public function homeAction() {}

Debido a que son un tipo de comentario con comportamiento asociado (magia), crean mas problemas de los que solucionan.

Comentarios aceptables

En casos muy concretos algunos comentarios son beneficiosos o incluso necesarios. Ten siempre presente que el único comentario realmente útil es aquel que no escribes.

Comentarios legales

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

Este tipo de comentarios a veces son necesarios por motivos de licencia. Generalmente los editores modernos suelen colapsarlos y es fácil ignorarlos.

Comentarios informativos

private $time = 1000; // In seconds

En contadas ocasiones es necesario aclarar el código, comentar justo las partes que no son suficientemente claras esta justificado.

Leer más

¡No uses anotaciones!

Si utilizas un ORM como Doctrine 2 te habrás percatado que utilizar anotaciones para mapear entidades es una practica sorprendentemente popular:

/** @Document */
class User
{
    /** @Id */
    private $id;

    /** @Field(type="string") */
    private $name;
}

También el de utilizarlas como configuración en tus controladores en Symfony 2:

/**
 * @Route("/")
 * @Template("BlogBundle:Blog:home.html.twig")
 */
public function homeAction() {}

Utilizar anotaciones es una mala idea

Las anotaciones son algo mágico. La magia en el software es mala por definición, lo es porque no sabemos como funciona exactamente. Son un foco de malentendidos, provocan inesperados side effects y dependen de interpretes de terceros.

Depurarlas no es sencillo

Debido a que no son nativas del lenguaje y funcionan de una forma poco habitual, requieren de intérpretes que normalmente bifurcan o interrumpen el flujo natural de tu código para ejecutarlas. Seguir la ejecución es cuanto menos una odisea.

Contaminan tu dominio

La capa de dominio debe ser agnóstica a los detalles. Así pues, persistir o no tus entidades de dominio sobre una base de datos o mantenerlas en memoria es un detalle de infraestructura en que el dominio no es partícipe. Incrustar anotaciones (configuración) en tu dominio es de las peores aberraciones que puedes cometer, hacen que tu dominio sea frágil y rígido y no permiten una clara separación en capas.

Dificultan la lectura

No nos olvidemos, las anotaciones son comentarios y si estos ya de por si son una fuente de problemas, el hecho de que ejecuten cosas debería hacer saltar todas las alarmas. Las anotaciones se mezclan con tu código, entorpeciendo su lectura y convirtiéndolo en un popurrí indescifrable.

Alternativas

Dado que detrás de las anotaciones, en algún lugar recóndito, hay código respaldándolas, siempre va a haber una forma de evitarlas.

Por ejemplo, en Symfony 2 liberar a tus controlador de la configuración de enrutado es tan sencillo como crear un fichero YAML:

home:
    pattern: /
    defaults: { _controller: Bundle:Controller:home }

De la misma forma, Doctrine 2 permite separar tu dominio de los detalles de persistencia a través de ficheros de mapeo:

Documents\User:
    db: documents
    collection: user
    fields:
        id:
            id: true
        name:
            type: string

¡Mucho mas claro! A la larga te aseguro que lo agradecerás.

Leer más

Copyright © 2014