Os detallo a continuación, un ejemplo de código limpio extraído de la lectura del libro: Código Limpio. Manual de estilo para el desarrollo ágil de software. Robert C. Martin.

El tema va sobre las instrucciones switch. Es muy difícil mantenerlas con un código reducido y con el objetivo de que una función haga una sola cosa. Por la naturaleza, de la instrucción switch vamos a tener funciones que hagan N cosas. Malo, esto no hace mantenible el software pero claro, tampoco podemos dejar de utilizar instrucciones switch.

Lo que sí que podemos hacer es asegurarnos de incluir estas sentencias switch en una clase nivel inferior y NO repetirlas. Para ello, EVIDENTEMENTE, tenemos que recurrir al POLIMORFISMO (me encanta, es probable que a alguno de vosotros en la entrevista os haya pedido que me definieráis el polimorfismo).

Vamos a fijarnos en este listado de código fuente (Payroll.java)

public Money calculatePay(Employee e) throws InvalidEmployeeType{
    switch (e.type){
        case COMMISSIONED:
             return calculateCommisionedPay(e);
        case HOURLY: 
             return calculateHourlyPay(e);
        case SALARIED: 
             return calculateSalariedPay(e);           
        default: 
             throw new InvalidEmployeeType(e.type);
    }
}

Esto, por un lado taaaaaaaaaaan habitual, tiene varios problemas:

  1. Es una función de gran tamaño y cuando se añadan más tipos de empleados todavía crecerá más.
  2. Hace más de una cosa, incumpliendo el Principio de Responsabilidad Única (Single Responsibility Principle – SRP-) ya que hay más de un motivo para cambiarla, por ejemplo cuando se añadan nuevos tipos de empleados.

Nota: No veais lo que me gusta la Orientación a Objetos. Sus conceptos que ya tienen sus años se utilizan ahora en temas de diseño de microservicios, por ejemplo.

  1. Incumple el Principio de abierto/cerrado (Open Closed Principle o OCP) igualmente porque debe cambiar cuando se añadan nuevos tipos de empleados.

  2. Pero el problema mayor, es que vamos a tener un número iliminatado de funciones que también van a tener el mismo problema.

Imagínate la implamentación de algo como:

ìsPayday(Employee e, Date date)

o

deliveryPay(Employee e, Money pay)

La estamos liando parda y no nos estamos dando ni cuenta.

La solución al problema consiste en OCULTAR LA INSTRUCCIÓN swicth en una FACTORIA ABSTRACTA e impedir que nadie la vea

Para los que no entendáis esto, digamos que lo que vamos a hacer es enterrar en lo más profundo del código ese switch (porqué tiene que estar) pero va a estar controlado por una FACTORIA que es un patrón de diseño que va a permitir crear, en este caso, los empleados con un DESACOPLAMIENTO del software (me parece brutal).

Fíjate la solución es sublime:


public abstract class Employee {
    public abstract boolean isPayDay();
    public abstract Money calculatePay();
    public abstract void deliverPay(Money pay);
}

public class CommissionedEmployee extends Employee { ... }
public class HourlyEmployee extends Employee { ... }
public class SalariedEmployee extends Employee { ... }

public interface EmployeeFactory{
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}

public class EmployeeFactoryImpl implements EmployeeFactory{
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
          switch (r.type) {   
             case COMMISSIONED:
                 return new CommissionedEmployee(r);
             case HOURLY: 
                 return new HourlyEmployee(r);
             case SALARIED: 
                 return new SalariedEmployee(r);           
             default: 
             throw new InvalidEmployeeType(e.type);

          }
    }
}

¡Madre mía! preparado para jerarquía de clase Employee y los diferentes empleados que extienden de ella. Ahí sí que nos tocará implementar cada vez que haya un nuevo empleado.
Y el switch encerrado en una factoría (interfaz con método makeEmployee más su implementación en EmployeeFactoryImpl. Si amplías a nuevos empleados extiendes de la clase Employee, implementas sus métodos abstractos y tocas la factoría que hace NEW del objeto devolviendolo sobre una referencia a clase base (Employee).

Nota: Que no te asuste la clase EmployeeRecord que es simplemente para identificar el tipo de empleado.