2016-11-23 7 views
1

考えると、二つのクラスのAとBのBは、我々はそのAオブジェクトも作成しておく必要がありBオブジェクトを作成する瞬間にオブジェクトA. が含まれています。 質問:Bのコンストラクタまたはデカレーションの瞬間にAをインスタンス化する方が良いですか?のは、その簡単な例を見てHAVAましょう:ベストな方法 - Javaの

public Class A{ 
//Staff 
} 

- 最初の提案

public Class B{ 
//staff 
A a; 
public B(){ 
a = new A() 
} 

}

- 第2案

public Class B{ 
//staff 
A a = new A(); 
public B(){ 
//staff 
} 
} 

だから何がありますthの差2つのソリューション?

+1

nullポインタ例外を回避するために、宣言時にインスタンス化するのが一般的です。 しかし、この場合は、とにかくインスタンス化しているので、実際には問題ではないと思います。しかし、前のアプローチでは、コンストラクタの定義をスキップできます。 – Jay

+2

チェック[this anwser](http://stackoverflow.com/questions/4916735/default-constructor-vs-inline-field-initialization) –

+0

は、新しい 'B'を作成するときに異なる' A'が必要です – XtremeBaumer

答えて

1

コンストラクタにパラメータを追加できるので、最初のものを使用することをお勧めします。

public Class B { 
    A a; 
    public B() { 
     a = new A(); 
    } 
    public B(A a) { 
     this.a = a; 
    } 
} 
+0

初期値は、(あなたの例のように、与えられたサイズの配列や特定の値の整数)常に同じ初期値が必要なときには良いですが、 変数を別々に(つまり異なる値で)初期化するコンストラクタがたくさんある場合、変更がオーバーライドされ、無駄になるため、初期化子は役に立たなくなります。 – Jay

1

具体的な例では2つの違いはありません。

は、オブジェクトの作成がすべてのコンストラクタで共有されるため、通常は2番目のアプローチ(初期化子)を使用します。

B()のパラメータリストにあるA()にパラメータを渡す必要がある場合は、パラメータをフィールド初期化子に渡すことができないため、最初の選択肢のみが使用できます。

1

実際には違いはありません。コンパイラは、初期化コードをコンストラクタ本体に移動します。それはちょうどあなたのコード準備についてです。

2

私はあなたがコンストラクタを介してそれを設定したい場合はBのコンストラクタにAのインスタンスを渡す、または適切なセッターを経由する必要がありどちらかだと思います。それ以外の場合は、2つのクラスを密接に結合する必要があります。

+0

これは私の答えのはるかに短いバージョンです: - D –

1

オブジェクトの作成は、ライフサイクル管理の問題の一部です。これは通常、別個に扱いたい責任です。一般的なパターンは以下のとおりです。

  • 依存性注入
  • ビルダー、抽象工場や工場方法
  • シングルトン、プロトタイプやオブジェクトプール

これらの上に読むには、たとえば、Googleがcreational patternsのために結果を確認し、 dependency injection

それはきれいで、テスト可能なコードにあなたの例と結果を照合最も簡単な方法ですので、私は依存性の注入にここに焦点を当てます。他のお気に入りのパターンはビルダーまたは工場です。

依存性の注入(または制御の反転)は、(通常のパターンと呼ばれていないが、私はそれが1だと思う)非常に一般的なパターンです。 D.I.を提供する最もよく知られているフレームワークSpringまたはJavaのCDIですが、以下のように手作業で行うこともできます。

コンストラクタ・インジェクションあなたの例では

依存性注入の簡単な使用法は次のように仕事ができる:

public Class Dependency { 
} 

public Class Application { 

    private final Dependency dependency; 

    public Application (Dependency dependency) { 
      this.dependency = dependency; 
    } 
} 

使用法:

public static void main(String[] args) { 
    Application application = new Application(new Dependency()); 
} 

ApplicationことDependencyを作成するのではなく、 Dependencyのインスタンスがそれに供給されることを期待するコンストラクタを介してこれはコンストラクタインジェクションと呼ばれます。この方法の利点は、依存関係フィールドを最終的にすることができることです。欠点は、多くの依存関係では、コンストラクターが理解できないほど多くの引数を取得する可能性があることです。

セッター・インジェクション

public Class Dependency { 
    public String doStuff(String input) { 
     return input + " stuffed"; 
    } 
} 

public Class Application { 

    private Dependency dependency; 

    public void setDependency(Dependency dependency) { 
      this.dependency = dependency; 
    } 

    public String letsGo(String input) { 
     String stuff = dependency.doStuff(input); 
     return "I called my dependency and it said: " + stuff ; 
    } 
} 

使用法:

public static void main(String[] args) { 
    Application application = new Application(); 
    application.setDependency(new Dependency()); 
    System.our.println(application.letsGo("rabbit")); 
} 

出力:

I called my dependency and it said: rabbit stuffed 

テストあなたはBuilderパターンを使用するか、セッター・インジェクションを使用することができることを避けるために、

今、テスト容易性について。単位テストでは、クラスをテストせずにクラスApplicationをテストできることを覚えておいてください。 ApplicationDependencyという独自のインスタンスを作成していた場合、これはやりにくいでしょう。依存性注入は簡単です。ここではかなり標準である、注釈でのJUnitとMockitoを使った例です:

@RunWith(MockitoJUnitRunner.class) 
public class ApplicationTest { 

    @InjectMocks 
    private Application instance; 
    @Mock 
    private Dependency dependency; 

    @Test 
    public void test() { 
     // SETUP 
     String testStuff = "some test stuff"; 
     String input = "Micky";   
     when(dependency.doStuff(input)).thenReturn(testStuff);    

     // CALL 
     String actualResult = instance.letsGo(input); 

     // VERIFY 
     verify(dependency).doStuff(input); 
     String expectedResult = "I called my dependency and it said: " + testStuff; 
     assertEquals(actualResult, expectedResult); 
    } 
} 

ここMockitoがあなたのためにApplicationのインスタンスを作成し、それに@Mockで注釈を付け任意のフィールドを噴射します。コンストラクタまたはセッターを介して自動的に決定します。

when(dependency.doStuff(input)).thenReturn(testStuff); 

モックDependencyは、呼び出されたときに特定の値を返すように指示します。これは、実際のDependencyコードを呼び出すのではなく、呼び出されたときにそのクラスから偽の結果を生成するようにMockitoに依頼することを意味します。指定されたコールが正確に一度行われ、これがない場合は、エラーが発生した場合

verify(dependency).doStuff(input); 

Mockitoチェック:次のように依存関係に予想される呼び出しが行われたかどうかをチェックすることができApplicationインスタンスを呼び出した後

場合。

assertEqualsを使用すると、実際の結果が予想される結果と比較されます。値が等しくなければ、テストを実行するとJUnitによってエラーが生成されます。

この主な教訓は、オブジェクトの作成は、理想的には、創造的パターンまたは依存性注入のいずれかを使って別々に扱われるべきであるということです。これを行うことで、主なタスクを実行することにクラスを集中させ、理解しやすくテストすることができます。

備考

D.I.それは十分ではないため、おそらくパターンとは見なされません。いくつかの点でそれを設計することができます。それでも、独自の依存関係のインスタンスを作成するコードを見るときはいつでも、コードのにおいを考慮して、それをリファクタリングする必要があります。

D.I.アプリケーションコンテキストと呼ばれる抽象ファクトリを使用します。スプリングは、単に@Autowired(又は@Inject)のフィールドに注釈を付けることにより、注入をサポート:

@Autowired 
private Dependency dependency; 

Dependencyインスタンスが通常@Configurationで注釈XML fileまたはクラスであるアプリケーションのコンテキストからバネにより得られます。アプリケーションコンテキストでは、どの依存関係(Beans)が存在するか、それらをどのように設定して配線するかを指定できます。

関連する問題