web-dev-qa-db-ja.com

再利用可能なオブジェクトの抽象メソッドとインスタンス変数

Javaコードを再利用して再利用するコードがかなりあります。問題は、プロジェクト固有の多くの部分があるため、結果的に高くなることです。アプリケーションプロジェクトとコードベースプロジェクト間の結合レベル。

抽象メソッドの使用を実装するクラスを使用して子クラスからリソースを取得する以下の状況と、インスタンス変数を単に宣言する状況とを比較してください。

抽象メソッド:

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

}

インスタンス変数:

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

}

トレードオフはありますか?抽象メソッドの状況はやりすぎですか、それとも抽象クラスが作成されたのですか?

8
Styler

抽象メソッドがより良い選択だと思います。これらのプロパティの実装がサブクラスで心配する必要のないものである場合は、基本クラスに責任を持たせることもできます。つまり、基本クラスのプライベートフィールドをパブリックアクセサーと保護されたミューテーターで使用します。

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

この種類のアプローチは、強力なカプセル化を提供します。つまり、フィールドを直接変更できるコードの量が最小限に抑えられます。

8

抽象クラスアプローチでは、拡張されたクラスが変数を「初期化」するように強制しますが、保護されたインスタンス変数を使用すると、デフォルトを簡単に使用して、拡張しないと混乱/バグを引き起こす可能性があります。

5
arin

私見より良いアプローチは、いくつかのフィールドをカスタマイズするためだけの場合は、合成を使用して継承を完全に回避することです。 「子クラスからリソースを取得するには」と言うので、次のようにします。

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

次に、クラスでMyResourcesオブジェクトをインスタンス化し、必要に応じてそれらをカスタマイズします。それでもクラスの階層が必要な場合は、基本クラスにリソースオブジェクトを置き、それを子クラスに継承できます。

public abstract class SuperBaseClass {

protected MyResources resources;

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

public class ReusedAppClass extends SuperBaseClass {

public ReusedAppClass () {
    super();
    resources.setStringOne("Hi");
}
}

これらのすべての変数が基本クラスのプロパティであり、子がそれらに一様に依存している場合、それらを基本で宣言することが適切であり、抽象get/setters/accessorsを追加することは、子が適切と思われるデータをマッサージできるようにする方法です。これは決してやり過ぎではなく、OOPはこれらの種類の関係を表現するように設計されています。

この種のデータをベースクラスに集めすぎている(またはこのデータがすべての子に普遍的に共通しているわけではない)場合は、ベースクラスをコンテナにリファクタリングする時期が来るかもしれません。ロジックとコードの整理に役立つ、より具体的な基本クラス。これはもう少し複雑になりますが、よりすっきりとした設計が可能になり、適切な名前を付けると、わかりやすくなります。

私のどちらの場合でも、物事を変更する前に、最初に再利用すると予想されることを正確に分析する必要があります。既存のコードから再利用可能なフレームワークを作成することは、たとえば、データを再編成してモジュールを再利用するためにそのデータに対する操作を行うこととはまったく異なる場所に行き着きます。

2
Patrick Hughes