1. 소개

이 예제에서는 Java의 Service Locator 디자인 패턴에 대해 알아봅니다 .

개념을 설명하고 예제를 구현하며 사용의 장단점을 강조합니다.

2. 패턴의 이해

Service Locator 패턴의 목적은 요청 시 서비스 인스턴스를 반환하는 것입니다. 이는 구체적인 클래스에서 서비스 소비자를 분리하는 데 유용합니다.

구현은 다음 구성 요소로 구성됩니다.

  • 클라이언트 – 클라이언트 개체는 서비스 소비자입니다. 서비스 로케이터에서 요청을 호출하는 역할을 합니다.
  • 서비스 로케이터 – 캐시에서 서비스를 반환하기 위한 통신 진입점입니다.
  • 캐시 – 나중에 재사용하기 위해 서비스 참조를 저장하기 위한 객체
  • Initializer – 캐시에서 서비스에 대한 참조를 생성하고 등록합니다.
  • 서비스 – 서비스 구성 요소는 원래 서비스 또는 해당 구현을 나타냅니다.

원래 서비스 객체는 로케이터에 의해 조회되고 요청 시 반환됩니다.

3. 시행

이제 실습을 통해 예제를 통해 개념을 살펴보겠습니다.

먼저 다양한 방법으로 메시지를 보내기 위한 MessagingService  인터페이스를 만듭니다.

public interface MessagingService {

    String getMessageBody();
    String getServiceName();
}

다음으로 이메일과 SMS를 통해 메시지를 보내는 위 인터페이스의 두 가지 구현을 정의합니다.

public class EmailService implements MessagingService {

    public String getMessageBody() {
        return "email message";
    }

    public String getServiceName() {
        return "EmailService";
    }
}

SMSService 클래스 정의는 EmailService 클래스와 유사 합니다 .

두 서비스를 정의한 후 이를 초기화하는 논리를 정의해야 합니다.

public class InitialContext {
    public Object lookup(String serviceName) {
        if (serviceName.equalsIgnoreCase("EmailService")) {
            return new EmailService();
        } else if (serviceName.equalsIgnoreCase("SMSService")) {
            return new SMSService();
        }
        return null;
    }
}

서비스 로케이터 개체를 함께 배치하기 전에 필요한 마지막 구성 요소는 캐시입니다.

이 예에서 이것은 List 속성 이 있는 간단한 클래스입니다 .

public class Cache {
    private List<MessagingService> services = new ArrayList<>();

    public MessagingService getService(String serviceName) {
        // retrieve from the list
    }

    public void addService(MessagingService newService) {
        // add to the list
    }
}

마지막으로 서비스 로케이터 클래스를 구현할 수 있습니다.

public class ServiceLocator {

    private static Cache cache = new Cache();

    public static MessagingService getService(String serviceName) {

        MessagingService service = cache.getService(serviceName);

        if (service != null) {
            return service;
        }

        InitialContext context = new InitialContext();
        MessagingService service1 = (MessagingService) context
          .lookup(serviceName);
        cache.addService(service1);
        return service1;
    }
}

여기의 논리는 상당히 간단합니다.

클래스는 캐시 의 인스턴스를 보유합니다 . 그런 다음 getService() 메서드에서 서비스 인스턴스에 대한 캐시를 먼저 확인합니다.

그런 다음 null이면 초기화 논리를 호출하고 새 개체를 캐시에 추가합니다.

4. 테스트

이제 인스턴스를 얻는 방법을 살펴보겠습니다.

MessagingService service 
  = ServiceLocator.getService("EmailService");
String email = service.getMessageBody();

MessagingService smsService 
  = ServiceLocator.getService("SMSService");
String sms = smsService.getMessageBody();

MessagingService emailService 
  = ServiceLocator.getService("EmailService");
String newEmail = emailService.getMessageBody();

ServiceLocator 에서 EmailService처음 가져올 때 새 인스턴스가 생성되고 반환 됩니다. 그런 다음 다음에 호출하면 EmailService 가 캐시에서 반환됩니다.

5. 서비스 로케이터와 의존성 주입

언뜻 보기에 서비스 로케이터 패턴은 다른 잘 알려진 패턴, 즉 의존성 주입과 비슷해 보일 수 있습니다.

첫째, 의존성 주입과 서비스 로케이터 패턴 모두 Inversion of Control 개념의 구현이라는 점에 유의하는 것이 중요합니다 .

계속 진행하기 전에 이 글 에서 의존성 주입에 대해 자세히 알아보세요 .

여기서 주요 차이점은 클라이언트 개체가 여전히 의존성을 생성한다는 것입니다 . 이를 위해 로케이터를 사용합니다. 즉, 로케이터 개체에 대한 참조가 필요합니다.

이에 비해 의존성 주입을 사용하면 클래스에 의존성이 부여됩니다. 인젝터는 클래스에 의존성을 주입하기 위해 시작 시 한 번만 호출됩니다.

마지막으로 Service Locator 패턴을 사용하지 않는 몇 가지 이유를 살펴보겠습니다.

이에 반대하는 한 가지 주장은 단위 테스트를 어렵게 만든다는 것입니다. 의존성 주입을 사용하면 종속 클래스의 모의 개체를 테스트된 인스턴스에 전달할 수 있습니다. 반면에 이것은 Service Locator 패턴의 병목 현상입니다.

또 다른 문제는 이 패턴을 기반으로 API를 사용하는 것이 더 까다롭다는 것입니다. 그 이유는 의존성이 클래스 내부에 숨겨져 있고 런타임에만 확인되기 때문입니다.

이 모든 것에도 불구하고 Service Locator 패턴은 코딩과 이해가 쉬우며 소규모 애플리케이션에 적합한 선택이 될 수 있습니다.

6. 결론

이 사용방법(예제)는 Service Locator 디자인 패턴을 사용하는 방법과 이유를 보여줍니다. Service Locator 디자인 패턴과 의존성 주입 개념 간의 주요 차이점에 대해 설명합니다.

일반적으로 응용 프로그램에서 클래스를 디자인하는 방법을 선택하는 것은 개발자에게 달려 있습니다.

Service Locator 패턴은 코드를 분리하는 간단한 패턴입니다. 그러나 여러 응용 프로그램에서 클래스를 사용하는 경우 의존성 주입이 올바른 선택입니다.

평소와 같이 전체 코드는  Github 프로젝트 에서 사용할 수 있습니다 .

Generic footer banner