1

시스템에서 사용하는 언어에 따라 다른 버전이있는 여러 구성 요소가 있습니다 (구성 가능하며 런타임에 변경할 수 있음).여러 언어 구성 요소/클래스 [OOP/Patterns/IoC/DI]

public interface Tokenizer { 
    List<String> tokenize(String s); 
} 

public class EnglishTokenizer implements Tokenizer { 
    List<String> tokenize(String s) { ... }; 
} 

public interface ChineseTokenizer implements Tokenizer { 
    List<String> tokenize(String s) { ... }; 
} 

지금, 내 코드의 많은 클래스에서, 나는 언어가 특정 얻을 필요가 예를 들어, 나는 인터페이스 ("구성 요소") Tokenizer을 위해, 그래서 같은, 영어와 중국어 두 가지 구체적인 구현이 이러한 구성 요소 중 일부 (Tokenizer, Parser 및 기타 많은 구성 요소)를 구현했는데이를 달성하는 가장 우아한 방법이 무엇인지 궁금합니다. 나는 다음과 같은 방법 중 하나를 사용하여 생각 : 언어 enum 주어, 그 공장 (싱글 톤) 것 (예 : Tokenizer 등)

  • 각 구성 요소를, (그래서 같이 해당 언어 특정 구현을 반환 이) 많은 공장을 필요로 :

    public enum TokenizerFactory { 
        SINGLETON; 
        private Map<Language, Tokenizer> cache; 
        public getTokenizer(Language) { 
         return cache.get(Language); 
        } 
    } 
    
  • 가 (매우 큰) Language 클래스, 그쪽 되세요 t는 특정 언어 enum으로 인스턴스화되며 언어 별 구성 요소를 가져 오는 데는 여러 가지 방법이 있습니다. 그런 다음 런타임에 쉽게 언어 간 전환이 가능합니다 (이것이 내 목표 중 하나입니다). 그래서 같이 :
    public class Language { 
        public Language(LanguageEnum) {/* load language specific components*/}; 
        public Tokenizer getTokenizer() {/* language specific tokenizer */}; 
        public Parser getParser() {/* language specific parser */}; 
    } 
    

내가 할 노력하고있어 달성하기 위해 가장 적절한 방법은 무엇입니까

? 코드를 개선하려면 어떻게해야합니까?

답변

3

dependency injection을 사용하십시오.

Spring Framework은 매우 유용한 소프트웨어이고 내 개인적으로 좋아하지만 Google Guice과 같은 많은 대안이 있습니다.

Spring을 사용하면 언어별로 하나씩 2 개의 (3 개, 15 개 ...) 별도의 컨텍스트를 정의하고 적절한 컨텍스트에서 필요한 구성 요소를 얻을 수 있습니다. 두 번째 방법과 비슷하지만 Language 클래스를 사용하지 않아야합니다. 예를 들면 다음과 같습니다.

또 다른 대안은 단일 컨텍스트를 사용하지만 bean ID에 사용자의 언어를 접두사로 사용하는 것입니다."English-Tokenizer"및 "Chinese-Tokenizer"가 있습니다.

이전에 의존성 삽입을 사용한 적이없는 경우 공장 및/또는 동적 클래스 로딩을 통해 얻을 수있는 결과에 대해 너무 많은 작업처럼 들릴 수 있습니다 .-) 그러나 그렇지 않습니다. (구성 요소의 속성/종속성을 구성 할 수 있으므로 자신의 싱글 톤 등을 캐싱하거나 유지하는 것에 대해 걱정할 필요가 없습니다.) 일단 사용을 시작하면 사용하지 않은 채로 어떻게 살아 왔는지 궁금 할 것입니다. -)

업데이트 (두 번째 의견에서 질문에 대답).

다음은 샘플 "ComponentLocator"패턴입니다. ComponentLocator은 Spring에 의존성이없는 싱글 톤입니다. 인스턴스 (및 구현)는 컨텍스트에 의해 주입됩니다. ComponentLocator

public abstract class ComponentLocator { 
    protected static ComponentLocator myInstance; 

    protected abstract <T> T locateComponent(Class<T> componentClass, String language); 

    public static <T> T getComponent(Class<T> componentClass, String language) { 
    return myInstance.locateComponent(componentClass, language); 
    } 
} 

구현은 인터페이스 이름은 세미콜론 및 언어 (예를 들어, "영어 com.mypackage.Parser") 다음과 같이 맥락에서 콩의 이름을 지정하는 가정합니다. ComponentLocatorImpl은 컨텍스트에서 bean으로 선언되어야합니다 (bean 이름은 중요하지 않음). 다른 코드에서

public class ComponentLocatorImpl extends ComponentLocator 
    implements ApplicationContextAware { 
    private ApplicationContext myApplicationContext; 

    public void setApplicationContext(ApplicationContext context) { 
    myApplicationContext = context; 
    myInstance = this; 
    } 

    @Override 
    protected <T> T locateComponent(Class<T> componentClass, String language) { 
    String beanName = componentClass.getName() + ":" + language; 
    return componentClass.cast(myApplicationContext.getBean(beanName, componentClass)); 
    } 
} 

당신이 ApplicationContext를로드 할거야 (의() 주?) : 당신이 실제로 직접 다른 곳에서 컨텍스트를 참조 할 필요가 없습니다

ApplicationContext ctx = new ClasspathXmlApplicationContext("components.xml"); 

주 응용 프로그램. 위의 단지 하나의 가능한 방법입니다

