게임 아키텍처에서 싱글 톤을 많이 사용하지 않으려면 어떻게해야합니까?


55

게임을 만들기 위해 cocos2d-x 게임 엔진을 사용합니다. 엔진은 이미 많은 싱글 톤을 사용합니다. 누군가 그것을 사용했다면, 그들 중 일부에 익숙해야합니다.

Director
SimpleAudioEngine
SpriteFrameCache
TextureCache
EventDispatcher (was)
ArmatureDataManager
FileUtils
UserDefault

전체적으로 약 16 개의 수업이 있습니다. 이 페이지에서 비슷한 목록을 찾을 수 있습니다. Cocos2d-html5 v3.0의 싱글 톤 객체 그러나 게임을 쓰려면 훨씬 더 많은 싱글 톤이 필요합니다.

PlayerData (score, lives, ...)
PlayerProgress (passed levels, stars)
LevelData (parameters per levels and level packs)
SocialConnection (Facebook and Twitter login, share, friend list, ...)
GameData (you may obtain some data from server to configure the game)
IAP (for in purchases)
Ads (for showing ads)
Analytics (for collecting some analytics)
EntityComponentSystemManager (mananges entity creation and manipulation)
Box2dManager (manages the physics world)
.....

왜 싱글 톤이어야한다고 생각합니까? 게임에서 매우 다른 장소에서 필요하기 때문에 공유 액세스가 매우 편리합니다. 다시 말해서, 어딘가에 그것들을 생성하고 모든 아키텍처에 포인터를 전달하는 것은 매우 어려울 것입니다. 또한 이것들은 하나만 필요합니다. 어쨌든 여러 개가 필요하며 Multiton 패턴도 사용할 수 있습니다. 그러나 최악의 점은 싱글 톤이 다음과 같은 이유로 가장 비판적인 패턴이라는 것입니다.

 - bad testability
 - no inheritance available
 - no lifetime control
 - no explicit dependency chain
 - global access (the same as global variables, actually)
 - ....

https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletonshttps://stackoverflow.com/questions/4074154/when-should-the-singleton 에서 몇 가지 생각을 찾을 수 있습니다. -사용하지 않는 패턴-명백한 것

그래서 나는 뭔가 잘못하고 있다고 생각합니다. 내 코드 냄새가 나는 것 같아요 . :) 나는 더 숙련 된 게임 개발자들이이 건축 문제를 어떻게 해결 하는가? 나는 이미 게임 엔진에있는 것으로 간주되는 싱글 톤을 30 개 이상 갖는 것이 게임 개발에서 여전히 정상인지 확인하고 싶습니다 .

필자는 필요한 모든 클래스의 인스턴스가있는 Singleton-Facade를 사용하려고 생각했지만 각각 싱글 톤이 아닙니다. 이것은 많은 문제를 없애고 Facade 자체가 될 싱글 톤 하나만 가질 것입니다. 그러나이 경우 다른 디자인 문제가 발생합니다. 외관은 하나님의 대상이 될 것입니다. 나는 이것도 냄새가 생각합니다 . 따라서이 상황에 맞는 좋은 디자인 솔루션을 찾을 수 없습니다. 조언을 부탁드립니다.


26
Singleton에 대해 논쟁하는 대부분의 사람들은 모든 코드를 통해 일부 데이터 / 기능을 전파하는 데 필요한 작업이 Singleton을 사용하는 것보다 더 많은 버그 소스 일 수 있다는 것을 인식하지 못하는 것 같습니다. 그래서 기본적으로 그들은 비교하지 않고 ==> 거부합니다. 단독 방지가 자세라고 생각합니다 :-) 자, 싱글 톤에는 결함이 있습니다. , 지금 당신이 할 수 없다면, 뒤돌아 보지 않고 그것을 찾으십시오. 결국 Cocos2d는 16 개의 싱글 톤으로 꽤 잘 작동하는 것 같습니다 ...
GameAlchemist

