Ejercicios (katas) para mejorar habilidades de programación practicando

Escrito por el .
blog-stack java software-libre planeta-codigo planeta-linux programacion
Comentarios

La semana pasada escribía sobre algunos libros que leer para ser mejores programadores, sin embargo, toda esa teoría solo sirve cuando se interioriza para usar más tarde en la práctica. Para interiorizar parte de ese conocimiento hasta el momento creo que no se ha inventado mejor manera que escribiendo código teniendo en cuenta esos principios.

Las katas y dojos son unos ejercicios que se realizan para practicar, son problemas sencillos de los que se conoce la solución pero lo importante no es resolverlos sino aplicar las lecciones aprendidas y mejorar nuestras habilidades de programación que posteriormente usemos en los proyectos que trabajamos. Estos ejercicios se suelen realizar con otras personas, en la página Katayunos - Merendojos se suelen organizar encuentros en algunas ciudades y fechas, si no nos cuadran las fechas y lugares podemos realizarlas individualmente cuando y donde prefiramos aunque una de las partes que nos perderemos es aprender de las habilidades y formas de trabajar de otras personas.

En la página CodeKata podemos leer una introducción a las katas y una colección de ejercicios con los que practicar. En estos ejercicios deberemos intentar aplicar varios principios de la programación orientada a objetos como el principio SOLID, DRY, abierto a extensión cerrado a modificación (OCP), patrones de diseño, nombres de métodos y variables que hagan que el código sea expresivo, teses unitarios, refactorizaciones, … todas esas cosas que consideramos correctas para escribir buen código.

El primero de los ejercicios propuestos en CodeKata es Kata01: Supermarket Pricing que consiste en pensar una forma de representar los precios de los productos de un supermercado, aparte de un precio simple como puede ser $0.65 por producto, otros como tres por un dolar, $1.99 / pound o compre dos obtenga uno más gratis o con descuento. Para esta kata en internet hay comentadas varias soluciones, probablemente para representar los diferentes precios una solución sea crear una clase que calcule los diferentes tipos de precios aplicando el patrón Strategy en función de como se calcule el precio de cada producto.

Intentando implementar en código una posible solución aplicando el patrón Strategy, usando BigDecimal para los precios (en vez de double y float que no pueden representar correctamente algunos valores decimales), este ejemplo muestra como calcular el precio de un producto dada su cantidad y su tipo de precio.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
package io.github.picodotdev.blogbitix.kata01;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Main {

    public interface Pricing {
        BigDecimal calculate(BigDecimal quantity);
    }

    public interface Offer {
        BigDecimal calculateWithOffer(BigDecimal quantity);

        BigDecimal getNumberWithoutOffer(BigDecimal quantity);
        BigDecimal getNumberWithOffer(BigDecimal quantity);
    }

    public static class Product {

        Pricing pricing;
        
        Product(Pricing pricing) {
            this.pricing = pricing;
        }    
        
        public BigDecimal calculate(BigDecimal quantity) {
            return pricing.calculate(quantity);
        }
    }

    public static class SimplePricing implements Pricing {

        BigDecimal unitPrice;

        SimplePricing(BigDecimal unitPrice) {
            this.unitPrice = unitPrice;
        }
        
        @Override
        public BigDecimal calculate(BigDecimal quantity) {
            return unitPrice.multiply(quantity);
        }
    }

    public static class WeightPricing implements Pricing {

        BigDecimal weightPrice;

        WeightPricing(BigDecimal weightPrice) {
            this.weightPrice = weightPrice;
        }
        
        @Override
        public BigDecimal calculate(BigDecimal weight) {
            return weightPrice.multiply(weight);
        }
    }

    public static class OfferPricing implements Pricing, Offer {

        Pricing normalPricing;
        BigDecimal offerQuantity;
        BigDecimal offerPercent;
        
        OfferPricing(Pricing normalPricing, BigDecimal offerQuantity, BigDecimal offerPercent) {
            this.normalPricing = normalPricing;
            this.offerQuantity = offerQuantity;
            this.offerPercent = offerPercent;
        }

        public BigDecimal calculate(BigDecimal quantity) {
            BigDecimal withoutOfferPrice = normalPricing.calculate(getNumberWithoutOffer(quantity));
            BigDecimal withOfferPrice = calculateWithOffer(getNumberWithOffer(quantity));
            
            return withoutOfferPrice.add(withOfferPrice);
        }
        
        public BigDecimal calculateWithOffer(BigDecimal quantity) {
            return normalPricing.calculate(new BigDecimal("1")).multiply(quantity).multiply(getOfferPercent());
        }
        
        public BigDecimal getNumberWithoutOffer(BigDecimal quantity) {
            BigDecimal groups = quantity.divide(offerQuantity, 0, RoundingMode.DOWN);
            return quantity.subtract(groups);
        }
        
        public BigDecimal getNumberWithOffer(BigDecimal quantity) {
            return quantity.subtract(getNumberWithoutOffer(quantity));
        }
        
        private BigDecimal getOfferPercent() {
            return BigDecimal.valueOf(100).subtract(offerPercent).divide(new BigDecimal("100"));
        }
    }
    
    public static void main(String[] args) {
        Product p1 = new Product(new SimplePricing(new BigDecimal("2")));
        System.out.println(p1.calculate(new BigDecimal("3")));

        Product p2 = new Product(new WeightPricing(new BigDecimal("1.35")));
        System.out.println(p2.calculate(new BigDecimal("3")));

        Product p3 = new Product(new OfferPricing(new SimplePricing(new BigDecimal("1")), new BigDecimal("3"), new BigDecimal("50")));
        System.out.println(p3.calculate(new BigDecimal("6")));
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.github.picodotdev.blogbitix.kata01;

import io.github.picodotdev.blogbitix.kata01.Main.OfferPricing;
import io.github.picodotdev.blogbitix.kata01.Main.Product;
import io.github.picodotdev.blogbitix.kata01.Main.SimplePricing;
import io.github.picodotdev.blogbitix.kata01.Main.WeightPricing;

import java.math.BigDecimal;

import org.junit.Assert;
import org.junit.Test;

public class MainTest {

    @Test
    public void simplePricing() {
        Product p1 = new Product(new SimplePricing(new BigDecimal("2")));
        Assert.assertEquals(new BigDecimal("6"), p1.calculate(new BigDecimal("3")));
    } 
    
    @Test
    public void weightPricing() {
        Product p2 = new Product(new WeightPricing(new BigDecimal("1.35")));
        Assert.assertEquals(new BigDecimal("4.05"), p2.calculate(new BigDecimal("3")));
    }

    @Test
    public void offerPricing() {
        Product p3 = new Product(new OfferPricing(new SimplePricing(new BigDecimal("1")), new BigDecimal("3"), new BigDecimal("50")));
        Assert.assertEquals(new BigDecimal("4.5"), p3.calculate(new BigDecimal("5")));
    }
}

Aun practicando estas katas no va a hacer que luego nuestro código en un proyecto real sea perfecto ni siquiera algo cercano a ello más bien hará que sea un poquito mejor, estos ejercicios son bastante simples que no tienen las complejidades de algunos casos reales, aún así siguen mereciendo realizarlos. También hay que tener en cuenta que no son realmente para aprender a programar aunque si pueden servir para aprender un nuevo lenguaje sobre todo si se hacen con otra persona que ya lo conoce y mientras se realiza la kata podemos preguntarle y nos resuelva las dudas que nos vayan surgiendo de la sintaxis, API o herramientas de ese lenguaje.