Els principis SOLID són un conjunt de cinc principis de disseny de programari que ajuden a crear sistemes més comprensibles, flexibles i mantenibles. Aquests principis van ser introduïts per Robert C. Martin (també conegut com Uncle Bob) i són fonamentals per a qualsevol desenvolupador de programari que vulgui dissenyar arquitectures robustes i escalables.
S: Principi de Responsabilitat Única (Single Responsibility Principle - SRP)
Explicació
Cada classe ha de tenir una única responsabilitat o motiu per canviar. Això significa que una classe ha de tenir només una raó per ser modificada.
Exemple
// Incorrecte: La classe Report té més d'una responsabilitat public class Report { public void generateReport() { // Generar el report } public void saveToFile() { // Guardar el report en un fitxer } } // Correcte: Separar les responsabilitats en diferents classes public class ReportGenerator { public void generateReport() { // Generar el report } } public class ReportSaver { public void saveToFile() { // Guardar el report en un fitxer } }
Exercici
Refactoritza la següent classe per seguir el principi de responsabilitat única:
public class User { public void login(String username, String password) { // Lògica de login } public void register(String username, String password, String email) { // Lògica de registre } public void sendEmail(String email, String message) { // Lògica per enviar correu electrònic } }
O: Principi de Obert/Cerrat (Open/Closed Principle - OCP)
Explicació
Les entitats de programari (classes, mòduls, funcions, etc.) han d'estar obertes per a l'extensió, però tancades per a la modificació. Això significa que hauríem de poder afegir noves funcionalitats sense canviar el codi existent.
Exemple
// Incorrecte: Afegir una nova forma requereix modificar la classe public class AreaCalculator { public double calculateArea(Object shape) { if (shape instanceof Circle) { Circle circle = (Circle) shape; return Math.PI * circle.radius * circle.radius; } else if (shape instanceof Rectangle) { Rectangle rectangle = (Rectangle) shape; return rectangle.length * rectangle.width; } return 0; } } // Correcte: Utilitzar polimorfisme per afegir noves formes sense modificar la classe existent public interface Shape { double calculateArea(); } public class Circle implements Shape { public double radius; @Override public double calculateArea() { return Math.PI * radius * radius; } } public class Rectangle implements Shape { public double length; public double width; @Override public double calculateArea() { return length * width; } } public class AreaCalculator { public double calculateArea(Shape shape) { return shape.calculateArea(); } }
Exercici
Refactoritza la següent classe per seguir el principi de obert/cerrat:
public class DiscountCalculator { public double calculateDiscount(String customerType, double amount) { if (customerType.equals("Regular")) { return amount * 0.1; } else if (customerType.equals("Premium")) { return amount * 0.2; } return 0; } }
L: Principi de Substitució de Liskov (Liskov Substitution Principle - LSP)
Explicació
Els objectes d'una classe derivada han de poder substituir els objectes de la seva classe base sense alterar les propietats del programa (correcció, tasques, etc.).
Exemple
// Incorrecte: La classe Square no es comporta com un Rectangle public class Rectangle { protected int width; protected int height; public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } public int getArea() { return width * height; } } public class Square extends Rectangle { @Override public void setWidth(int width) { this.width = width; this.height = width; } @Override public void setHeight(int height) { this.height = height; this.width = height; } } // Correcte: Separar les classes per respectar el principi de substitució de Liskov public abstract class Shape { public abstract int getArea(); } public class Rectangle extends Shape { protected int width; protected int height; public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } @Override public int getArea() { return width * height; } } public class Square extends Shape { private int side; public void setSide(int side) { this.side = side; } @Override public int getArea() { return side * side; } }
Exercici
Refactoritza la següent jerarquia de classes per seguir el principi de substitució de Liskov:
public class Bird { public void fly() { // Lògica per volar } } public class Ostrich extends Bird { @Override public void fly() { // Les estruços no poden volar throw new UnsupportedOperationException(); } }
I: Principi de Segregació d'Interfícies (Interface Segregation Principle - ISP)
Explicació
Els clients no haurien de dependre de mètodes que no utilitzen. Això significa que és millor tenir diverses interfícies específiques que una interfície general.
Exemple
// Incorrecte: La interfície Worker té mètodes que no són necessaris per a tots els treballadors public interface Worker { void work(); void eat(); } public class HumanWorker implements Worker { @Override public void work() { // Lògica de treball } @Override public void eat() { // Lògica per menjar } } public class RobotWorker implements Worker { @Override public void work() { // Lògica de treball } @Override public void eat() { // Els robots no mengen throw new UnsupportedOperationException(); } } // Correcte: Separar les interfícies per respectar el principi de segregació d'interfícies public interface Workable { void work(); } public interface Eatable { void eat(); } public class HumanWorker implements Workable, Eatable { @Override public void work() { // Lògica de treball } @Override public void eat() { // Lògica per menjar } } public class RobotWorker implements Workable { @Override public void work() { // Lògica de treball } }
Exercici
Refactoritza la següent interfície per seguir el principi de segregació d'interfícies:
public interface Printer { void printDocument(String document); void scanDocument(String document); void faxDocument(String document); }
D: Principi d'Inversió de Dependència (Dependency Inversion Principle - DIP)
Explicació
Els mòduls d'alt nivell no haurien de dependre de mòduls de baix nivell. Ambdós haurien de dependre d'abstraccions. Les abstraccions no haurien de dependre de detalls. Els detalls haurien de dependre d'abstraccions.
Exemple
// Incorrecte: La classe de baix nivell està directament instanciada a la classe de nivell alt public class LightBulb { public void turnOn() { // Encendre la bombeta } public void turnOff() { // Apagar la bombeta } } public class Switch { private LightBulb lightBulb; public Switch() { this.lightBulb = new LightBulb(); } public void operate() { // Operar l'interruptor } } // Correcte: Utilitzar una interfície per invertir la dependència public interface Switchable { void turnOn(); void turnOff(); } public class LightBulb implements Switchable { @Override public void turnOn() { // Encendre la bombeta } @Override public void turnOff() { // Apagar la bombeta } } public class Switch { private Switchable device; public Switch(Switchable device) { this.device = device; } public void operate() { // Operar l'interruptor } }
Exercici
Refactoritza la següent classe per seguir el principi d'inversió de dependència:
public class Database { public void connect() { // Connectar a la base de dades } } public class Application { private Database database; public Application() { this.database = new Database(); } public void start() { database.connect(); } }
Conclusió
Els principis SOLID són fonamentals per al disseny de programari de qualitat. Aquests principis ajuden a crear sistemes que són més fàcils de mantenir, escalar i entendre. En aplicar aquests principis, els desenvolupadors poden evitar molts dels problemes comuns que sorgeixen en el desenvolupament de programari, com ara el codi duplicat, les dependències rígides i les dificultats per afegir noves funcionalitats.
Arquitectures de Sistemes: Principis i Pràctiques per Dissenyar Arquitectures Tecnològiques Robustes i Escalables
Mòdul 1: Introducció a les Arquitectures de Sistemes
- Conceptes Bàsics d'Arquitectura de Sistemes
- Importància d'una Bona Arquitectura
- Tipus d'Arquitectures de Sistemes
Mòdul 2: Principis de Disseny d'Arquitectures
Mòdul 3: Components d'una Arquitectura de Sistemes
Mòdul 4: Escalabilitat i Rendiment
Mòdul 5: Seguretat en Arquitectures de Sistemes
Mòdul 6: Eines i Tecnologies
Mòdul 7: Casos d'Estudi i Exemples Pràctics
- Cas d'Estudi: Arquitectura d'un Sistema de Comerç Electrònic
- Cas d'Estudi: Arquitectura d'una Aplicació de Xarxes Socials
- Exercicis Pràctics