Ir al contenido principal

Historia de una conversa sobre OOP

Y luego de un largo rato volvemos por acá con una historia basada en hechos reales, sobre una conversación muy didáctica que tuve con un amigo:

-Mr. Bladi, he escuchado que Ud. se opone, es más desprecia, al uso de las clases Utilitarias y los métodos estáticos en la programación orientada a objetos (OOP).
-Ciertamente. No aconsejo su uso y lo considero una mala práctica, algo ofensivo en la OOP.
-Pero si no podemos usar clases utilitarias y métodos estáticos, ¿Cuáles opciones Tenemos?
-Tenemos unas cuantas opciones. Pero antes un poco de contexto. Las clases utilitarias, esas que están repletas de métodos estáticos, no son objetos reales, son un recurso totalmente procedimental y rompen el principio de encapsulamiento en la OOP.
Peor aún, las clases utilitarias con su torrente de métodos estáticos, son muy difíciles de testear en las pruebas automáticas. 

TODO MÉTODO ESTÁTICO, E INCLUSO LOS PRIVADOS, SON CANDIDATOS PARA UNA NUEVA CLASE, es decir son elegibles para una nueva abstracción.




Veamos un ejemplo…. Y como siempre nuestro grito de guerra: Manos a las sobras!!!
Supongamos que tenemos la usual clase:

public class MyMath {
 public static double sumar(double a, double b) {
  return a + b;
 }

 public static double restar(double a, double b) {
  return a - b;
 }

 public static double multiplicar(double a, double b) {
  return a * b;
 }

 public static double dividir(double a, double b) {
  return a / b;
 }
}


La cual podemos y solemos utilizar del siguiente modo:

double result = MyMath.sumar(2.0, 3.0);
System.out.println(“result = “ + result);
System.out.println(“result * 3 + 2 = ” + (MyMath.sumar(MyMath.multiplicar(result, 3.0),2.0)));

Primera implementación:

Haciendo un análisis podemos observar que tenemos una operación entre dos operadores numéricos, lo que se puede representar en una interface tal como:
public interface Operacion{
    public double ejecutar(double ope1, double ope2);
}

Escribimos clases para cada implementación de Operacion que deseemos:

public final class Sumar implements Operacion {
        public double ejecutar(double ope1, double ope2){
            return ope1 + ope2;
        }
}
public final class Multiplicar implements Operacion {
        public double ejecutar(double ope1, double ope2){
            return ope1 * ope2;
        }
}

Y la podemos usar de la siguiente forma:

Operacion sumar = new Sumar();
Operacion multiplicar = new Multiplicar();
double result = sumar.ejecutar(2.0, 3.0);
System.out.println(“result = “ + result);
System.out.println(“result * 3 + 2 = ” + (sumar.ejecutar(multiplicar.ejecutar(result, 3.0),2.0))); 

Como ven, nada de métodos estáticos y dejamos que los objetos trabajen para nosotros. Pero.... siempre hay un pero, esta implementación tampoco es del todo correcta. Según los principios pigbar, no deben existir clases con sólo métodos, así como no deben existir clases con sólo propiedades. Ambos casos son síntomas de un mal diseño.

Así que mejoremos este enfoque.

Segunda implementación: 

 Ahora la interface Operacion queda de la siguiente forma:

public interface Operacion{
    public double ejecutar();
}

Mucho mas sencilla. Y ahora sus implementaciones:

public final class Sumar implements Operacion {
    private double ope1, ope2;
    public Sumar(double ope1, double ope2){
        this.ope1= ope1;
        this.ope2= ope2;
    }
    public double ejecutar(){
        return ope1 + ope2;
    }
}
public final class Multiplicar implements Operacion {
    private double ope1, ope2;
    public Multiplicar(double ope1, double ope2){
        this.ope1= ope1;
        this.ope2= ope2;
    }
    public double ejecutar(){
        return ope1 * ope2;
    }
}

Como ven, muy parecidas pero con grandes diferencias. Estos si son verdaderos objetos encapsulando comportamiento y datos. Ahora los podemos usar de la siguiente forma:

