Abstrakte Methoden gegen Instanzvariablen für wiederverwendbare Objekte

8

Ich habe ziemlich viel Java-Code, den ich überarbeite, um ihn wiederzuverwenden. Das Problem ist, dass es viele Teile gibt, die projektspezifisch sind, so dass am Ende eine höhere Kopplungsstufe zwischen dem Anwendungsprojekt und dem Codebasisprojekt besteht.

Vergleichen Sie die folgenden Situationen, in denen wir eine Klasse verwenden, die die Verwendung abstrakter Methoden implementiert, um die Ressourcen aus der untergeordneten Klasse herauszuholen, und eine, in der wir einfach Instanzvariablen deklarieren.

Abstrakte Methoden:

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();
    }

}

Instanzvariablen:

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();
    }

}

Gibt es einen Kompromiss? Ist die Situation mit abstrakten Methoden übertrieben, oder wurden dafür abstrakte Klassen erstellt?

Styler
quelle

Antworten:

8

Ich denke, die abstrakten Methoden sind die bessere Wahl. Wenn die Unterklassen sich nicht um die Implementierung dieser Eigenschaften kümmern sollen, möchten Sie möglicherweise sogar, dass die Basisklasse dafür verantwortlich ist, dh verwenden Sie private Felder in der Basisklasse mit öffentlichen Zugriffsmethoden und geschützten Mutatoren:

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()));
    }
}

Diese Art von Ansatz bietet eine starke Kapselung, dh die Menge an Code, die Ihre Felder direkt ändern kann, wird minimiert.

Gyan alias Gary Buyn
quelle
Eine bessere Kapselung ist ein klarer Vorteil für die Verwendung abstrakter Methoden +1
Styler
5

Der abstrakte Klassenansatz zwingt die erweiterte Klasse auch, die Variablen zu "initialisieren", während bei den geschützten Instanzvariablen die Standardeinstellungen leicht verwendet werden können, was zu Verwirrung / Fehlern führen kann, wenn sie nicht erweitert werden.

Arin
quelle
3

Meiner Meinung nach besteht ein besserer Ansatz darin, die Komposition zu verwenden und die Vererbung insgesamt zu vermeiden, wenn nur einige Felder angepasst werden sollen. Sie sagen "um die Ressourcen aus der Kinderklasse herauszuholen", also tun Sie es:

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;
}
}

Instanziieren Sie dann MyResources-Objekte in Ihren Klassen und passen Sie sie nach Bedarf an. Wenn Sie weiterhin eine Klassenhierarchie benötigen, können Sie Ihr Ressourcenobjekt in der Basisklasse haben und es in den untergeordneten Klassen erben:

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
quelle
2

Wenn alle diese Variablen Eigenschaften der Basisklasse sind und Kinder einheitlich von ihnen abhängen, ist es angemessen, sie in der Basis zu deklarieren. Wenn Sie abstrakte get / setters / accessors hinzufügen, können Kinder die Daten nach Belieben massieren. Dies ist überhaupt kein Overkill und OOP wurde entwickelt, um diese Art von Beziehungen auszudrücken.

Wenn Sie feststellen, dass Sie viel zu viele dieser Daten in die Basisklasse übertragen (oder diese Daten nicht allen Kindern gemeinsam sind), ist es möglicherweise an der Zeit, die Basisklasse für einen größeren Umbau in einen Container umzugestalten andere, spezifischere Basisklassen zur Organisation Ihrer Logik und Ihres Codes. Dies wäre etwas komplexer, würde aber ein saubereres Design ermöglichen und bei guter Benennung leichter zu befolgen sein.

In beiden Fällen müssen Sie zunächst analysieren, was genau Sie wiederverwenden möchten, bevor Sie Änderungen vornehmen. Das Erstellen eines wiederverwendbaren Frameworks aus vorhandenem Code endet an einem ganz anderen Ort als das Reorganisieren von Daten und Vorgängen für diese Daten, beispielsweise zur Wiederverwendung von Modulen.

Patrick Hughes
quelle