web-dev-qa-db-ja.com

コンストラクターのオーバーロード-Javaのベストプラクティス

コンストラクターは他のメソッドと同様にオーバーロードされる可能性があり、私はその事実を認識しています。タスクのために、複数のコンストラクターを持つ抽象スーパークラスを使用することにしました。

抽象スーパークラス:

protected ListSortierer()
{
  this( null, null );
}

protected ListSortierer( List<E> li )
{
  this( li, null );
}

protected ListSortierer( Comparator<E> comp )
{
  this( null, comp );     
}

protected ListSortierer( List<E> li, Comparator<E> com )
{
  this.original = Optional.ofNullable( li );
  this.comp = Optional.ofNullable( com );
}

これらのコンストラクターのそれぞれにアクセスするには、サブクラスにも複数のコンストラクターが必要でした。

BubbleSort.Java:

public ListBubbleSort()
{
  super();
}

public ListBubbleSort( List<E> li )
{
  super( li );
}

public ListBubbleSort( Comparator<E> com )
{
  super( com );
}

public ListBubbleSort( List<E> li, Comparator<E> com )
{
  super( li, com );
}

この場合、サブクラスのすべてのコンストラクターはすぐにスーパークラスのコンストラクターを呼び出します。自分のコンストラクターを再度参照して、null値を渡すことができると思いました。

public ListBubbleSort()
{
  this( null, null );
}

public ListBubbleSort( List<E> li )
{
   this( li, null );
}

public ListBubbleSort( Comparator<E> com )
{
   this( null, com );
}

public ListBubbleSort( List<E> li, Comparator<E> com )
{
   super( li, com );
}

そうすることで、抽象スーパークラスのオーバーロードされたコンストラクターを3つ省略できますが、すべてのサブクラスが同じパターンに従うように強制されます。

私の質問は:一貫性のある場合のより良いアプローチは何ですか?抽象スーパークラスまたはサブクラスで欠落している値を処理しますか?それはインスタンス化に関して違いを生むのでしょうか、それとも単なる意見の問題でしょうか?

17
L.Spillner

一貫性がある場合のより良いアプローチは何ですか?

  1. すべての子コンストラクターをプライベートにします。
  2. 静的ファクトリメソッドを導入します。

    _ListBubbleSort.withList(List<E> list)
    ListBubbleSort.withComparator(Comparator<E> comparator)
    _
  3. 適切なsuperコンストラクターを呼び出します。 nullsを渡さないでください。

    _public static <E> ListBubbleSort withList(List<E> list) {
        return new ListBubbleSort(list);
    }
    
    private ListBubbleSort(List<E>) {
        super(list);
    }
    
    protected ListSortierer(List<E>) {
        // initialise only the list field
        this.Origin = list;
    }
    _
  4. フィールドとしてOptionalを使用しないでください。

    this.original = Optional.ofNullable(li);

  5. 3つ以上のパラメーターがある場合は、 ビルダーパターン を検討してください。

抽象スーパークラスまたはサブクラスで欠落している値を処理しますか?

コンストラクターは初期値を提供することになっています。あなたは初期値を渡していない、あなたはただそれらの不在を示している

デフォルトでは、nullは参照型の初期値です。したがって、フィールドの値が指定されていない場合は、フィールドを再割り当てする必要はありません。

それはインスタンス化に関して違いを生むのでしょうか、それとも単なる意見の問題でしょうか?

読みやすさ、メンテナンス。


読むことをお勧めします Effective Java by Joshua Bloch

オブジェクトの作成と破棄

  • 項目1:コンストラクターではなく静的ファクトリメソッドを検討する
  • 項目2:多くのコンストラクターパラメーターに直面したときにビルダーを検討する
14
Andrew Tobilko

質問自体について:両方のオプションは理想的ではないと思います。