6
다시 말해, 이것은 특정 작업을 수행하는 방법에 대한 질문입니다. 이것은 싱글 톤에 대한 찬반 토론을 요구하는 질문이 아닙니다 (어쨌든 여기서는 주제가 아닐 것입니다). 또한 싱글 톤의 장단점에 대한 탄젠트 토론을위한 플랫폼이 아니라 주석을 사용하여 설명을 요청해야합니다. 입력 내용을 제공하려면 올바른 방법으로 질문에 대한 정답을 제공하는 것이 좋습니다.이 방법을 사용하면 싱글 톤 패턴에 대한 조언으로 솔루션을 강화할 수 있습니다.
Josh

나는이 싱글 톤이 전 세계적으로 액세스 가능하다고 가정합니까? 그 패턴을 피하는 이유 중 하나입니다.
피터-복원 모니카

답변:


41

기본 응용 프로그램 클래스에서 서비스를 초기화 한 다음 생성자 또는 함수를 통해 서비스를 사용해야하는 모든 것에 대한 포인터로 전달합니다. 이것은 두 가지 이유로 유용합니다.

첫째, 초기화 및 정리 순서는 간단하고 명확합니다. 싱글 톤으로 가능한 한 다른 곳에서 실수로 하나의 서비스를 초기화하는 방법은 없습니다.

둘째, 누가 무엇에 의존하는지가 분명합니다. 클래스 선언에서 어떤 클래스에 어떤 서비스가 필요한지 쉽게 알 수 있습니다.

이러한 모든 객체를 전달하는 것이 성가신 일이라고 생각할 수도 있지만 시스템이 제대로 설계되면 전혀 나쁘지 않고 훨씬 명확하게 만듭니다 (어쨌든 나를 위해).


34
+1. 또한 시스템에 대한 참조를 전달해야하는 "불쾌감"은 종속성 크립을 방지하는 데 도움이됩니다. " 모든 것"에 무언가를 전달해야한다면 , 이는 디자인에 나쁜 커플 링 문제가 있다는 것을 나타내는 좋은 신호이며, 모든 곳에서 모든 것에 대한 무차별 한 글로벌 액세스보다 훨씬 더 분명합니다.
Josh

2
이것은 실제로 솔루션을 제공하지 않습니다 ... 그래서 당신은 프로그램 클래스에 많은 싱글 톤 객체가 있고 장면에서 코드를 10 레벨 깊이내어 싱글 톤 중 하나가 필요합니다 ... 어떻게 전달됩니까?
War

Wardy : 왜 싱글 톤이어야합니까? 물론 대부분의 경우 특정 인스턴스를 전달할 수 있지만 싱글 톤을 싱글 톤으로 만드는 것은 다른 인스턴스를 사용할 수 없다는 것입니다. 주입을 사용하면 한 객체에 대해 한 인스턴스를 전달하고 다른 객체에 대해 다른 인스턴스를 전달할 수 있습니다. 어떤 이유에서든 그렇게하지 않는다고해서 할 수 없다는 것을 의미하지는 않습니다.
para

3
그들은 싱글 톤이 아닙니다. 그것들은 초기화 / 정리를 제어하는 ​​잘 정의 된 소유자가있는 다른 것과 같은 일반적인 객체입니다. 액세스 권한이없는 10 단계 깊이 인 경우 디자인 문제가있을 수 있습니다. 모든 것이 렌더러, 오디오 등을 필요로하거나 액세스 할 수있는 것은 아닙니다. 이런 식으로 디자인을 생각하게됩니다. 코드를 테스트하는 사람들 (what ??)을위한 추가 보너스로 단위 테스트도 훨씬 쉬워졌습니다.
megadan

2
예, Spring과 같은 프레임 워크가 있거나없는 종속성 주입은 이것을 처리하기위한 완벽한 솔루션이라고 생각합니다. 나는 이것을 어디에나 전달하는 것을 피하기 위해 더 나은 구조 코드를 만드는 방법을 궁금해하는 사람들에게 훌륭한 기사라고 생각했다 : misko.hevery.com/2008/10/21/…
megadan

17

인터넷이 나보다 더 잘할 수 있기 때문에 싱글 톤의 악에 대해서는 이야기하지 않겠습니다.

내 게임에서는 서비스 로케이터 패턴 을 사용하여 많은 톤의 싱글 톤 / 매니저를 피합니다.

개념은 매우 간단합니다. Singleton으로 사용했던 것에 도달하는 유일한 인터페이스처럼 작동하는 Singleton이 하나뿐입니다. 싱글 톤이 여러 개있는 대신 하나만 있습니다.

