Les quatre lliçons anteriors d'aquest mòdul van donar a BiblioTech un estil consistent, un vocabulari de patrons de disseny, dependències desacoblades mitjançant interfícies, i una bateria de proves unitàries que verifiquen el seu comportament sense necessitar una base de dades real. Aquesta última lliçó del Mòdul 8 tanca el cercle: ensenya què busca una bona revisió de codi, cataloga els code smells (senyals d'un disseny que pot millorar) més comuns, i refactoritza un mètode de Biblioteca que barreja diverses responsabilitats —recolzant-se, precisament, en les proves unitàries de la lliçó anterior com a xarxa de seguretat—. Amb això, BiblioTech arriba al Mòdul 9 (Projecte Final) amb un codi no només funcional, sinó revisat, provat i depurat segons tot el que s'ha vist en aquest mòdul.

Contingut

  1. Què busca una bona revisió de codi
  2. Checklist de revisió
  3. Code smells comuns
  4. Refactorització: extreure mètode i extreure classe
  5. Les proves unitàries com a xarxa de seguretat abans de refactoritzar
  6. Exemple: refactoritzant un mètode llarg de Biblioteca
  7. Tancament del Mòdul 8 i enllaç amb el Projecte Final

  1. Què busca una bona revisió de codi

Una revisió de codi (code review) és el procés pel qual una altra persona (o, en un projecte individual, un mateix amb perspectiva fresca) llegeix un canvi abans d'incorporar-lo definitivament al projecte, buscant problemes que l'autor original, immers en el detall, pot no haver vist. Una bona revisió no busca imposar gustos personals d'estil —això ja ho automatitza EditorConfig (Lliçó 1)—, sinó verificar quatre aspectes concrets:

Aspecte Pregunta que es fa el revisor
Llegibilitat S'entén què fa aquest codi sense haver-lo d'executar mentalment pas a pas?
Proves Existeixen proves unitàries (Lliçó 4) que cobreixin el comportament nou o modificat?
Adherència als estàndards Segueix les convencions de nomenclatura, documentació i nul·labilitat ja establertes (Lliçó 1)?
Disseny Alguna classe o mètode ha crescut fins a barrejar responsabilitats que s'haurien de separar (Lliçó 2, responsabilitat única)?

Una revisió centrada en aquests quatre aspectes, i no en preferències personals d'estil, és més ràpida, més objectiva, i genera menys fricció entre qui escriu el codi i qui el revisa.

  1. Checklist de revisió

Una checklist concreta ajuda al fet que una revisió no depengui només de la intuïció del revisor. Una checklist raonable per a un canvi a BiblioTech:

  • [ ] El nom de cada mètode i variable nous segueix les convencions de la Lliçó 1 (PascalCase/camelCase, prefixos I/_, sufix Async)?
  • [ ] Hi ha algun mètode que faci més d'una cosa clarament separable (validar, persistir, notificar...)?
  • [ ] Les dependències externes (persistència, serveis) es reben injectades, o es creen directament amb new dins de la classe (Lliçó 3)?
  • [ ] El canvi inclou proves unitàries noves, o modifica proves existents que ja no reflecteixen el comportament nou (Lliçó 4)?
  • [ ] La nul·labilitat (?) reflecteix fidelment què pot retornar null i què no?
  • [ ] Els comentaris expliquen decisions no evidents, o només repeteixen el que el codi ja diu (Lliçó 1)?

Aquesta llista no és exhaustiva ni universal —cada equip ajusta la seva amb el temps—, però serveix com a punt de partida concret, més útil que una revisió sense cap criteri explícit.

  1. Code smells comuns

Un code smell (literalment, "olor de codi") és una senyal superficial en el codi que suggereix, sense ser en si mateixa un error, que el disseny subjacent es podria millorar. Quatre code smells especialment comuns:

Code smell Com es reconeix Per què és un problema
Mètode llarg Un mètode de desenes de línies, amb diversos blocs clarament separables Difícil de llegir d'un sol cop, difícil de provar de forma aïllada (cada bloc necessitaria la seva pròpia prova)
Duplicació El mateix fragment de lògica, copiat (potser amb petites variacions) en diversos llocs Un canvi d'aquesta lògica obliga a recordar actualitzar cada còpia; és fàcil oblidar-ne alguna
Classe amb massa responsabilitats Una classe que barreja, per exemple, lògica de domini, persistència i presentació alhora Viola el principi de responsabilitat única (Lliçó 1); canviar una responsabilitat arrisca trencar les altres sense relació
Paràmetres excessius Un mètode amb sis o més paràmetres, diversos d'ells relacionats entre si Suggereix que aquests paràmetres s'haurien d'agrupar en un objecte propi (per exemple, un record, Mòdul 3)

