Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service Locator 패턴이란 ? #23

Open
ChoiSeEun opened this issue Oct 16, 2023 · 1 comment
Open

Service Locator 패턴이란 ? #23

ChoiSeEun opened this issue Oct 16, 2023 · 1 comment
Assignees
Labels
ch24 about chapter24 🎊week5

Comments

@ChoiSeEun
Copy link
Contributor

👍 문제

p.425 ~ 426 에서는 애플리케이션 점검 시 확인해야 할 패턴 및 아키텍처에 관해 얘기 하고 있습니다.
이 중 Service Locator 패턴에 대해서 잘 알고 있으면 여러모로 도움이 되지 않을까 생각이 들었습니다.
잠깐 검색해보니 DI (#2)와 함께 언급되던데 , 부담이 되지 않는다면 연관해서 같이 정리해봐도 좋지 않을까 합니다!

✈️ 선정 배경

이전에 디자인 패턴 찾아보면서 못 본 패턴이기도 하고, 마침 또 DI와 함께 언급되길래
이제 스프링을 (처음) 배우는 지금 시즌에 다 같이 정리해보면 좋을 것 같아서 선정했습니다.

📺 관련 챕터 및 레퍼런스

Story24. 애플리케이션에서 점검해야 할 대상들

🐳 비고

(only 희망사항) 가능하다면 실제 코드에서 패턴을 확인해볼 수 있으면 좋겠습니다 !

@kgh2120 kgh2120 added 🎊week5 ch24 about chapter24 labels Oct 16, 2023
@olrlobt
Copy link
Contributor

olrlobt commented Oct 20, 2023

Service Locator 패턴과 DI

들어가며,

Service Locator과 Dependency Injection (DI) 패턴은 객체 지향 프로그래밍에서 객체의 의존성을 관리하기 위한
디자인 패턴이다.

이 중, Service Locator 패턴은 조금만 검색해도 안티 패턴이냐에 대한 의문을 갖는 글들이 많이 보인다.

Service Locator패턴은 몇 가지 이유들로 비판받기도 하지만, 특정 상황에서는 아주 유용하게 쓰이기도 하는 패턴이다.
하지만, 많은 사람들은 Dependency Injection (DI) 패턴이 Service Locator의 패턴의 단점을 해결하면서,
코드의 의존성 주입을 더욱 명확하게 관리할 수 있다고 믿는다.

그러면, Service Locator패턴과 DI 패턴에 대해 어떤 차이가 있는 지 알아보자.

의존성 주입

img

위와 같이 종속성이 있는 클래스들에서, new로 객체간의 종속성을 연결한다면 어떻게 될까?

img_1

클래스가 자체적으로 종속성을 생성하거나 초기화 하는 경우, 그 클래스는 원래의 업무(1)와 종속성 관리(2)의
두 가지 책임을 지게 된다. 이는 객체지향 설계 원칙인 "단일 책임 원칙"을 위배하는 일이며, 클래스는
단 하나의 책임만 가져야한다.

또한, 특정 종속성이 구체적인 구현에 의존하게 된다면, 그 종속성을 변경하거나 대체하기 어려워지고,
클래스 간의 종속성 사이에 강한 결합이 발생하게 되어 시스템 전체의 유연성을 저하시키게 된다.

이를 해결하기 위해 사용하는 패턴이 Service Locator 패턴과, DI 패턴인 것이다.

Service Locator

Service Locator 패턴은 서비스를 사용하는 클라이언트와 서비스를 제공하는
구체적인 구현 간의 의존성을 줄이는 디자인 패턴이다.

쉽게 말해, 객체의 의존성을 중앙저장소(Service Locator)에 저장해 놓고, 필요할 때마다 찾아서 쓰는 패턴이라고 생각하면 쉽다.

public interface MessageService {
    void sendMessage(String message);
}

public class EmailService implements MessageService {
    public void sendMessage(String message) {
        System.out.println("Email sent: " + message);
    }
}

public class ServiceLocator { // 중앙 관리 저장소
    private static Map<String, MessageService> services = new HashMap<>(); // key : value로 관리

    public static void registerService(String name, MessageService service) {
        services.put(name, service);
    }

    public static MessageService getService(String name) {
        return services.get(name);
    }
}

public class Notification {
    public void notify(String message) {
        MessageService service = ServiceLocator.getService("emailService"); // 의존성 가져오기
        service.sendMessage(message);
    }
}
public class Application {
    public static void main(String[] args) {
        ServiceLocator.registerService("emailService", new EmailService());
        Notification notification = new Notification();
        notification.notify("홍박사님을 아세요?");
    }
}

이렇게 Notification 클래스는 ServiceLocator를 사용하여 필요한 서비스를 검색하고 있다.
이렇게 되면 Notification 과 ServiceLocator 간의 결합도가 발생게 된다.

img_2

장점:

  • 유연성:
    서비스의 실제 구현을 변경하거나 새로운 서비스를 추가하더라도 클라이언트 코드를
    수정할 필요가 없다.
  • 중앙화:
    모든 서비스 검색 요청이 Service Locator를 통해 이루어지므로 관리 및 유지 보수가
    간편하다.
  • 제어의 역전:
    서비스의 생명주기 및 초기화를 중앙에서 관리할 수 있다.

단점:

  • 의존성:
    클라이언트와 Service Locator 사이의 강력한 결합이 발생한다.
    이로 인해 Service Locator가 시스템 전체에 걸쳐 전역 상태와 같이 동작할 수 있다.
  • 테스트 어려움:
    Service Locator를 사용하는 클라이언트를 독립적으로 테스트하기가 어렵다.
  • 디버깅:
    서비스가 Service Locator에 제대로 등록되지 않았을 경우, 문제의 원인을 찾기
    어려울 수 있다.

문제점 :

  1. Service Locator가 컴파일 시간 오류를 런타임으로 미룬다.

Service Locator는 패턴은 일반적으로 서비스 요청을 런타임에 처리하기 때문에,
일부 문제점이나 오류는 컴파일 시간에서 발견되지 않고 런타임에서만 나타날 수 있다.

  1. 서비스 로케이터는 인터페이스 분리 원칙을 위반 했다는 비난을 받았다.

클라이언트가 필요로 하는 서비스만 찾기 위해 서비스 로케이터 인터페이스를 사용하면,
클라이언트는 실제로 필요하지 않은 여러 다른 서비스에 대한 메서드나 접근 방법에도 의존하게 될 수 있다.
이로 인해 서비스 로케이터는 범용적이고 "과도하게 큰" 인터페이스를 제공하게 될 수 있으며,
이는 ISP(인터페이스 분리 원칙)의 주요 개념에 반하는 것으로 간주될 수 있다.

  1. 취약성
  2. 강한 결합

결론적으로, Service Locator 패턴은 특정 상황에서 서비스의 결합도를 줄이는 데
도움이 될 수 있지만, 과도하게 의존하게 되면 여러 문제점을 유발할 수 있다.
따라서 사용 시 상황과 목적을 명확하게 판단하고 적용해야 한다.

Dependency Injection(DI)

Dependency Injection(DI) = 의존성 주입
의존성 주입은 객체가 자신의 종속성을 직접 생성하거나 검색하는 대신 외부에서 종속성을 제공받는 방식이다.
종속성은 주로, 생성자주입, 세터주입, 필드주입 등이 있다.

public interface MessageService {
    void sendMessage(String message);
}

public class EmailService implements MessageService {
    public void sendMessage(String message) {
        // send email
        System.out.println("Email sent: " + message);
    }
}

public class Notification {
    private final MessageService service;

    public Notification(MessageService service) {
        this.service = service;
    }

    public void notify(String message) {
        service.sendMessage(message);
    }
}
public class Application {
    public static void main(String[] args) {
        MessageService emailService = new EmailService();
        Notification notification = new Notification(emailService);
        notification.notify("Hello via Email!");
    }
}

여기서는 Notification 클래스가 MessageService의 특정 구현에 의존하지 않는다.
대신, 외부에서 주입된 종속성을 사용한다.

img_3

DI는 Service Locator에서의 문제를 어느정도 해결은 하지만, 종속성을 구성하고 생성하는 전용 클래스가 여전히
존재하는 패턴이다.

장점:

  • 객체 간의 결합도 감소:
    DI를 사용하면 객체 간의 결합도를 낮출 수 있다. 이로 인해 각 객체는 독립적으로 동작하며, 다른 객체의 변경에 덜 영향을 받게 된다.
  • 유연성 및 확장성: \
  • 테스트 용이성:\
  • 명확한 의존성 관계

단점:

  • 학습 곡선:
    DI와 DI 프레임워크(예: Spring, Guice)를 처음 접하는 개발자에게는 초기 학습 곡선이 있을 수 있다.
  • 과도한 추상화:
    때로는 너무 많은 추상화나 인터페이스가 필요 없는 경우에도 사용될 수 있다. 이로 인해 코드의 복잡성이 높아질 수 있다.

ref.
서비스 로케이터는 안티패턴이다.
https://edykim.com/ko/post/service-locator-is-an-antipattern/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ch24 about chapter24 🎊week5
Projects
None yet
Development

No branches or pull requests

3 participants