같은 전화 :

FlyingToasterManager.GetInstance().Toast();
SmokeAlarmManager.GetInstance().Start();

Service Locator 패턴을 사용하여 다음과 같이 보입니다.

SvcLocator.FlyingToaster.Toast();
SvcLocator.SmokeAlarm.Start();

보시다시피 모든 싱글 톤은 서비스 로케이터에 포함되어 있습니다.

더 자세한 설명을 원하시면 로케이터 패턴 사용법에 대한이 페이지읽어 보시기 바랍니다 .

[ 편집 ] : 의견에서 지적했듯이 사이트 gameprogrammingpatterns.com에는 Singleton에 대한 매우 흥미로운 섹션 도 포함되어 있습니다 .

도움이 되길 바랍니다.


4
여기에 연결된 gameprogrammingpatterns.com 사이트에도 싱글 톤 관련 장이 있습니다.
ajp15243

28
서비스 로케이터는 모든 일반적인 문제를 컴파일 타임이 아닌 런타임으로 이동시키는 싱글 톤입니다. 그러나 당신이 제안하는 것은 거대한 정적 레지스트리입니다. 거의 더 나은 것이지만 여전히 본질적으로 대량의 전역 변수입니다. 많은 레벨에서 동일한 객체에 액세스해야하는 경우 이는 단순히 잘못된 아키텍처의 문제입니다. 여기서는 적절한 글로벌 인젝션이 필요하지만 현재 글로벌과 다르게 이름이 지정된 글로벌이 아닙니다.
Magus

2
"적절한 의존성 주입"에 대해 자세히 설명해 주시겠습니까?
Blue Wizard

17
이것은 실제로 문제를 해결하지 못합니다. 본질적으로 하나의 거대한 싱글 톤으로 병합 된 많은 싱글 톤입니다. 근본적인 구조적 문제는 해결되거나 해결되지 않았으며, 코드는 여전히 더 예쁘다.
ClassicThunder

4
@classicthunder 모든 단일 문제를 해결하는 것은 아니지만 여러 문제를 해결하기 때문에 Singleton보다 크게 개선되었습니다. 특히, 작성한 코드는 더 이상 단일 클래스에 연결되지 않고 클래스의 인터페이스 / 카테고리에 연결됩니다. 이제 다양한 서비스의 다양한 구현을 쉽게 바꿀 수 있습니다.
jhocking

12

이 답변, 의견 및 기사, 특히이 두 가지 훌륭한 기사를 읽으면,

결국, 나는 다음 결론에 도달했다. 그것은 내 자신의 질문에 대한 일종의 대답이다. 최선의 방법은 게으르지 않고 종속성을 직접 전달하는 것입니다. 이것은 명시적이고 테스트 가능하며 전역 액세스가 없으며 대의원을 매우 깊고 많은 장소로 전달 해야하는 경우 잘못된 디자인을 나타낼 수 있습니다. 그러나 프로그래밍에서 한 가지 크기가 모두 맞지는 않습니다. 따라서 여전히 직접 전달하는 것이 편리하지 않은 경우가 있습니다. 예를 들어 게임 프레임 워크 (다른 종류의 게임을 개발하기 위해 기본 코드로 재사용되는 코드 템플릿)를 만들고 많은 서비스를 포함시키는 경우가 있습니다. 여기서 우리는 각 서비스가 얼마나 깊이 전달 될 수 있는지, 그리고 얼마나 많은 장소가 필요한지를 모릅니다. 특정 게임에 따라 다릅니다. 이런 종류의 경우 어떻게해야합니까? 우리는 Singleton 대신 Service Locator 디자인 패턴을 사용합니다.

  1. 구현에 프로그래밍하는 것이 아니라 인터페이스에 프로그래밍합니다. 이것은 OOP 원칙에있어 매우 중요합니다. 런타임에 Null Object 패턴을 사용하여 서비스를 변경하거나 비활성화 할 수 있습니다. 이는 유연성 측면에서 중요 할뿐만 아니라 테스트에 직접적인 도움이됩니다. 모의 기능을 비활성화하거나 모의 할 수 있으며 Decorator 패턴을 사용하여 로깅 및 기타 기능도 추가 할 수 있습니다. 이것은 싱글 톤이 가지고 있지 않은 매우 중요한 재산입니다.
  2. 또 다른 큰 장점은 객체의 수명을 제어 할 수 있다는 것입니다. Singleton에서는 Lazy Initialization 패턴으로 오브젝트가 스폰되는 시간 만 제어합니다. 그러나 일단 오브젝트가 생성되면 프로그램이 끝날 때까지 지속됩니다. 그러나 경우에 따라 서비스를 변경하고 싶습니다. 또는 심지어 종료하십시오. 특히 게임에서이 유연성은 매우 중요합니다.
  3. 여러 싱글 톤을 하나의 소형 API로 결합 / 포장합니다. 한 번의 작업으로 여러 서비스를 한 번에 사용할 수있는 Service Locator와 같은 Facade를 사용할 수도 있습니다.
  4. Singleton의 주요 디자인 문제인 여전히 글로벌 액세스 권한이 있다고 주장 할 수 있습니다. 동의하지만, 우리는이 패턴이 필요합니다. 직접 의존성 주입이 상황에 적합하지 않다고 생각하고 글로벌 액세스가 필요한 경우 글로벌 액세스에 대해 불평해서는 안됩니다. 그러나이 문제에 대해서도 개선이 가능합니다 (위의 두 번째 기사 참조). 모든 서비스를 비공개로 만들 수 있으며 서비스를 사용해야한다고 생각하는 모든 클래스를 Service Locator 클래스에서 상속 할 수 있습니다. 이것은 액세스를 상당히 좁힐 수 있습니다. 그리고 Singleton의 경우 개인 생성자로 인해 상속을 사용할 수 없습니다.