Cap d'aquests smells és, per si sol, un error que impedeixi compilar o executar el programa —d'aquí que s'anomenin "olors" i no "errors"—; són senyals que convé investigar, no regles absolutes que obliguin sempre a refactoritzar de seguida.

  1. Refactorització: extreure mètode i extreure classe

Refactoritzar significa canviar l'estructura interna del codi sense canviar el seu comportament observable: el programa continua fent exactament el mateix des de fora, però el seu codi intern queda millor organitzat. Dues tècniques de refactorització cobreixen la majoria dels smells de l'apartat anterior:

  • Extreure mètode: agafar un fragment d'un mètode llarg i convertir-lo en un mètode propi, amb un nom que expliqui què fa aquest fragment. Resol directament el smell de "mètode llarg".
  • Extreure classe: quan una classe barreja diverses responsabilitats (el smell de "massa responsabilitats"), moure part dels seus membres a una classe nova dedicada a aquesta responsabilitat —exactament el que ja va fer la Lliçó 3 en extreure IRepositoriBiblioteca i les seves implementacions fora de Biblioteca.
// Abans d'"extreure metode": un fragment de logica de format barrejat amb una altra logica
Console.WriteLine($"Prestec #{prestec.Llibre.Titol}: prestat el {prestec.DataPrestec:dd/MM/yyyy}" +
    (prestec.DataDevolucio is not null ? $", retornat el {prestec.DataDevolucio:dd/MM/yyyy}" : ", en curs"));

// Despres d'"extreure metode": el fragment te ara un nom propi
string DescriurePrestec(Prestec prestec)
{
    string estat = prestec.DataDevolucio is not null
        ? $"retornat el {prestec.DataDevolucio:dd/MM/yyyy}"
        : "en curs";

    return $"Prestec #{prestec.Llibre.Titol}: prestat el {prestec.DataPrestec:dd/MM/yyyy}, {estat}";
}

Console.WriteLine(DescriurePrestec(prestec));

DescriurePrestec no canvia el missatge mostrat per consola —el comportament observable és idèntic—, però ara té un nom que explica el seu propòsit, i pot reutilitzar-se a qualsevol altre punt del programa que necessiti el mateix format, sense duplicar la lògica.

  1. Les proves unitàries com a xarxa de seguretat abans de refactoritzar

La pregunta que hauria de sorgir sempre abans de refactoritzar és: com sé que no he trencat res? Sense proves, l'única resposta és "executant l'aplicació sencera a mà i confiant a no haver passat res per alt" —lent, i poc fiable. Amb les proves unitàries de la Lliçó 4 ja escrites sobre Prestec.RegistrarDevolucio() i Biblioteca.PrestarLlibreAsync, refactoritzar deixa de ser un salt de fe:

flowchart LR
    A["Proves existents en verd"] --> B["Refactoritzar el codi intern"]
    B --> C{"Les proves segueixen en verd?"}
    C -->|"Si"| D["El comportament no ha canviat: refactoritzacio segura"]
    C -->|"No"| E["Alguna cosa ha canviat sense voler: revisar abans de continuar"]

Aquest és el paper exacte de les proves unitàries com a xarxa de seguretat: no impedeixen cometre un error en refactoritzar, però el detecten de seguida —en segons, en tornar a executar el mateix conjunt de proves— en comptes de descobrir-ho molt més tard, potser ja en producció. Refactoritzar codi sense cap prova que ho recolzi no és impossible, però és força més arriscat: cada canvi depèn únicament de l'atenció del programador en aquell moment.

  1. Exemple: refactoritzant un mètode llarg de Biblioteca

Imagina que, amb les presses d'anar afegint funcionalitat mòdul a mòdul, Biblioteca va acabar amb un mètode que barreja validació, registre del préstec i notificació, tot junt:

// Abans: un metode llarg que valida, registra i notifica, tot barrejat
public async Task GestionarPrestecAsync(Llibre llibre, Soci soci)
{
    // Validacio
    if (llibre is null)
    {
        throw new ArgumentNullException(nameof(llibre));
    }
    if (soci is null)
    {
        throw new ArgumentNullException(nameof(soci));
    }
    if (!llibre.Disponible)
    {
        throw new InvalidOperationException($"'{llibre.Titol}' no esta disponible per a prestec.");
    }

    // Espera simulada
    await Task.Delay(1000);

    // Registre
    llibre.Prestar();
    Prestec prestec = new Prestec(llibre, soci);
    Prestecs.Add(prestec);
    PrestecRegistrat?.Invoke(prestec);

    // Persistencia
    _repositori.GuardarCataleg(Cataleg);

    // Notificacio per consola
    Console.WriteLine($"'{llibre.Titol}' prestat correctament a {soci.Nom}.");
}

Aquest mètode funciona, i les proves de la lliçó anterior probablement ja el cobririen amb algun ajust menor —però barreja, en un únic bloc de codi, quatre responsabilitats diferents (validar, esperar, registrar+persistir, mostrar un missatge), cosa que el fa difícil de llegir d'un cop d'ull i difícil de provar de forma aïllada. Aplicant "extreure mètode" sobre cada bloc:

// Despres: cada responsabilitat te el seu propi metode, amb un nom que l'explica
public async Task GestionarPrestecAsync(Llibre llibre, Soci soci)
{
    ValidarPrestec(llibre, soci);

    await Task.Delay(1000); // simulacio d'una verificacio lenta, Modul 4

    Prestec prestec = RegistrarIPersistirPrestec(llibre, soci);

    Console.WriteLine($"'{llibre.Titol}' prestat correctament a {soci.Nom}.");
}

private void ValidarPrestec(Llibre llibre, Soci soci)
{
    ArgumentNullException.ThrowIfNull(llibre);
    ArgumentNullException.ThrowIfNull(soci);

    if (!llibre.Disponible)
    {
        throw new InvalidOperationException($"'{llibre.Titol}' no esta disponible per a prestec.");
    }
}

private Prestec RegistrarIPersistirPrestec(Llibre llibre, Soci soci)
{
    llibre.Prestar();
    Prestec prestec = new Prestec(llibre, soci);

    Prestecs.Add(prestec);
    PrestecRegistrat?.Invoke(prestec);
    _repositori.GuardarCataleg(Cataleg);

    return prestec;
}

El comportament observable no ha canviat en absolut: les mateixes excepcions es llancen en els mateixos casos, el mateix missatge es mostra per consola, el mateix esdeveniment es dispara i el mateix repositori s'invoca. El que ha canviat és que ara GestionarPrestecAsync es llegeix gairebé com una llista de passos amb nom propi (ValidarPrestec, RegistrarIPersistirPrestec), i cadascun d'aquests passos es podria provar per separat si calgués més granularitat en el futur. En tornar a executar les proves de la Lliçó 4 (PrestarLlibreAsync_AmbLlibreDisponible_GuardaElCataleg i PrestarLlibreAsync_AmbLlibreNoDisponible_LlancaExcepcioINoGuarda, adaptades al nou nom del mètode) sobre aquesta versió refactoritzada, haurien de continuar passant exactament igual que abans: aquesta és la confirmació concreta que la refactorització va ser segura.

  1. Tancament del Mòdul 8 i enllaç amb el Projecte Final

Amb aquesta lliçó es tanca el Mòdul 8 (Millors Pràctiques i Patrons de Disseny). Al llarg de les seves cinc lliçons, BiblioTech no ha guanyat ni una sola funcionalitat de domini nova —l'objectiu explícit d'aquest mòdul era un altre: fer un pas enrere i consolidar tot el que s'ha construït en els mòduls anteriors. El recorregut complet:

Lliçó Què va aportar
1. Estàndards de codificació Nomenclatura consistent, EditorConfig, responsabilitat única, comentaris útils, documentació XML, nul·labilitat consistent
2. Patrons de disseny Vocabulari comú (Singleton, Factory Method, Adapter, Decorator, Strategy, Observer) i reconeixement que PrestecRegistrat ja era un Observer
3. Injecció de dependències IRepositoriBiblioteca desacoblant Biblioteca de la persistència concreta, i el contenidor de DI d'ASP.NET Core en profunditat
4. Proves unitàries xUnit, Arrange-Act-Assert, i mocks de Moq substituint IRepositoriBiblioteca a les proves
5. Revisió i refactorització Checklist de revisió, code smells, i refactorització d'un mètode llarg recolzada en les proves ja existents

