2

単純な(そして長い)質問であり、単純な答えではありません。いくつかのDIフレームワーク(Spring、Guice)を使用して作業することで、他の人が提示した実践のいくつかは、実装がそれほど簡単ではないという結論に達しました。私は本当にこのレベルで立ち往生しているようです。

詳細の一部が失われる可能性がありますが、これをできるだけ単純に提示するようにします。質問が明確になることを願っています。

文字列の検証を担当する単純なクラスであるStringValidatorがあるとします。

package test;

import java.util.ArrayList;
import java.util.List;

public class StringValidator {
    private final List<String> stringList;
    private final List<String> validationList;

    private final List<String> validatedList = new ArrayList<String>();

    public StringValidator(final List<String> stringList, final List<String> validationList) {
        this.stringList = stringList;
        this.validationList = validationList;
    }

    public void validate() {
        for (String currentString : stringList) {
            for (String currentValidation : validationList) {
                if (currentString.equalsIgnoreCase(currentValidation)) {
                    validatedList.add(currentString);
                }
            }
        }
    }

    public List<String> getValidatedList() {
        return validatedList;
    }
}

依存関係は可能な限り低く、次のような簡単なテストが可能です。

package test;

import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class StringValidatorTest {
    @Test
    public void testValidate() throws Exception {
        //Before
        List<String> stringList = new ArrayList<String>();
        stringList.add("FILE1.txt");
        stringList.add("FILE2.txt");

        final List<String> validationList = new ArrayList<String>();
        validationList.add("FILE1.txt");
        validationList.add("FILE20.txt");

        final StringValidator stringValidator = new StringValidator(stringList, validationList);

        //When
        stringValidator.validate();

        //Then
        Assert.assertEquals(1, stringValidator.getValidatedList().size());
        Assert.assertEquals("FILE1.txt", stringValidator.getValidatedList().get(0));
    }
}

柔軟性をさらに高めたい場合は、List<>の代わりにCollection<>を使用できますが、それは必要ないと仮定しましょう。

リストを作成するクラスは次のとおりです(他のインターフェイス標準を使用)。

package test;

import java.util.List;

public interface Stringable {
    List<String> getStringList();
}

package test;

import java.util.ArrayList;
import java.util.List;

public class StringService implements Stringable {

    private List<String> stringList = new ArrayList<String>();

    public StringService() {
        createList();
    }

    //Simplified
    private void createList() {
        stringList.add("FILE1.txt");
        stringList.add("FILE1.dat");
        stringList.add("FILE1.pdf");
        stringList.add("FILE1.rdf");
    }

    @Override
    public List<String> getStringList() {
        return stringList;
    }
}

と:

package test;

import java.util.List;

public interface Validateable {
    List<String> getValidationList();
}

package test;

import java.util.ArrayList;
import java.util.List;

public class ValidationService implements Validateable {

    private final List<String> validationList = new ArrayList<String>();

    public ValidationService() {
        createList();
    }

    //Simplified...
    private void createList() {
        validationList.add("FILE1.txt");
        validationList.add("FILE2.txt");
        validationList.add("FILE3.txt");
        validationList.add("FILE4.txt");
    }

    @Override
    public List<String> getValidationList() {
        return validationList;
    }
}

そして、mainメソッドを持つMainクラスがあります。

package test;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        Validateable validateable = new ValidationService();
        final List<String> validationList = validateable.getValidationList();

        Stringable stringable = new StringService();
        final List<String> stringList = stringable.getStringList();

        //DI
        StringValidator stringValidator = new StringValidator(stringList, validationList);
        stringValidator.validate();

        //Result list
        final List<String> validatedList = stringValidator.getValidatedList();
    }
}

したがって、クラスが実行時に(ユーザーが望むときに)リストを生成するとします。「直接」(静的)バインディングは不可能です。

可能な限り低い結合を提供したい場合は、リストを使用して、validation(StringValidator)の実行に必要なデータを提供します。

ただし、コンテナを使用して「グルーコード」を支援する場合は、2つの「サービス」をStringValidatorに挿入できます。それは正しいデータを提供しますが、カップリングが犠牲になります。また、StringValidatorには、実際に依存関係を呼び出すという追加の責任があります。

