web-dev-qa-db-ja.com

引数の変数リストを持つ抽象メソッド

私はこの問題を解決するためのエレガントな方法をまだ見つけていません。私は、他のいくつかのクラスが、0から4-5までのさまざまな型の引数を含むことができる抽象メソッドで継承している抽象クラスを持っています。

public abstract class Item {
public abstract void use();
}

たとえば、これを継承し、use()をオーバーライドするときに引数を取らないBookクラスがあります。オーバーライドするときに、引数としてStringとQueueを継承して取るKeyクラスがあります。

ジェネリックスを使用してみましたが、実際にクラスに依存している場合は、Itemなどの使用されている数値を入力する必要があります。

public abstract class Item<T,U> {
public abstract void use(T arg1, U arg2); //Number of arguments/types could be more or less
}

オブジェクトの変数リストを送信しようとしましたが、オブジェクトタイプは常に変数であり、継承するクラスで受信する構文については不明です。

public abstract class Item<T> {
public abstract void use(T... arguments);
}

public class Book extends Item<?> {
public void use(?);
}

public class Book extends Item<String, Queue> { //Wrong number of arguments since I can't use Item<T...>
public void use(String str, Queue q); //fails
}

私は何か間違ったことをしているかもしれません-誰かが何か助けや洞察を提供できますか?

18
kaledev

同じ質問で苦労してきましたが、完全な答えはありませんが、いくつか考慮すべき点を挙げましょう。まず、基本的に、オブジェクト指向プログラミングに本質的に反することを実行しようとしています。つまり、可変インターフェイスを作成しようとしています。インターフェイスの要点は、オブジェクトの抽象的なバージョン(たとえば、BookではなくItem)を取得するコードが、use()メソッドの呼び出し方法を知っていることです。つまり、use()メソッドに渡すことができるものを知っている必要があります。答えが抽象クラスまたはインターフェースの実装に依存する場合は、それを使用するコードが実際にどの種類の実装(Bookなど)を使用しているかを確認する必要があります。そうでなければ、useを呼び出す方法がわかりません。 ()とにかく適切なパラメータを使用します。正直なところ、コードをリファクタリングする必要があるようです。

ただし、アーキテクチャをリファクタリングせずに、前述の質問に答える方法があります。データが、use()メソッドに渡される可能性のあるすべてのタイプのパラメーターであるクラスを作成し、呼び出しコードにそのクラスのフィールドを設定させ、それをuse()メソッドに渡すことができます。例えば:

public class UseParameters {
    private String string;
    private Queue queue;
    // Any other potential parameters to use(...)

    public void setString(String string) {
        this.string = string;
    }

    public String getString() {
        return string;
    }

    // All of the other accessor methods, etc.
}

次に、次のようにItemで使用方法を定義できます。

public abstract void use(UseParameters params);

また、アイテムを使用するコードは、オブジェクトのパラメーターを適切に設定する必要があります。

Item item = // However you're going to get the item
UseParameters params = new UseParameters();
params.setString("good string");
params.setQueue(new Queue());
item.use(params);

上記のコードがItemがBookであることを知っている場合(文字列とキューを設定するための方法です)、Bookを取得して変数use()メソッドを持つ抽象クラスの必要性をスキップしないのはなぜですか?結局、私は余談ですが、とにかく、Bookは次のようにuse()メソッドを実装します。

@Override
public void use(UseParameters params) {
    if(params.getString == null || params.getQueue() == null)
        // throw exception

    // Do what books do with strings and queues
}

それはあなたが望むものを手に入れると思いますが、あなたはリファクタリングを考慮すべきだと思います。

15
Mindle Hastings

必要なのは 値オブジェクトパターン です。

さまざまなパラメーターの型を1つの値オブジェクトにカプセル化するクラスを定義し、抽象メソッドにこの型のパラメーターを受け入れさせます。検討していたパラメータの各バリエーションには、独自の値クラスがあります。

次に、ジェネリック型をクラスに追加し、抽象メソッドにその型のパラメーターを受け入れさせるだけです。

public abstract class Item<V> {
    public abstract void use(V v);
}

これを使用するには、MyItemにタイプMyValueClassの値オブジェクトが必要だとします。

public class MyItem extends Item<MyValueClass> {
    public void use(MyValueClass v) {
    }
}
8
Bohemian

引数として使用される型が常に可変である場合、ジェネリックスを使用する理由がわかりません。プレーンオブジェクトタイプを使用するだけです。

public abstract class Item {
    public abstract void use(Object ... arguments);
}

public class Book extends Item {
    public void use(Object ... arguments) { ... }
}
5
morgano

私が考えることができる最良のアプローチは、use()メソッドの動作に従ってアイテムをグループ化することです。

public abstract class QueueableItem {
    public abstract void use(String, Queue);
}

public abstract class OrdinaryItem{
    public abstract void use(String);
}

グループ化されたアイテムが共通の動作(同じメソッドシグネチャと戻り値の場合と同様)を共有する場合、この共通の動作の定義を含む親クラスを定義および拡張できます。

1
Bnrdo

はい、パラメーターを抽象メソッドに提供できますが、派生クラスで記述した実装済みメソッドに同じタイプのパラメーターを提供する必要があります。

0
Arun Raaj