Més enllà de SOLID, existeix un conjunt de principis pragmàtics que actuen com a brúixola en el dia a dia del disseny. DRY ens recorda no repetir coneixement; KISS advoca per la simplicitat; YAGNI ens frena de construir el que encara no necessitem. A aquests s'hi sumen el principi de mínima sorpresa i la preferència per la composició enfront de l'herència. Aquests principis són aparentment senzills, però la seva aplicació equilibrada distingeix un dissenyador experimentat: cadascun estira en una direcció i saber quan aplicar-los —i quan no— és una habilitat arquitectònica clau. En aquesta lliçó els estudiarem amb exemples pràctics en Java.

Contingut

  1. DRY: no et repeteixis
  2. KISS: fes-ho simple
  3. YAGNI: no ho necessitaràs
  4. La tensió entre DRY, KISS i YAGNI
  5. Principi de mínima sorpresa
  6. Composició sobre herència
  7. Altres principis útils
  8. Errors comuns i consells
  9. Exercicis
  10. Conclusió

  1. DRY: no et repeteixis

DRY (Don't Repeat Yourself) estableix que cada peça de coneixement ha de tenir una representació única, inequívoca i autoritzada dins del sistema. El matís important és que DRY tracta de coneixement, no de coincidències textuals de codi.

// VIOLACIÓ de DRY: la regla de l'IVA (21%) està duplicada i dispersa
public class Carrito {
    public double total(double base) { return base + base * 0.21; }
}
public class Factura {
    public double importe(double base) { return base + base * 0.21; }
}

Explicació del problema: la regla "l'IVA és el 21%" viu en dos llocs. Si canvia el tipus impositiu, cal trobar i modificar totes les còpies, amb risc d'oblidar-ne alguna.

// CORRECCIÓ: una única font de veritat
public class PoliticaImpuestos {
    private static final double IVA = 0.21;
    public double aplicar(double base) { return base + base * IVA; }
}
public class Carrito {
    private final PoliticaImpuestos impuestos;
    public Carrito(PoliticaImpuestos impuestos) { this.impuestos = impuestos; }
    public double total(double base) { return impuestos.aplicar(base); }
}

Compte amb el DRY fals: dos fragments de codi poden semblar idèntics avui per casualitat però representar coneixements diferents que evolucionaran per separat. Forçar-ne la unificació crea un acoblament nociu. La regla és: unifica coneixement duplicat, no codi coincident.

  1. KISS: fes-ho simple

KISS (Keep It Simple, Stupid) recomana triar sempre la solució més senzilla que resolgui el problema. La complexitat ha d'estar justificada per un requisit real.

// VIOLACIÓ de KISS: complexitat innecessària per comprovar si és parell
public boolean esPar(int n) {
    String binario = Integer.toBinaryString(n);
    char ultimo = binario.charAt(binario.length() - 1);
    return ultimo == '0';
}

Explicació del problema: converteix a binari, manipula caràcters... tot per a una cosa que té una expressió trivial i directa.

// CORRECCIÓ: la solució òbvia
public boolean esPar(int n) {
    return n % 2 == 0;
}

KISS també s'aplica a l'arquitectura: no introdueixis microserveis, cues de missatges o capes d'abstracció si un monòlit modular ben organitzat resol el problema. La complexitat accidental és deute futur.

  1. YAGNI: no ho necessitaràs

YAGNI (You Aren't Gonna Need It) adverteix contra implementar funcionalitat per anticipat "perquè algun dia farà falta". La majoria d'aquestes prediccions fallen, i el codi especulatiu afegeix cost de manteniment sense aportar valor.

// VIOLACIÓ de YAGNI: parametrització especulativa
public class ExportadorCsv {
    // Ningú no ha demanat altres separadors, codificacions ni compressió
    public String exportar(List<String> filas, char separador,
                           String codificacion, boolean comprimir,
                           boolean incluirCabecera, String pieDePagina) {
        // ... lògica enorme que gestiona combinacions que mai no es fan servir
        return "";
    }
}

Explicació del problema: s'han afegit sis paràmetres per a escenaris hipotètics. Cadascun s'ha de provar, documentar i mantenir, encara que només es faci servir la coma.

// CORRECCIÓ: resol només el cas real d'avui
public class ExportadorCsv {
    public String exportar(List<String> filas) {
        return String.join("\n", filas); // separador per comes dins de cada fila
    }
}

Si demà es necessita un altre separador, s'afegeix aleshores, amb el requisit real al davant. YAGNI no és excusa per a un mal disseny; és una invitació a no resoldre problemes que encara no existeixen.

  1. La tensió entre DRY, KISS i YAGNI

Aquests principis de vegades s'oposen, i l'art rau a equilibrar-los:

Situació Principi que estira Principi en tensió Criteri de decisió
Veig codi repetit DRY (unificar) KISS/YAGNI (no abstreure encara) És el mateix coneixement o coincidència?
Vull una abstracció genèrica DRY YAGNI Hi ha 2-3 casos reals o un de sol?
El disseny es torna complex KISS DRY La simplicitat justifica una mica de duplicació?

Una heurística pràctica molt citada és la regla de tres: la primera vegada escrius el codi; la segona vegada que apareix quelcom semblant, ho toleres; la tercera vegada, abstreus. Això concilia DRY amb YAGNI: esperes a tenir evidència real d'un patró abans d'unificar-lo.

  1. Principi de mínima sorpresa

El principi de mínima sorpresa (Principle of Least Astonishment) diu que un component ha de comportar-se com un usuari raonable espera. El codi no ha d'amagar efectes secundaris sorprenents ni trencar convencions.

// VIOLACIÓ: un getter amb efectes secundaris sorprenents
public class Cuenta {
    private double saldo;
    public double getSaldo() {
        registrarAcceso();   // sorpresa: un getter que escriu
        saldo -= 1;          // sorpresa: consultar cobra comissió
        return saldo;
    }
}

Explicació del problema: ningú no espera que getSaldo() modifiqui l'estat ni cobri comissions. Qui el faci servir s'endurà una sorpresa difícil de depurar.

// CORRECCIÓ: noms honestos i comportament predictible
public class Cuenta {
    private double saldo;
    public double getSaldo() { return saldo; } // només consulta
    public double consultarConComision() {       // intenció explícita
        saldo -= 1;
        return saldo;
    }
}

Aplicat a APIs: respecta convencions de noms, retorna tipus esperats, llança les excepcions documentades i evita comportaments ocults.

  1. Composició sobre herència

L'herència crea un acoblament fort i estàtic entre classes. La composició —construir objectes combinant-ne d'altres— sol ser més flexible.

// PROBLEMÀTIC: herència rígida i explosió de subclasses
public class Pajaro {
    public void volar() { /* ... */ }
}
public class Pinguino extends Pajaro {
    // els pingüins no volen! Herència mal aplicada
    public void volar() { throw new UnsupportedOperationException(); }
}

Explicació del problema: heretar volar() obliga el pingüí a tenir un comportament que no li correspon. A més, combinar capacitats (neda, vola, corre) per herència provoca una explosió de subclasses.

// MILLOR: compondre comportaments
public interface Desplazamiento { void mover(); }
public class Vuelo implements Desplazamiento {
    public void mover() { System.out.println("Vuela"); }
}
public class Nado implements Desplazamiento {
    public void mover() { System.out.println("Nada"); }
}
public class Animal {
    private final Desplazamiento desplazamiento; // es compon, no s'hereta
    public Animal(Desplazamiento desplazamiento) { this.desplazamiento = desplazamiento; }
    public void mover() { desplazamiento.mover(); }
}
// Pinguino = new Animal(new Nado()); Aguila = new Animal(new Vuelo());

Explicació de la millora: el comportament s'injecta com un objecte. Es pot canviar en temps d'execució i combinar lliurement. Això és la base del patró Strategy. La regla "afavoreix la composició sobre l'herència" prové del llibre dels patrons de disseny (GoF) i evita jerarquies fràgils.

Aspecte Herència Composició
Acoblament Fort, estàtic Feble, dinàmic
Reutilització Limitada a la jerarquia Flexible i combinable
Canvi en runtime No
Risc Trencar LSP, jerarquies profundes Més objectes a orquestrar

  1. Altres principis útils

  • Separació comanda-consulta (CQS): un mètode o bé canvia estat (comanda) o bé retorna dades (consulta), però no totes dues coses. Reforça la mínima sorpresa.
  • Fail-fast: detectar i reportar errors com més aviat millor (validar paràmetres en entrar) en lloc de propagar estats invàlids.
  • Encapsulació: amagar l'estat intern i exposar només comportament; base del baix acoblament.
  • Alta cohesió / baix acoblament: ja vistos a la lliçó anterior, són el teló de fons de tots aquests principis.
  • Boy Scout Rule: deixa el codi una mica més net del que el vas trobar; manteniment incremental que combat el deute tècnic.

Errors Comuns i Consells

  • Aplicar DRY de manera dogmàtica i crear abstraccions prematures que acoblen coneixements diferents. Recorda: la duplicació és més barata que l'abstracció equivocada.
  • Confondre KISS amb simplista. Simple no és fer menys del que cal; és no fer de més. Un sistema massa simplista que no cobreix els requisits també és un error.
  • Fer servir YAGNI com a excusa per no dissenyar. YAGNI rebutja funcionalitat especulativa, no el bon disseny base ni l'extensibilitat raonable.
  • Heretar per reutilitzar línies de codi. Si la relació no és un "és-un" genuí, fes servir la composició.
  • Sorpreses en els noms: un mètode anomenat validar() que a més guarda a base de dades viola la mínima sorpresa. Anomena segons el que realment fa.
  • Consell: fes servir la regla de tres com a àrbitre entre DRY i YAGNI. I mesura: si una abstracció té un sol ús real, probablement sobra.

Exercicis

Exercici 1 (DRY). Detecta la duplicació de coneixement i corregeix-la:

class Empleado { double bono(double s) { return s * 0.1; } }
class Directivo { double bono(double s) { return s * 0.1; } }

Exercici 2 (KISS/YAGNI). Simplifica aquest codi aplicant KISS i YAGNI:

public String saludar(String nombre, boolean formal, boolean mayusculas,
                      String idioma, boolean conEmoji) {
    String saludo = formal ? "Estimado " : "Hola ";
    saludo += nombre;
    if (mayusculas) saludo = saludo.toUpperCase();
    if (conEmoji) saludo += " :)";
    return saludo; // només es fa servir saludar(nombre)
}

Exercici 3 (Composició). Converteix aquesta jerarquia d'herència en composició:

class Coche { void arrancar() {} }
class CocheElectrico extends Coche { void cargar() {} }
class CocheGasolina extends Coche { void repostar() {} }

Solucions

Solució 1. El càlcul del bo és el mateix coneixement; el centralitzem:

class PoliticaBonos {
    private static final double TASA = 0.1;
    double calcular(double salario) { return salario * TASA; }
}
// Empleado i Directivo reben/fan servir PoliticaBonos en lloc de duplicar la regla.

Solució 2. Eliminem els paràmetres especulatius no utilitzats:

public String saludar(String nombre) {
    return "Hola " + nombre;
}

Si en el futur cal el mode formal o els idiomes, s'afegeixen amb el requisit real.

Solució 3. Modelem el tipus d'energia com un component:

interface FuenteEnergia { void recargar(); }
class Electrica implements FuenteEnergia { public void recargar() { /* carregar */ } }
class Combustion implements FuenteEnergia { public void recargar() { /* repostar */ } }

class Coche {
    private final FuenteEnergia energia;
    Coche(FuenteEnergia energia) { this.energia = energia; }
    void arrancar() {}
    void recargar() { energia.recargar(); }
}
// new Coche(new Electrica());  new Coche(new Combustion());

Conclusió

DRY, KISS i YAGNI formen un trio que regula l'equilibri entre rigor, simplicitat i pragmatisme: no repetir coneixement, mantenir la senzillesa i no construir de més. Els hem complementat amb el principi de mínima sorpresa, que exigeix comportament predictible, i amb la preferència per la composició sobre l'herència, que evita jerarquies fràgils. Aplicats amb judici —i arbitrats per heurístiques com la regla de tres— aquests principis redueixen la complexitat accidental. A la lliçó següent farem un salt del disseny de classes al disseny del sistema, tot estudiant les tàctiques arquitectòniques amb què assolim atributs de qualitat com ara la disponibilitat, el rendiment o la seguretat.

Curs d'Arquitectura d'Aplicacions

Mòdul 1: Fonaments de l'Arquitectura d'Aplicacions

Mòdul 2: Principis i Tàctiques de Disseny

Mòdul 3: Estils i Patrons Arquitectònics

Mòdul 4: Arquitectures Distribuïdes i Microserveis

Mòdul 5: Arquitectures Dirigides per Esdeveniments i Missatgeria

Mòdul 6: Disseny Dirigit pel Domini (DDD)

Mòdul 7: Dades i Persistència

Mòdul 8: Arquitectura al Núvol i Desplegament

Mòdul 9: Qualitat, Seguretat i Observabilitat

Mòdul 10: Evolució, Governança i Casos Pràctics

© Copyright 2026. Tots els drets reservats