Métodos abstratos vs variáveis ​​de instância para objetos reutilizáveis

8

Eu tenho um pouco de código Java que estou trabalhando para ser reutilizado. O problema é que existem muitas partes que são específicas do projeto e, portanto, acaba sendo um nível mais alto de acoplamento entre o projeto do aplicativo e o projeto da base de código.

Compare com as seguintes situações em que usamos uma classe que implementa o uso de métodos abstratos para obter os recursos da classe filho e uma onde simplesmente declaramos variáveis ​​de instância.

Métodos abstratos:

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

}

Variáveis ​​de instância:

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

}

Existe uma troca? A situação do método abstrato é um exagero ou é para isso que as classes abstratas foram criadas?

Styler
fonte

Respostas:

8

Eu acho que os métodos abstratos são a melhor escolha. Se a implementação dessas propriedades não é algo com o qual você deseja que as subclasses se preocupem, você pode até querer que a classe base seja responsável por isso, ou seja, use campos privados na classe base com acessadores públicos e 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()));
    }
}

Esse tipo de abordagem fornece um forte encapsulamento, ou seja, a quantidade de código que pode modificar diretamente seus campos é minimizada.

Gyan tcp Gary Buyn
fonte
Melhor encapsulamento é uma vantagem definitiva do uso de métodos abstratos +1
Styler
5

A abordagem de classe abstrata também força a classe estendida a "inicializar" as variáveis, enquanto que com as variáveis ​​de instância protegidas os padrões podem ser facilmente usados, levando a confusão / bugs, se não forem estendidos.

arin
fonte
3

IMHO, uma abordagem melhor é usar a composição e evitar a herança completamente, se for apenas para personalizar alguns campos. Você diz "para obter os recursos da classe filho", faça o seguinte:

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

Instancie objetos MyResources em suas classes e personalize-os conforme necessário. Se você ainda precisar ter uma hierarquia de classes, poderá ter seu objeto de recurso na classe base e herdá-lo nas classes filho:

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

Se todas essas variáveis ​​são propriedades da classe base e os filhos dependem uniformemente delas, declará-las na base é apropriado e adicionar get / setters / accessors abstratos seria o caminho para permitir que as crianças massageiem os dados como acharem melhor. Isso não é um exagero e o POO foi projetado para expressar esse tipo de relacionamento.

Se você achar que está elevando muito esse tipo de dados para a classe base (ou esses dados não são universalmente comuns a todos os filhos), talvez seja hora de uma refatoração maior da classe base em um contêiner para outras classes base mais específicas para ajudar a organizar sua lógica e código. Isso seria um pouco mais complexo, mas permitirá um design mais limpo e com uma boa nomeação mais fácil de seguir.

Em qualquer um dos meus casos, você deve primeiro analisar o que exatamente espera reutilizar antes de mudar as coisas. A criação de uma estrutura reutilizável a partir do código existente terminará em um local muito diferente da reorganização dos dados e operações nesses dados para reutilização do módulo, por exemplo.

Patrick Hughes
fonte