Métodos abstractos vs variables de instancia para objetos reutilizables

8

Tengo bastante código Java que estoy trabajando para volver a usarlo. El problema es que hay muchas piezas que son específicas del proyecto, por lo que termina siendo un mayor nivel de acoplamiento entre el proyecto de la aplicación y el proyecto base del código.

Compare con las siguientes situaciones en las que usamos una clase que implementa el uso de métodos abstractos para sacar los recursos de la clase secundaria, y una en la que simplemente declaramos variables de instancia.

Métodos abstractos:

public abstract class SuperBaseClass {

    public abstract int getNumberOne();
    public abstract String getStringOne();
    public abstract String getStringTwo();

    public printStuff() {
        Log.i("IntAndTwoStrings", String.format("%i %s and %s",
                            getNumberOne(), getStringOne(), getStringTwo()));
    }

}

public class ReusedAppClass extends SuperBaseClass {

    public int getNumberOne() {
        return 1;
    }
    public String getStringOne() {
        return "String1";
    }
    public String getStringTwo() {
        return "String2";
    }

    public ReusedAppClass() {
        printStuff();
    }

}

Variables de instancia:

public class SuperBaseClass {

    protected int numberOne;
    protected String stringOne;
    protected String stringTwo;

    public printStuff() {
        //Possibly throw RuntimeExceptions if the app didnt set these
        Log.i("IntAndTwoStrings", String.format("%i %s and %s",
                            numberOne, stringOne, stringTwo));
    }

}

public class ReusedAppClass extends SuperBaseClass {

    public ReusedAppClass() {
        numberOne = 1;
        stringOne = "String1";
        stringTwo = "String2";
        printStuff();
    }

}

¿Hay una compensación? ¿La situación del método abstracto es excesiva, o para esto se crearon las clases abstractas?

Styler
fuente

Respuestas:

8

Creo que los métodos abstractos son la mejor opción. Si la implementación de estas propiedades no es algo de lo que desee que se preocupen las subclases, incluso puede querer que la clase base sea responsable de ello, es decir, use campos privados en la clase base con accesores públicos y mutadores protegidos:

public class SuperBaseClass {

    private int numberOne;
    private String stringOne;
    private String stringTwo;

    public int getNumberOne() { return numberOne; }
    public String getStringOne() { return stringOne; }
    public String getStringTwo() { return stringTwo; }

    protected void setNumberOne(String numberOne) { this.numberOne = numberOne; }
    protected void setStringOne(String stringOne) { this.stringOne = stringOne; }
    protected void setStringTwo(String stringTwo) { this.stringTwo = stringTwo; }

    public printStuff() {
        Log.i("IntAndTwoStrings", String.format("%i %s and %s", getNumberOne(), getStringOne(), getStringTwo()));
    }
}

Este tipo de enfoque proporciona una fuerte encapsulación, es decir, se minimiza la cantidad de código que puede modificar directamente sus campos.

Gyan alias Gary Buyn
fuente
Una mejor encapsulación es una ventaja definitiva al usar métodos abstractos +1
Styler
5

El enfoque de clase abstracta también obliga a la clase que se extiende a "inicializar" las variables, mientras que con las variables de instancia protegidas, los valores predeterminados se pueden usar fácilmente, lo que lleva a confusión / errores si no se amplía.

arin
fuente
3

En mi humilde opinión, un mejor enfoque es usar la composición y evitar la herencia por completo si es solo por el hecho de personalizar algunos campos. Dices "para sacar los recursos de la clase secundaria", así que hazlo:

public class MyResources() {

private int numberOne;
private String stringOne;
private String stringTwo;

public int getNumberOne() {
    return numberOne;
}

public void setNumberOne(int numberOne) {
    this.numberOne = numberOne;
}

public String getStringOne() {
    return stringOne;
}

public void setStringOne(String stringOne) {
    this.stringOne = stringOne;
}

public String getStringTwo() {
    return stringTwo;
}

public void setStringTwo(String stringTwo) {
    this.stringTwo = stringTwo;
}
}

Luego, ejemplifique los objetos MyResources en sus clases y personalícelos según sea necesario. Si aún necesita tener una jerarquía de clases, puede tener su objeto de recurso en la clase base y heredarlo en las clases secundarias:

public abstract class SuperBaseClass {

protected MyResources resources;

public SuperBaseClass() {
    resources.setStringOne("Hello");
}

public class ReusedAppClass extends SuperBaseClass {

public ReusedAppClass () {
    super();
    resources.setStringOne("Hi");
}
}
Trasplazio Garzuglio
fuente
2

Si todas esas variables son propiedades de la clase base y los niños dependen uniformemente de ellas, entonces es apropiado declararlas en la base, y agregar get / setters / accessors abstractos sería la forma de permitir que los niños masajeen los datos como mejor les parezca. Esto no es exagerado en absoluto y OOP está diseñado para expresar este tipo de relaciones.

Si descubre que está generando demasiados datos de este tipo a la clase base (o estos datos no son universalmente comunes para todos los niños), entonces podría ser el momento de una mayor refactorización de la clase base en un contenedor para otras clases base más específicas para ayudar a organizar su lógica y código. Esto sería un poco más complejo pero permitiría un diseño más limpio y con una buena denominación, uno más fácil de seguir.

En cualquiera de mis casos, primero debe analizar qué es exactamente lo que espera reutilizar antes de cambiar las cosas. Crear un marco reutilizable a partir del código existente terminará en un lugar muy diferente que reorganizar datos y operaciones en esos datos para la reutilización del módulo, por ejemplo.

Patrick Hughes
fuente