そのように委任を使用すると、不要な責任(私が望むものではない)でコードが乱雑になります。

そうしないと、これが機能する方法がわかりません(プロバイダーは正しいリストを提供してくれますが、ここでも依存関係があります)。

より一般的な質問は、DIフレームワークを使用して完全に分離されたアプリケーションを実際に作成する方法はありますか、それともこれはある種の理想ですか?DIフレームワークを使用する状況では、使用しませんか?DIフレームワークは本当に「新しい」ものですか?

ありがとうございました。

4

3 に答える 3

4

これは、特に不自然な例であるため、答えるのがやや難しい質問ですが、クラスが既に希望どおりに設計されていると仮定すると、ここでの依存性注入の正しい適用は簡単です。あなたはStringValidatorのテスト容易性と、依存性注入を使って何か魔法をかけようとすることに集中しているようです。妥当性について心配する必要があるのは、メインクラスです。ここで、密結合とテスト不可能なコードを導入し、DIコンテナーがその値を示す場所です。DIとIoCの原則を適切に適用すると、代わりに次のような結果になる可能性があります。

public class Main {
    @Autowired
    private Validateable validateable;
    @Autowired
    private Stringable stringable;

    public void main() {
        final List<String> validationList = validateable.getValidationList();
        final List<String> stringList = stringable.getStringList();
        StringValidator stringValidator = new StringValidator(stringList, validationList);
        stringValidator.validate();
        final List<String> validatedList = stringValidator.getValidatedList();
    }

    public static void main(String[] args) {
        Container container = new ...;
        container.get(Main.class).main();
    }
}

つまり、すべての手動配線がDIコンテナの制御に引き継がれるだけです。それが要点です。個人的には、コードによってインスタンス化されている「コンポーネント」クラス(StringValidator)のように見えるものがまだあるため、これには満足できません。コード内のこのハードな依存関係を取り除き、その作成をコンテナーにも引き継ぐために、物事を再設計する方法を検討します。

この「新しい」に関しては、いいえ、DIコンテナは新しいものではありません。彼らはかなり長い間存在しています。「1つ使用する必要がありますか?」という意味の場合、パターンは特定の実装よりも重要ですが、私の答えは一般的に「はい」になると思います。メリットは十分に確立され、受け入れられており、実際のフレームワークというよりも考え方です。先ほど説明したように、メインクラスは本質的にプリミティブDIコンテナでした。

更新:主な関心事がStringValidatorへの入力を処理する方法である場合、いくつかのオプションがあります。「stringList」と「validationList」をDIコンテナーで管理できず、StringValidatorに挿入できない理由はありません。次に、それらのリストのソースはコンテナ次第です。それらはあなたの他のオブジェクトから来るか、テストによって注入される可能性があります。あるいは、StringValidatorが入力を取得する方法に関する抽象化を変更したいとお考えですか?その場合、おそらくこのようなものがあなたのニーズによりよく合うでしょう:

public class StringValidator {
    private SourceOfStrings stringSource;
    private SourceOfStrings validationStringSource;

    private final List<String> validatedList = new ArrayList<String>();

    ...

    public void validate() {
        for (String currentString : stringSource.getStrings()) {
            for (String currentValidation : validationStringSource.getStrings()) {
                if (currentString.equalsIgnoreCase(currentValidation)) {
                    validatedList.add(currentString);
                }
            }
        }
    }

    public List<String> getValidatedList() {
        return validatedList;
    }
}

interface SourceOfStrings {
    List<String> getStrings();
}

注:スレッドセーフではありません。スレッド環境では、結果をフィールドに格納する必要をなくし、追加のメソッド呼び出しを呼び出して取得するために、追加の手順を実行します。

于 2012-04-30T14:16:07.133 に答える
0

少し戸惑いますが、クラスに依存せずにDIを使いたいということですか?これはアノテーションまたはカスタムクラスローダーで可能かもしれませんが、それは遅く、信じられないほど難しいでしょう。多分あなたはあなたが望むものを明確にすることができますか?

于 2012-04-30T14:13:50.853 に答える
0