또한이 패턴은 Unity, LibGDX, XNA에서 사용되었다는 것을 고려해야합니다. 이것은 이점이 아니지만 패턴의 유용성에 대한 증거입니다. 이 엔진은 많은 똑똑한 개발자가 오랫동안 개발했으며 엔진의 진화 단계에서 여전히 더 나은 솔루션을 찾지 못했다고 생각하십시오.

결론적으로, Service Locator는 내 질문에 설명 된 시나리오에 매우 유용 할 수 있지만 가능하면 피해야합니다. 경험상, 필요한 경우 사용하십시오.

편집 : 1 년 동안 일하고 직접 DI를 사용하고 거대한 생성자와 많은 위임에 문제가있는 후 더 많은 연구를 수행했으며 DI 및 Service Locator 방법에 대한 장단점에 대해 이야기하는 좋은 기사를 찾았습니다. Martin Fowler 의이 기사 ( http://www.martinfowler.com/articles/injection.html )를 인용 하면 실제로 서비스 로케이터가 나쁜시기를 설명합니다.

따라서 주요 문제는 작성자가 제어 할 수없는 응용 프로그램에서 사용될 것으로 예상되는 코드를 작성하는 사람들에게 있습니다. 이러한 경우 서비스 로케이터에 대한 최소한의 가정조차 문제가됩니다.

그러나 어쨌든 DI를 처음으로 원한다면이 http://misko.hevery.com/2008/10/21/dependency-injection-myth-reference-passing/ 기사를 읽어야합니다 (@megadan이 지적함) , LoD (Law of Demeter) 또는 최소한의 지식 원칙에 매우주의를 기울임으로써 .


서비스 로케이터에 대한 개인적인 편협은 주로 그것을 사용하는 코드를 테스트하려는 시도에서 비롯됩니다. 블랙 박스 테스트를 위해 생성자를 호출하여 테스트 할 인스턴스를 만듭니다. 예외가 즉시 또는 메소드 호출에서 발생할 수 있으며, 누락 된 종속성으로 이끄는 공용 특성이 없을 수 있습니다. 소스에 액세스 할 수있는 경우 사소한 것일 수 있지만 그래도 서프라이즈 원칙 (싱글 톤에서 자주 발생하는 문제)에 위배됩니다. 서비스 로케이터를 전달하는 것이 좋습니다.이 기능은이 부분의 일부를 완화하고 이에 대한 칭찬을받습니다.
Magus

3

코드베이스의 일부가 초석 객체 또는 기초 클래스로 간주되는 것은 드문 일이 아니지만 싱글 톤으로 지시되는 수명주기를 정당화하지는 않습니다.

프로그래머는 종종 대안적인 접근 방식을 취하고 명시적인 영지에서 서로 간결하고 장엄한 객체 관계를 취하는 대신 편의성과 순수한 게으름의 수단으로 싱글 톤 패턴에 의존합니다.

따라서 유지하기 쉬운 것을 스스로에게 물어보십시오.

나 같은 사람이라면 헤더 파일을 열고 생성자 인수와 공용 메서드를 잠깐 살펴서 구현 파일에서 수백 줄의 코드를 검사하지 않고 객체에 필요한 종속성을 확인하는 것이 좋습니다.

객체를 싱글 톤으로 만든 후 나중에 해당 객체의 여러 인스턴스를 지원할 수 있어야한다는 것을 알고 있다면이 클래스가 코드 기반에서 상당히 많이 사용 된 클래스 인 경우 필요한 변경 사항을 상상해보십시오. 더 나은 대안은 오브젝트가 한 번만 할당되고 여러 인스턴스를 지원해야하는 경우에도 그러한 종속성없이 안전하게 수행 할 수있는 종속성을 명시 적으로 작성하는 것입니다.

다른 사람들이 지적했듯이 Singleton 패턴을 사용하면 커플 링이 난독 화되어 종종 모듈 사이의 열악한 디자인과 높은 응집력을 나타냅니다. 이러한 응집력은 일반적으로 바람직하지 않으며 유지하기 어려운 취성 코드로 이어집니다.


-1 :이 답변은 싱글 톤 패턴을 잘못 설명하고 있으며, 모든 인수는 패턴의 구현 불량 문제를 설명합니다. 적절한 싱글 톤 인터페이스이며 설명 된 모든 단일 문제 (단독으로 전환 (GoF가 명시 적으로 해결하는 시나리오) 포함)를 피합니다. 싱글 톤의 사용을 리팩토링하기 어려운 경우, 명시 적 객체 참조의 사용을 리팩토링하는 것이 더 어려울 수 있습니다. 싱글 톤이 어떻게 든 더 많은 코드 커플 링을 유발하면 단순히 잘못되었습니다.
세스 배틴

1

싱글 톤은 유명한 패턴이지만 그것이 제공하는 목적과 장단점을 아는 것이 좋습니다.

아무런 관계없다면 정말 의미가 있습니다 .
완전히 다른 객체 (강한 의존성 없음)로 구성 요소를 처리 할 수 있고 동일한 동작을 기대할 경우 싱글 톤을 선택하는 것이 좋습니다.

반면, 특정 시간에만 사용되는 작은 기능 이 필요한 경우에는 참조 전달 을 선호해야합니다 .

언급했듯이 싱글 톤에는 평생 제어가 없습니다. 그러나 장점은 초기화가 느리다는 것입니다.
애플리케이션 수명 동안 해당 싱글 톤을 호출하지 않으면 인스턴스화되지 않습니다. 그러나 싱글 톤을 만들고 사용하지 않는 것이 좋습니다.

구성 요소 대신 서비스 와 같은 단일 항목이 표시되어야합니다 .

Singletons는 작동하지만 응용 프로그램을 중단하지 않았습니다. 그러나 그들의 부족 (네가 준 네 가지) 은 당신이 하나를 만들지 않는 이유입니다.


1

엔진은 이미 많은 싱글 톤을 사용합니다. 누군가 그것을 사용했다면, 그들 중 일부에 익숙해야합니다.

익숙하지 않은 것이 싱글 톤을 피해야하는 이유는 아닙니다.

싱글 톤이 필요하거나 피할 수없는 이유는 여러 가지가 있습니다. 게임 프레임 워크는 단일 상태 기반 하드웨어 만 있으면 피할 수없는 결과이기 때문에 종종 싱글 톤을 사용합니다. 각 처리기의 여러 인스턴스를 사용하여 이러한 하드웨어를 제어하려는 것은 이치에 맞지 않습니다. 그래픽 표면은 외부 상태 저장 하드웨어이며 실수로 그래픽 서브 시스템의 두 번째 사본을 초기화하는 것은 결코 재앙이 아닙니다. 이제 두 그래픽 서브 시스템은 그릴 수있는 사람과 언제, 서로를 제어 할 수없는 방식으로 덮어 쓰면서 서로 싸울 것입니다. 이벤트 큐 시스템과 마찬가지로 마우스 이벤트를 비 결정적 방식으로 얻는 사람과 싸울 것입니다. 하나만있는 상태 저장 형 외부 하드웨어를 처리 할 때 싱글 톤은 충돌을 피할 수 없습니다.

싱글 톤이 합리적인 또 다른 곳은 캐시 관리자입니다. 캐시는 특별한 경우입니다. 싱글 톤과 같은 기술을 모두 사용하여 영원히 살면서도 실제로 싱글 톤으로 간주해서는 안됩니다. 캐싱 서비스는 투명한 서비스이므로 프로그램의 동작을 변경하지 않아야하므로 캐시 서비스를 널 캐시로 바꾸면 프로그램이 느리게 실행된다는 점을 제외하고는 여전히 프로그램이 작동해야합니다. 캐시 관리자가 싱글 톤에서 예외 인 주된 이유는 응용 프로그램 자체를 종료하기 전에 캐시 서비스를 종료하는 것이 의미가 없기 때문에 캐시 된 객체를 버릴 수 있기 때문입니다. 하나씩 일어나는 것.

이것이 이러한 서비스가 싱글 톤 인 이유입니다. 그러나 싱글 톤이 있어야하는 좋은 이유는 여러분이 열거 한 수업에 적용되지 않습니다.

게임에서 매우 다른 장소에서 필요하기 때문에 공유 액세스가 매우 편리합니다.

싱글 톤의 이유는 아닙니다. 이것이 글로벌 이유입니다. 또한 다양한 시스템 주위에 많은 것들을 전달 해야하는 경우 디자인이 좋지 않다는 신호입니다. 주변을 통과해야하는 것은 우수한 OO 설계로 방지 할 수있는 높은 커플 링을 나타냅니다.

게시물에서 싱글 톤이 필요하다고 생각하는 클래스 목록을 보면 절반이 실제로 싱글 톤이 아니어야하고 나머지 절반은 처음에는 거기에 없어야한다고 생각할 수 있습니다. 객체를 전달 해야하는 것은 싱글 톤의 실제 사용 사례보다는 적절한 캡슐화가 없기 때문인 것 같습니다.

수업을 조금씩 살펴 보겠습니다.


PlayerData (score, lives, ...)
LevelData (parameters per levels and level packs)
GameData (you may obtain some data from server to configure the game)

클래스 이름에서 나는이 클래스들이 Anemic Domain Model antipattern 과 같은 냄새가납니다 . 빈혈 객체는 일반적으로 많은 양의 데이터를 전달해야하므로 커플 링이 증가하고 나머지 코드는 번거 롭습니다. 또한 빈혈 클래스는 객체 방향을 사용하여 세부 사항을 캡슐화하지 않고 절차 적으로 생각하고 있다는 사실을 숨 깁니다.


IAP (for in purchases)
Ads (for showing ads)

왜이 클래스들이 싱글 톤이어야합니까? 이것들은 단기 수업이어야하며, 사용자가 구매를 마치거나 광고를 더 이상 표시 할 필요가 없을 때 필요하고 해체 될 때 제기되어야합니다.


EntityComponentSystemManager (mananges entity creation and manipulation)

다시 말해, 생성자와 서비스 계층? 왜이 클래스가 처음부터 명확한 경계 나 목적이 없는가?


PlayerProgress (passed levels, stars)

플레이어 진행률이 Player 클래스와 다른 이유는 무엇입니까? Player 클래스는 자신의 진행 상황을 추적하는 방법을 알고 있어야합니다. 책임 분리를 위해 Player 클래스와 다른 클래스에서 진행 추적을 구현하려면 PlayerProgress가 Player 뒤에 있어야합니다.


Box2dManager (manages the physics world)

나는이 클래스가 실제로 무엇을하는지 알지 못하고이 클래스에 대해 더 이상 언급 할 수 없지만 한 가지 분명한 것은이 클래스의 이름이 잘못되었다는 것입니다.


Analytics (for collecting some analytics)
SocialConnection (Facebook and Twitter login, share, friend list, ...)

소셜 연결 객체가 실제로 객체가 아니기 때문에 싱글 톤이 합리적인 유일한 클래스 인 것 같습니다. 사회적 연결은 원격 서비스에 존재하는 외부 객체의 그림자 일뿐입니다. 다시 말해, 이것은 일종의 캐시입니다. 캐싱 부분을 외부 서비스의 서비스 부분과 명확하게 분리하십시오. 후자는 싱글 톤일 필요가 없기 때문입니다.

이러한 클래스의 인스턴스 전달을 피할 수있는 한 가지 방법은 메시지 전달을 사용하는 것입니다. 이러한 클래스의 인스턴스를 직접 호출하는 대신, Analytic and SocialConnection 서비스로 주소가 지정된 메시지를 비동기식으로 보내면 이러한 서비스가 구독되어 해당 메시지를 수신하고 메시지에 대해 조치를 취합니다. 이벤트 큐는 이미 싱글 톤이므로 호출을 트램 펄링하여 싱글 톤과 통신 할 때 실제 인스턴스를 전달할 필요가 없습니다.


-1

나에게 싱글 톤은 항상 나쁘다.

내 아키텍처에서 게임 클래스가 관리하는 GameServices 컬렉션을 만들었습니다. 필요에 따라이 컬렉션에 추가 및 제거 할 수 있습니다.

위의 예제에서 "이 습격은 신이며 주인이 없다"고 말하는 것입니다. 나는 대부분 도우미 클래스 또는 GameServices라고 말하고 게임이 그들에게 책임이 있다고 말합니다.

그러나 그것은 내 엔진의 내 디자인 일뿐입니다. 상황은 다를 수 있습니다.

더 직접적으로 말하기 ... 유일한 싱글 톤은 코드의 모든 부분을 소유하는 게임입니다.

이 답변은 질문의 주관적인 성격을 기반으로하며 순수하게 내 의견을 바탕으로합니다. 모든 개발자에게 정확하지 않을 수도 있습니다.

편집 : 요청에 따라 ...

좋아, 이것은 지금 내 앞에 코드가 없기 때문에 내 머리에서 나온 것이지만 본질적으로 모든 게임의 근본에는 실제로 싱글 톤 인 Game 클래스가 있습니다 ...

그래서 다음을 추가했습니다 ...

class Game
{
   IList<GameService> Services;

   public T GetService<T>() where T : GameService
   {
       return Services.FirstOrDefatult(s => s is T);
   }
}

이제 전체 프로그램의 모든 싱글 톤을 포함하는 시스템 클래스 (정적)가 있습니다 (게임 및 구성 옵션이 거의 포함되어 있지 않습니다) ...

public static class Sys
{
    // only the main game assembly can set this (done by the game ctor)
    // anything can get
    public static Game Game { get; internal set; }
}

그리고 사용법 ...

어디서나 나는 같은 것을 할 수 있습니다

var tService = Sys.Game.getService<T>();
tService.Something();

이 방법은 일반적으로 "싱글 톤"으로 간주되는 내 게임 "자체"항목을 의미하지만 기본 GameService 클래스를 원하는대로 확장 할 수 있습니다. 게임에서 특정 상황에 필요한 경우이를 구현하는 서비스를 확인하고 호출하는 IUpdateable과 같은 인터페이스가 있습니다.

또한 더 구체적인 장면 시나리오를 위해 다른 서비스를 상속 / 확장하는 서비스가 있습니다.

예를 들어 ...

물리 시뮬레이션을 처리하는 물리 서비스가 있지만 장면 물리 시뮬레이션에 따라 약간 다른 동작이 필요할 수 있습니다.


1
게임 서비스를 한 번만 인스턴스화해서는 안됩니까? 그렇다면 클래스 수준에서 적용되지 않는 싱글 톤 패턴 일 뿐이며 제대로 구현 된 싱글 톤보다 최악입니다.
GameAlchemist

2
@Wardy 나는 당신이 한 일을 명확하게 이해하지 못하지만 그것은 내가 설명한 Singleton-Facade처럼 들리며 GOD 객체가되어야합니다.
Narek

10
게임 레벨에서 구현 된 싱글 톤 패턴입니다. 기본적으로 가장 흥미로운 측면은 싱글 톤을 사용하지 않는 척하는 것입니다. 당신의 상사가 반 패턴 교회 출신이라면 유용합니다.
GameAlchemist

2
이것이 싱글 톤을 사용하는 것과 어떻게 다른지 잘 모르겠습니다. 이 유형의 단일 기존 서비스에 구체적으로 액세스하는 방법의 유일한 차이점은 아닌가? 즉 var tService = Sys.Game.getService<T>(); tService.Something();, getService부품 을 건너 뛰고 직접 액세스하는 대신 ?
Christian

1
@Christian : 사실, 그것은 Service Locator 안티 패턴과 정확히 같습니다. 분명히이 커뮤니티는 여전히 그것이 권장되는 디자인 패턴이라고 생각합니다.
Magus

-1

싱글 톤의 반대자들이 종종 고려하지 못하는 싱글 톤 제거의 필수 요소는 싱글 톤이 인스턴스 값에 도달 할 때 객체가 캡슐화하는 훨씬 더 복잡한 상태를 처리하기 위해 논리를 추가하는 것입니다. 실제로, 싱글 톤을 인스턴스 변수로 교체 한 후 객체의 상태를 정의 하는 것 조차 어려울 수 있지만, 싱글 톤을 인스턴스 변수로 대체하는 프로그래머는이를 처리 할 준비가되어 있어야합니다.

예를 들어, Java HashMap와 .NET 을 비교하십시오 Dictionary. 둘 다 상당히 비슷하지만 중요한 차이점이 있습니다 .Java HashMap는 사용하기가 하드 코딩되어 equals있고 hashCode(단일 비교기를 효과적으로 사용합니다) .NET Dictionary은 인스턴스를 IEqualityComparer<T>생성자 매개 변수로 사용할 수 있습니다 . 를 유지하는 데 소요되는 런타임 비용 IEqualityComparer은 최소한이지만 그로 인한 복잡성은 아닙니다.

Dictionary인스턴스는를 캡슐화 하기 때문에 IEqualityComparer상태는 실제로 포함하는 키와 관련 값 사이 매핑이 아니라 키와 비교기로 정의 된 동등성 세트 와 관련 값 사이 매핑 입니다. 두 인스턴스 경우 d1d2Dictionary동일한 행 홀드 참조는 IEqualityComparer, 그것이 새로운 인스턴스를 생성하는 것이 가능할 것이다 d3임의 대한 그러한를 x, d3.ContainsKey(x)동일 할 것이다 d1.ContainsKey(x) || d2.ContainsKey(x). 그러나, 경우 d1d2다른 임의의 평등 comparers에 대한 참조를 보유 할 수있다, 일반적으로 그 상태가 유지 보장하기 위해 병합 할 방법이 없습니다.

싱글 톤을 제거하기 위해 인스턴스 변수를 추가하지만 해당 인스턴스 변수에 의해 암시 될 수있는 가능한 새로운 상태를 제대로 설명하지 않으면 싱글 톤보다 더 부서지기 쉬운 코드가 생성 될 수 있습니다. 모든 객체 인스턴스에 별도의 필드가 있지만 모두 동일한 객체 인스턴스를 식별하지 않으면 사물이 제대로 작동하지 않는 경우 새로 구입 한 모든 필드는 사물이 잘못 될 수있는 방법의 수가 증가합니다.


1
이것이 토론에 대한 흥미로운 측면이지만, 실제로 그것이 OP에 의해 실제로 제기 된 질문을 다루지는 않는다고 생각합니다.
Josh

1
@JoshPetrie : 원래 게시물에 대한 의견 (예 : Magus)은 싱글 톤 사용을 피하기 위해 참조를 수행하는 데 소요되는 런타임 비용이 크지 않다고 제안했습니다. 따라서 단일 객체를 피하기 위해 모든 객체가 참조를 캡슐화하는 데 필요한 의미 론적 비용을 고려할 것입니다. 저렴하고 쉽게 피할 수있는 경우 싱글 톤을 피해야하지만, 싱글 톤을 필요로하지 않지만 단일 인스턴스를 사용하는 코드보다 여러 인스턴스가 존재하는 즉시 중단 될 수있는 코드는 피해야합니다.
supercat

2
답변은 질문에 직접 답변하기위한 의견을 다루기위한 것이 아닙니다.
ClassicThunder

@ClassicThunder : 편집 내용이 더 좋습니까?
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.