Operacion sumar = new Sumar(2.0, 3.0);
double result = sumar.ejecutar();
System.out.println(“result = “ + result);
System.out.println(“result * 3 + 2 = ” + (new Sumar(new Multiplicar(result, 3.0).ejecutar(),2.0).ejecutar())); 

Hemos dado un paso al frente en nuestra visión de la programación orientada a objetos. Pero aún lo podemos hacer de forma distinta...

Tercera implementación:


Y… ¿Qué tal si hacemos esto más al estilo “Fluent”? Pues veamos…. Vamos a crear una Interface Operable:

public interface Operable{
    public double valor();
    public Operable sumar(double operando);
    public Operable multiplicar(double operando);
}

E implementamos la Interface:

public final class Operando implements Operable{
    private double valor;
    public Operando(){
        this(0.0);
    }
    public Operando(double number){
        valor= number;
    }
    public double valor(){
        return valor;
    }
    public Operable sumar(double operando){
        return new Operando(this.valor + operando);
    }
    public Operable multiplicar(double operando) ){
        return new Operando(this.valor * operando);
    }
    @overwrite
    public String toString(){
        return “” + valor;
    }
}

Ahora lo podemos usar escribiendo código como el siguiente:

Operando result = new Operando(2.0).sumar(3.0);
System.out.println(“result = “ + result);
System.out.println(“result * 3 + 2 = ” + result.multiplicar(3.0).sumar(2.0));

Elegante, ¿no les parece? Así que nada de excusas para clases utilitarias y métodos estáticos.

-Mr. Bladi, cuando crezca quiero ser como Ud.
-Lo serás mi pequeño saltamontes. Lo serás...

Comentarios

  1. "una operación entre dos operadores" esto es = a tres ?

    ResponderEliminar
    Respuestas
    1. Si, "=" es una operación, y los valores a su izquierda o derecha serían los operandos u operadores.

      Eliminar

Publicar un comentario

Entradas populares de este blog

El Melange todavía corre

Ese era el estribillo de un capítulo de unas de mis series favoritas de la infancia, Meteoro o Speed Racer. En ese capítulo un auto “fantasma” el X-3, aparecía de imprevisto y dejaba a todos asombrados con su rendimiento y prestaciones y volvía a desaparecer. Traigo ese episodio a colación puesto que recientemente sostuve una amena charla con un querido amigo, en la que el me manifestaba como los Mainframes habían muerto, o mejor dicho, el concepto de la computación distribuida basada en Mainframes había desaparecido. Para variar, yo no estuve de acuerdo, y le dije que por el contrario, el modelo de computación basado en Mainframes está mas vigente que nunca. Estos fueron mis argumentos:

Primeros pasos con Camunda BPM – Modelando un Proceso BPMN 2.0

Tenemos entre manos la tercera publicación de nuestra serie sobre la Plataforma de BPM de Camunda .  El día de hoy vamos, por fin, a empezar a modelar o construir nuestro primer proceso sencillo en notación BPMN 2.0. Para ello vamos a usar el modelador o editor que ya hemos instalado en nuestra primera publicación , y vamos a guardarlo en la sección de recursos del proyecto Maven Java que configuramos en la segunda publicación . Así que, como ya es costumbre, manos a las sobras…

Como configurar jBPM para usar nuestra propia Base de Datos en un sólo paso

Llevo un buen rato trabajando con jBPM en su serie 6.x, y mi opinión sobre este producto en la versión mecionada no ha mejorado para nada. Es una herramienta plena de funciones y caracteristicas avanzadas, pero tambien está llena de Bugs y es realmente inestable, sobre todo en el ambiente de modelamiento.  Así mismo, debo decir que tiene una muy aceptable API REST y que el motor de procesos y la consecuente ejecución de los procesos es estable y bastante rápida. En esta publicación daré inicio a una serie de artículos que hablan sobre ciertas configuraciones comunes e importantes que se hacen con jBPM. Hoy iniciamos con la configuración de jBPM para que use nuestra base de datos favorita. Esto tiene sentido porque el producto viene con la base de datos H2 por omisión, la cual es excelente para pruebas y evaluaciones rápidas de la herramienta, pero es completamente inaceptable en un ambiente de desarrollo, QA o producción cualquiera. Así que manos a l