やっと手に入れたと思います!私の質問に情報が不足してすみません。Ryan Stewartは、「「stringList」と「validationList」をDIコンテナーで管理して、StringValidatorに挿入できない理由はありません」と書いています。おそらく、彼はこれを念頭に置いていました。もしそうなら、それよりも私が探していた答えであり、あなたの答えは正しいので、ありがとうございます。春に実験して自分で見つけました。

リストを含むクラスを使用すると、結果のクラスはリストを受け取ることができません。それらは動的に作成され、StringValidatorにそれらを持ってくる方法を見ませんでした。動的に意味する-コンテナの制御なし。

私がそれらを注入することができた唯一の方法は、それらをStringValidatorに直接注入することでした。

しかし、私は一つのことを忘れました。春ははるかに柔軟性があります(私の経験に基づく)-私は率直に言って、Guiceでこれをどのように解決できたのかわかりません(実際には試していません、多分私はそれを試してみます)。

リストを動的に作成し、コンテナライフでそのリストを、必要なクラスを挿入するために使用できるリストとして使用してみませんか?

ここに画像の説明を入力してください

重要なのは、コンテナがリストを初期化するときです。

package test;

import org.springframework.stereotype.Component;

import java.util.ArrayList;

@Component
public class StringList extends ArrayList<String> {
}

package test;

import org.springframework.stereotype.Component;

import java.util.ArrayList;

@Component
public class ValidationList extends ArrayList<String> {
}

または、xmlの方法を好む場合(コメント):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
    <context:component-scan base-package="test"/>

    <!--<bean id="validationList" class="java.util.ArrayList" scope="singleton"/>-->
    <!--<bean id="stringList" class="java.util.ArrayList" scope="singleton"/>-->
</beans>

そのリストは、コンテナ、つまりアプリケーションの存続期間を通じて使用できます。

package test;

import org.springframework.stereotype.Component;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@Component
public class StringService implements Stringable {

    private List<String> stringList;

    @Inject
    public StringService(final ArrayList<String> stringList) {
        this.stringList = stringList;
        createList();
    }

    //Simplified
    private void createList() {
        stringList.add("FILE1.txt");
        stringList.add("FILE1.dat");
        stringList.add("FILE1.pdf");
        stringList.add("FILE1.rdf");
    }

    @Override
    public List<String> getStringList() {
        return stringList;
    }
}

package test;

import org.springframework.stereotype.Component;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@Component
public class ValidationService implements Validateable {

    private List<String> validationList;

    @Inject
    public ValidationService(final ArrayList<String> validationList) {
        this.validationList = validationList;
        createList();
    }

    //Simplified...
    private void createList() {
        validationList.add("FILE1.txt");
        validationList.add("FILE2.txt");
        validationList.add("FILE3.txt");
        validationList.add("FILE4.txt");
    }

    @Override
    public List<String> getValidationList() {
        return validationList;
    }
}

また、サービスについて心配する必要はありません。リストはコンテナ内にあり、独自のライフサイクルを実行しているため、リクエストするたびに利用できます。

package test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class StringValidator {
    private List<String> stringList;
    private List<String> validationList;

    private final List<String> validatedList = new ArrayList<String>();

    @Autowired
    public StringValidator(final ArrayList<String> stringList,
                           final ArrayList<String> validationList) {
        this.stringList = stringList;
        this.validationList = validationList;
    }

    public void validate() {
        for (String currentString : stringList) {
            for (String currentValidation : validationList) {
                if (currentString.equalsIgnoreCase(currentValidation)) {
                    validatedList.add(currentString);
                }
            }
        }
    }

    public List<String> getValidatedList() {
        return validatedList;
    }
}

答えは実はとてもシンプルに見えますが、ここにたどり着くまでに少し時間がかかりました。

つまり、Mainクラスはこのようになり、すべてがコンテナによって処理されます。

package test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class Main {
    @Autowired
    private StringValidator stringValidator;

    public void main() {
        stringValidator.validate();
        final List<String> validatedList = stringValidator.getValidatedList();
        for (String currentValid : validatedList) {
            System.out.println(currentValid);
        }
    }

    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");
        container.getBean(Main.class).main();
    }
}

可能だと思います。したがって、答えを要約すると、コンテナ内に動的に作成されたクラスを常に持つことができ、かなり良い分離が可能です!

于 2012-05-02T13:28:27.027 に答える