El Mòdul 9 (Projecte Final) retoma ara tot el que s'ha construït en el curs —el domini complet des del Mòdul 2, la persistència del Mòdul 5, les cinc interfícies del Mòdul 7, i les pràctiques d'aquest Mòdul 8— per construir la versió final i completa de BiblioTech: se'n definiran els requisits amb precisió, se'n planificarà la implementació, es construirà seguint els estàndards i patrons ja apresos, es provarà amb la mateixa disciplina de proves unitàries vista aquí, i finalment es desplegarà. Res del que s'ha après en aquest mòdul queda de banda: és, precisament, el conjunt de pràctiques amb què es construirà aquesta versió final.

Errors Comuns i Consells

  • Refactoritzar sense cap prova que recolzi el canvi: sense proves prèvies, no hi ha manera objectiva de confirmar que el comportament no ha canviat; en aquest cas, escriu primer almenys les proves més importants sobre el comportament actual, i refactoritza després.
  • Canviar comportament "de passada" mentre es refactoritza: refactoritzar i corregir un error real són dues activitats diferents; barrejar-les en el mateix canvi dificulta saber, si alguna cosa falla després, si va ser la refactorització o la correcció la causa.
  • Revisar codi fixant-se només en l'estil: una revisió centrada únicament en espais, noms o format (automatitzable amb EditorConfig, Lliçó 1) desaprofita l'oportunitat de detectar problemes de disseny, absència de proves, o riscos reals.
  • Extreure mètodes fins a l'extrem: dividir un mètode en fragments tan petits que cal saltar entre deu mètodes diferents per entendre un flux senzill també dificulta la lectura; l'objectiu és claredat, no fragmentació per si mateixa.
  • Consell: si dubtes si un mètode necessita refactoritzar-se, pregunta't si el podries explicar en una frase curta; si la resposta requereix un "i també..." diverses vegades, probablement barreja més d'una responsabilitat.

Exercicis

  1. Identifica, en el mètode GestionarPrestecAsync "abans" de l'apartat 6, quin code smell de l'apartat 3 descriu millor el seu problema principal, i explica en una frase per què.

  2. El següent mètode d'un Soci barreja registrar una sanció amb mostrar un missatge per consola. Refactoritza'l amb "extreure mètode", separant les dues responsabilitats:

    public void AplicarSancio(decimal import)
    {
        SaldoPendent += import;
        Console.WriteLine($"S'ha aplicat una sancio de {import:C} a {Nom}. Saldo pendent: {SaldoPendent:C}");
    }
    

Solucions

El code smell principal és mètode llarg (amb una responsabilitat barrejada addicional, "massa responsabilitats" a nivell de mètode): un únic mètode valida, espera, registra, persisteix i notifica, tot en el mateix bloc de codi, cosa que en dificulta la lectura i la prova per parts.

public void AplicarSancio(decimal import)
{
    RegistrarSancio(import);
    NotificarSancio(import);
}

private void RegistrarSancio(decimal import)
{
    SaldoPendent += import;
}

private void NotificarSancio(decimal import)
{
    Console.WriteLine($"S'ha aplicat una sancio de {import:C} a {Nom}. Saldo pendent: {SaldoPendent:C}");
}

Conclusió

En aquesta lliçó has vist què busca una bona revisió de codi (llegibilitat, proves, adherència als estàndards, disseny), una checklist concreta per aplicar-la, els code smells més comuns (mètode llarg, duplicació, classes amb massa responsabilitats, paràmetres excessius), dues tècniques de refactorització (extreure mètode i extreure classe), i com les proves unitàries de la lliçó anterior actuen com a xarxa de seguretat en refactoritzar un mètode llarg de Biblioteca sense canviar el seu comportament observable.

Amb això es tanca el Mòdul 8 al complet. BiblioTech arriba al Mòdul 9 —el Projecte Final del curs— amb un domini sòlid, persistència desacoblada, una bateria de proves unitàries, i un codi revisat segons els estàndards i patrons apresos en aquestes cinc lliçons: tot el necessari per construir, planificar, provar i desplegar la versió completa i definitiva de BiblioTech.

Curs de Programació en C#

Mòdul 1: Introducció al C#

Mòdul 2: Estructures de Control

Mòdul 3: Programació Orientada a Objectes

Mòdul 4: Conceptes Avançats de C#

Mòdul 5: Treballant amb Dades

Mòdul 6: Temes Avançats

Mòdul 7: Construcció d'Aplicacions

Mòdul 8: Bones Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2026. Tots els drets reservats