우리는 Singleton 이 얼마나 나쁜지 알고 있습니다. 그 이유는 의존성과 다른 이유로 숨기기 때문 입니다.
그러나 프레임 워크에는 한 번만 인스턴스화 하고 모든 곳에서 호출해야하는 많은 객체 (로거, DB 등)가있을 수 있습니다.
이 문제를 해결하기 위해 서비스 (로거 등)에 대한 모든 참조를 내부적으로 저장하는 소위 “개체 관리자”(또는 심포니와 같은 서비스 컨테이너 ) 를 사용하라는 지시를 받았습니다.
하지만 서비스 제공 업체가 순수한 싱글 톤만큼 나쁘지 않은 이유는 무엇입니까?
서비스 공급자는 종속성도 숨기고 첫 번째 인스턴스 생성을 래핑합니다. 그래서 싱글 톤 대신 서비스 제공 업체를 사용해야하는 이유를 이해하기 위해 정말 고심하고 있습니다.
추신. 종속성을 숨기지 않으려면 DI를 사용해야한다는 것을 알고 있습니다 (Misko가 말한대로)
더하다
나는 추가 할 것이다 : 요즘 싱글 톤은 그렇게 사악하지 않다. PHPUnit의 제작자는 여기에서 설명했다.
DI + Singleton은 문제를 해결합니다.
<?php
class Client {
public function doSomething(Singleton $singleton = NULL){
if ($singleton === NULL) {
$singleton = Singleton::getInstance();
}
// ...
}
}
?>
이것이 모든 문제를 전혀 해결하지 못하더라도 꽤 똑똑합니다.
DI 및 서비스 컨테이너 외에이 도우미 개체에 액세스 할 수있는 좋은 솔루션이 있습니까?
답변
서비스 로케이터는 말할 것도없이 두 가지 악 중 더 적은 것입니다. 이 네 가지 차이점으로 요약되는 “작은”( 적어도 지금은 다른 것을 생각할 수 없습니다 ) :
단일 책임 원칙
Service Container는 Singleton처럼 단일 책임 원칙을 위반하지 않습니다. 싱글 톤은 개체 생성과 비즈니스 로직을 혼합하는 반면 서비스 컨테이너는 애플리케이션의 개체 수명주기를 관리하는 데 전적으로 책임이 있습니다. 그런 점에서 서비스 컨테이너가 더 좋습니다.
커플 링
싱글 톤은 일반적으로 정적 메서드 호출로 인해 애플리케이션에 하드 코딩되므로 코드에서 밀접하게 결합되고 모의 종속성 이 발생하기 어렵습니다 . 반면에 SL은 하나의 클래스이며 주입 될 수 있습니다. 따라서 모든 클래스가 그것에 의존하지만 적어도 느슨하게 결합 된 종속성입니다. 따라서 ServiceLocator를 Singleton 자체로 구현하지 않는 한 다소 더 좋고 테스트하기도 쉽습니다.
그러나 ServiceLocator를 사용하는 모든 클래스는 이제 커플 링의 한 형태 인 ServiceLocator에 의존하게됩니다. 이것은 ServiceLocator에 대한 인터페이스를 사용하여 완화 할 수 있으므로 구체적인 ServiceLocator 구현에 묶여 있지는 않지만 클래스는 일종의 Locator의 존재에 의존하지만 ServiceLocator를 전혀 사용하지 않으면 재사용이 크게 증가합니다.
숨겨진 종속성
종속성을 숨기는 문제는 매우 많이 존재합니다. 로케이터를 소비하는 클래스에 주입하면 종속성을 알 수 없습니다. 그러나 Singleton과 달리 SL은 일반적으로 백그라운드에서 필요한 모든 종속성을 인스턴스화합니다. 따라서 서비스를 가져올 때 CreditCard 예제에서 Misko Hevery 처럼 끝나지 않습니다. 예 를 들어 종속성의 모든 종속성을 수동으로 인스턴스화 할 필요가 없습니다.
인스턴스 내부에서 종속성을 가져 오는 것도 공동 작업자를 파헤쳐서는 안된다는 Demeter의 법칙을 위반 하는 것입니다. 인스턴스는 직속 공동 작업자와 만 대화해야합니다. 이것은 Singleton과 ServiceLocator 모두의 문제입니다.
글로벌 상태
글로벌 상태의 문제는 테스트 사이에 새 서비스 로케이터를 인스턴스화 할 때 이전에 생성 된 모든 인스턴스도 삭제되기 때문에 다소 완화됩니다 (실수를 만들어 SL의 정적 속성에 저장하지 않는 한). 물론 SL이 관리하는 클래스의 전역 상태에는 적용되지 않습니다.
또한 훨씬 더 심층적 인 논의를 위해 Fowler on Service Locator vs Dependency Injection 을 참조하십시오 .
Singletons를 사용하는 코드 테스트에 대한 Sebastian Bergmann 의 업데이트 및 링크 된 기사에 대한 참고 사항 : Sebastian은 제안 된 해결 방법이 Singleons를 사용하는 데 문제가 덜하다는 것을 결코 제안하지 않습니다. 다른 방법으로는 테스트 할 수없는 코드를 테스트 할 수있는 방법 중 하나 일뿐입니다. 그러나 여전히 문제가있는 코드입니다. 사실 그는 “당신이 할 수 있다고해서 그렇게해야한다는 것을 의미하지는 않는다”고 명시 적으로 언급합니다.
답변
서비스 로케이터 패턴은 안티 패턴입니다. 종속성 노출 문제를 해결하지 못합니다 (클래스 정의를 보면 종속성이 주입되지 않고 서비스 로케이터에서 빠져 나가기 때문에 어떤 종속성인지 알 수 없습니다).
따라서 귀하의 질문은 서비스 로케이터가 좋은 이유입니다. 내 대답은 그렇지 않습니다.
피하고, 피하고, 피하십시오.
답변
서비스 컨테이너는 Singleton 패턴처럼 종속성을 숨 깁니다. 서비스 컨테이너의 모든 장점은 있지만 서비스 컨테이너의 단점은 없기 때문에 종속성 주입 컨테이너를 대신 사용하는 것이 좋습니다.
내가 이해하는 한, 둘 사이의 유일한 차이점은 서비스 컨테이너에서 서비스 컨테이너는 주입되는 개체 (따라서 종속성 숨기기)이며 DIC를 사용할 때 DIC가 적절한 종속성을 주입한다는 것입니다. DIC에 의해 관리되는 클래스는 DIC에 의해 관리된다는 사실을 완전히 인식하지 못하므로 결합이 적고 종속성이 명확하며 단위 테스트가 만족 스럽습니다.
이것은 두 가지의 차이점을 설명하는 좋은 질문입니다 . 종속성 주입과 서비스 로케이터 패턴의 차이점은 무엇입니까?
답변
1) 상속 (Object Manager 클래스 상속 및 메서드 재정의 가능) 으로 서비스 컨테이너의 개체를 쉽게 교체 할 수 있기 때문에
2) 구성 변경 (Symfony의 경우)
그리고 Singleton은 높은 결합 때문에뿐만 아니라 _ Single _tons 이기 때문에 나쁩니다. 거의 모든 종류의 객체에 대해 잘못된 아키텍처입니다.
‘순수한’DI (생성자에서)를 사용하면 매우 큰 비용을 지불하게됩니다. 모든 객체는 생성자에 전달되기 전에 생성되어야합니다. 더 많은 메모리를 사용하고 성능은 떨어집니다. 또한 항상 객체가 생성자에서 생성되고 전달 될 수있는 것은 아닙니다. 종속성 체인을 생성 할 수 있습니다. 내 영어는 그것에 대해 완전히 논의하기에 충분하지 않습니다. Symfony 문서에서 이에 대해 읽어보십시오.
답변
저에게는 간단한 이유로 전역 상수, 싱글 톤을 피하려고합니다. API를 실행해야하는 경우가 있습니다.
예를 들어 프론트 엔드와 관리자가 있습니다. 관리자 내부에서 사용자로 로그인 할 수 있기를 바랍니다. 관리자 내부의 코드를 고려하십시오.
$frontend = new Frontend();
$frontend->auth->login($_GET['user']);
$frontend->redirect('/');
이것은 프런트 엔드 초기화를위한 새로운 데이터베이스 연결, 새로운 로거 등을 설정하고 사용자가 실제로 존재하는지, 유효한지 등을 확인할 수 있습니다. 또한 적절한 별도의 쿠키 및 위치 서비스를 사용합니다.
싱글 톤에 대한 내 생각은-부모 안에 동일한 객체를 두 번 추가 할 수 없습니다. 예를 들어
$logger1=$api->add('Logger');
$logger2=$api->add('Logger');
하나의 인스턴스와 두 변수 모두를 가리킬 것입니다.
마지막으로 객체 지향 개발을 사용하려면 클래스가 아닌 객체로 작업하십시오.