Parser englishParser = ComponentLocator.getComponent(Parser.class, "English"); 
Parser chineseParser = ComponentLocator.getComponent(Parser.class, "Chinese"); 

참고 그것을 당신이 상황에서 당신의 언어 별 클래스를 가하고 거의 유일한 걸 가정합니다 : 당신이 당신의 구성 요소를 얻을 필요가 어디든지 당신은 않습니다. 귀하의 경우에는 아마도 (모든 언어를 동시에 사용할 수 있어야하기 때문에) 가장 좋을 것입니다. 그렇지 않으면 모든 수업 (언어 당 한 번)이 복제되므로 A/B/C 문제가 여기에 해당되지 않을 수 있습니다. 당신은 당신이 할 수있는 것은 A/B/C 의존성이있는 경우

는하지만 (나는 A, B, C를 믿고있어 인터페이스이며, Aimpl는 Bimpl, Cimpl 자신의 구현입니다) :

<bean id="A" class="Aimpl"> 
    <property name="B" ref="B"/> 
</bean> 

<bean id="B" class="Bimpl"> 
    <property name="C" ref="C"/> 
</bean> 

<bean id="C" class="Cimpl"> 
    <property name="tokenizer" ref="Tokenizer:English"/> 
</bean> 

구현에는 setB(), setC() 및 setTokenizer() 메소드가 있어야합니다. 후자도 가능하지만 이것은 생성자 주입보다 쉽습니다.

+0

감사합니다. DI 개념에 대한 기사를 읽었지만 아직 사용하지 않았습니다. 그러나 나는 그것에 대해 몇 가지 질문을 가지고있다 : i) 나는 그 콩들이 클래스의 ctor에 인수를 전달하기 위해 구성 될 수 있다고 가정한다. ii) 런타임에 "응용 프로그램 컨텍스트"(시스템에서 사용하는 언어)를 변경할 수 있으며 DI로 그렇게 할 수 있는지 궁금합니다. iii) 캐싱을 사용하면 englishContext.getBean ("Parser")를 두 번 호출하면 두 번째 캐싱은 항상 캐시 된 버전을 반환한다는 것을 의미합니다. iv) 전체 프레임 워크 대신 Spring의 IoC 컨테이너를 사용할 수 있습니까? –

+0

1) "제작자에게 인수를 전달"하는 것이 무슨 뜻인지 잘 모르겠습니다. 컨텍스트에서 bean 자체에 대한 특성 (또는 생성자 인수)을 지정할 수 있습니다. 2) 언어 별 컨텍스트를 사용하는 경우 ApplicationContext 인스턴스 세트가 있고 적절한 컨텍스트에서 빈 (예 : 파서)을 가져올 수 있습니다. 컨텍스트 자체는 필요할 때 런타임에 쉽게 다시로드 할 수 있습니다. 3) 기본적으로 yes입니다. 하지만 원하는 것이 아니라면 빈 범위를 변경하여 항상 새로운 인스턴스를 반환 할 수 있습니다. 4) 물론입니다. 필요한 모듈 (및 JAR) 만 선택할 수 있습니다. – ChssPly76

+0

예, 저는 생성자 인수를 말했습니다. 고마워요. 나는 확실히 DI를 시도해야한다고 생각합니다. 최종 질문 : (코드에서) 일반적으로 ApplicationContext를 저장하고 .getBean() 메소드를 호출하는 곳은 어디입니까? 당신을 돕기 위해 어떤 종류의 도우미 클래스가 있습니까? 또한 클래스 'C'를 생성하는 클래스 B를 생성하는 클래스'A '가 있고'Tokenizer '를 가져와야하는 클래스'C'가 있다고 가정 해 보겠습니다. 이 경우 DI를 사용하려면 'A', 'B'및 'C'의 생성자를 변경하여 'Tokenizer'를 인수로 사용하고 'A'에 주입하여 순서대로 처리해야합니다. 다른 수업을 듣기 위해? –

1

변경 사항을 고려하십시오. "새 언어 추가"및 "새 구성 요소 추가"와 같은 사용 사례. 얼마나 쉽게 이것을 할 수 있으며, 실수를 얼마나 쉽게 피할 수 있습니다.

지도가 첫 번째 경우에 어떻게 채워지는지 명확하지 않습니다. 어떤 종류의 등록 스키마가 있습니까? 완전성에 대한 책임이 여러 클래스에 걸쳐 퍼져 있다고 생각하는 것이 옳은 것인지 잘 알고 있습니다. 또한 나는 항상 Singletons의 의혹입니다.

두 번째 경우에는 새로운 conmponent를 추가하는 경우 언어 클래스에 하나의 새 메서드를 추가하고 제대로 작동하는지 확인해야합니다. 새로운 언어를 추가하는 것은 생성자에 국한되어있는 것처럼 보입니다 (아마도 몇 가지 추가 구현 메소드).

전체적으로 나는 두 번째 방법을 선호합니다.

+0

의견에 감사드립니다. map이 지연되면서 컴포넌트가로드된다 (필자는'getter' 메소드에서이를 생략했다). 이 경우 대부분의 구성 요소가 매우 무거 우므로 (메모리를 많이 사용하기 때문에) Singleton을 사용했기 때문에 한 번만로드 할 수 있습니다 (따라서 캐시합니다). –

0

나는 스프링 응답에 동의하며, IOC는 갈 길이 멀다. 비 프레임 워크 방식은 AbstractFactory을 사용하는 것입니다.

관련 문제