web-dev-qa-db-ja.com

大きな静的初期化子はコードの匂いですか?

AndroidでSimpleExpandableListAdapterを拡張しています。 Androidのアダプターは、コンストラクターにかなり複雑な引数が多数あり、セッターやビルダーがないため、あまりうまく実装されていないと思います。私のクラスでは、これらの引数のほとんどは呼び出し元のクラスに依存していないため、内部的に構築したいと思います。ただし、引数はネストされたListsと、プログラムで構築する必要がある整数と文字列の配列です。

superコンストラクターの前には何も呼び出すことができず、super呼び出しが戻る前にインスタンスメソッドを呼び出すことができないため、superから呼び出す静的メソッドがいくつかありますコール:

super(getContext(), initGroupData(), groupLayout, initGroupFrom(), initGroupTo(),
        initChildData(), childLayout, initChildFrom(), initChildTo());

これを処理する方法は3つあります。私と同じように静的メソッドを呼び出す、おそらくこれらと同じメソッドを呼び出して静的変数を初期化してsuper呼び出しで使用する、またはこれらすべてをカプセル化する大きな静的イニシャライザを使用するメソッドをビルダーに組み込みます。

今はビルダーに傾いていると思いますが、これを処理するためのより良い方法があるかどうか疑問に思っています。

8
TBridges42

静的ヘルパー関数を使用してコンストラクター引数を作成することは完全に正気のソリューションですが、これらの関数はそれぞれ正確に1つの引数を生成する必要があり、互いに通信できないため、実行できる操作が制限されます。

インターフェースConstructor(A, B, C)をより使いやすいインターフェースConstructor(X, Y)に適合させる最も一般的なケースでは、プライベートArgumentObjectを受け取るプライベートヘルパーコンストラクターを定義できます。既存のコンストラクタにチェーンします。次に、より使いやすいコンストラクターが静的ヘルパー関数を介してヘルパーコンストラクターにチェーンし、引数オブジェクトを作成します。

_class Constructor {
  // constructor you want to wrap
  public Constructor(A a, B b, C c) { ... }
  // better constructor you are defining
  public Constructor(X x, Y y) { this(createArgumentObject(x, y)); }
  // helper constructor using an ArgumentObject
  private Constructor(ArgumentObject ao) { this(ao.a, ao.b, ao.c); }
  // helper to create the argument object
  private static ArgumentObject createArgumentObject(X x, Y y) { ... }
  private static class ArgumentObject { ... }
}
_

同じクラス(C++ 03など)内にチェーンコンストラクターがない言語では、ヘルパーコンストラクターの中間サブクラスを定義する必要があります。

ただし、この手法は、コンストラクター引数での静的関数の使用を一般化したものにすぎません。あなたが提案した他のソリューションにはさまざまな欠点があります。そのため、それらを好む十分な理由がない限り、私はそれらを回避します。

  • 優れたビルダーを実装するには、非常に小さな価値のために多大な労力が必要です。新しいコンストラクターが十分に単純であれば、ビルダーなしで実行できます。ラップしているクラスが確実な検証を実行すると仮定すると、Constructor(new Constructor.Arguments {{ foo = 42; bar = baz; }})イディオムを使用して引数を渡すことができるように、引数オブジェクトをパブリックにすることができます。

  • 静的初期化ブロックで計算された静的変数を使用することは、真に静的なデータには意味がありますが、グローバル状態を回避するように注意する必要があります。初期化が非常に単純な場合を除き、初期化をテスト可能にするには、静的関数を使用してこれらの変数を初期化する必要があります。現在、静的メソッドを直接使用することの唯一の利点は、値が一度だけ計算され、すべての初期化で再利用されることです。

    あなたの質問はこれらの初期化がより複雑になる可能性があることを示しているので、静的な初期化子ブロックを使用することは、テスト容易性があなたにとって重要である場合、大きなノーノーです。 (そうでない場合は、より緊急の問題があります。)

9
amon