あなたはできるだけ少ないコードを書くように努めます。 便利に見えるという理由だけで、オーバーロードの追加には注意が必要です。実際には、逆のことを行う必要があります。実際のユースケースが何であるかを非常によく考え、それらのみをサポートしてください。

そしてあなたの場合、演習の全体的なポイントは、さまざまな実装でソートできるようにすることだと思われます。その意味では、たとえば 戦略パターン を調べる必要があります。

言い換えれば、あなたのfirstの考えは、常に継承よりも構成を優先する。そして、あなたのデザインがそのような質問にあなたを導くとき、より良い答えはあなたの現在のデザインから離れて、ある種の「サービス」として異なるソートを可能にする方法を見つけることかもしれません- list自体を並べ替えに戻す代わりに。

そうすることで、抽象スーパークラスのオーバーロードされたコンストラクターを3つ省略できますが、すべてのサブクラスが同じパターンに従うように強制されます。

契約の実施は、抽象的なメソッドまたはインターフェースを介して行われます。すべてのサブクラスにこれらのコンストラクターがあるかどうかはわかりません。少なくとも、すべてのサブクラスがこれらのコンストラクターを適切に追加するかどうかはわかりません。

したがって、 Encapsulation を考慮すると、これらのコンストラクターはスーパークラスの方が適しています。

2
isah

そうすることで、抽象スーパークラスのオーバーロードされたコンストラクターを3つ省略できますが、すべてのサブクラスが同じパターンに従うように強制されます。

サブクラスには、親クラスの特定のコンストラクターを呼び出すための制約はありません。これがそれらの1つを呼び出す間、それはそれらのいずれかを呼び出すことができます。
したがって、そのような要件を達成することはできませんでした。

静的ファクトリメソッドを使用したソリューションについては、許容できる場合もあれば問題ない場合もありますが、奇跡ではなく、いくつかの制限もあります。
例:別の実装に切り替えることはできません。
JDKクラスの場合、これが問題になることはほとんどないことに同意しますが、カスタムクラスの場合は、慎重に使用する必要があります。
その上、サブクラスが親クラスを拡張し、このサブクラスのインスタンスを作成するためにファクトリを通過しないことを妨げるものは何もありません。
したがって、引用された要件も保証されません。

私の質問は:一貫性のある場合のより良いアプローチは何ですか?抽象スーパークラスまたはサブクラスで欠落している値を処理しますか?それはインスタンス化に関して違いを生むのでしょうか、それとも単なる意見の問題でしょうか?

それは意見の問題ではありません。それは重要です。
サブクラスが親クラスに委任し、チェックが常に同じである場合、親クラスにチェックを実行させるのは興味深いようです。
ただし、親クラスがチェックに関して変更されないという保証がない場合、親クラスがチェックを変更すると、サブクラスによって不整合インスタンスが作成される可能性があります。

2
davidxxx

最後のaddAllを追加して、オプションの追加として子コンストラクターで呼び出すことができます。コンパレータのセマンティクスは異なりますが、オプションの場合は(ほとんど)オーバーロードする必要があります。

private final Optional<Comparator<E>> comparatorOption;

public final void addAll(List<E> li) {
    ...;
}

protected ListSortierer() {
    comparatorOption = Optional.empty();     
}

protected ListSortierer(Comparator<E> comp) {
    comparatorOption = Optional.of(comp);     
}

Nullパラメータを回避する方が良いようです。

必要に応じて、多くのコンストラクターを使用してすべてのサブクラスに同じコンストラクターを指定することは悪くありません。利点は、すべてのクラスのAPIが同じように動作することです。ただし、ビルダーパターンで防止できる不要なボイラープレートコードは不要です。元の古いJavaでは、多くのコンストラクターが使用されていましたが、最近ではAPIに多くのビルダーが含まれています。

リストソーターの場合、ある種のビルダーを使用した流暢なAPIがアイデアかもしれません。

したがって、最終的な答えは、よりコンテキストスタイルの決定です。

1
Joop Eggen