싱글 톤 / 글로벌 대안


16

Singletons / globals의 함정에 대해 수많은 시간을 들었으며 왜 자주 눈살을 찌푸리는 지 이해합니다.

내가 이해하지 못하는 것은 우아하고 지저분하지 않은 대안입니다. 싱글 톤 / 글로벌을 사용하는 것에 대한 대안은 항상 필요한 객체에 도달 할 때까지 엔진 객체를 통해 백만 레벨 아래로 객체를 전달하는 것입니다.

예를 들어 게임에서 게임을 시작할 때 자산을 미리로드합니다. 이 애셋은 플레이어가 메인 메뉴를 탐색하고 게임에 들어갈 때까지 나중에 사용되지 않습니다. 이 데이터를 Game 객체에서 ScreenManager 객체로 전달해야합니까 (단 하나의 Screen 만 실제로이 데이터를 처리한다는 사실에도 불구하고) 그 다음 적절한 Screen 객체로 전달해야합니까?

난 그냥 의존성 주입을 위해 전역 상태 데이터를 교환하고 자식 객체로 데이터를 전달하는 목적을 제외하고는 데이터를 신경 쓰지 않는 객체로 데이터를 전달하는 것 같습니다.

이것이 싱글 톤이 좋은 경우입니까, 아니면 내가 놓친 우아한 해결책이 있습니까?

답변:


16

싱글 톤과 글로벌을 혼동하지 마십시오. 글로벌 변수의 일종이 필요는하지만, 싱글은 하지 전역 변수 단지 대체하지만, 주로 C ++ 정적 초기화 순서의 문제를 해결할 수있는 방법 ( 및 FQA ). (다른 언어에서는 전역 변수 부족 및 베어 기능 부족과 같은 다른 언어 부족 문제를 해결할 수있는 방법입니다.)

싱글 톤 대신 전역 포인터를 사용하고 필요한 것보다 먼저 수동으로 초기화 해야하는지 확인하면 함수 호출 및 분기 오버 헤드, 객체에 도달하기위한 절름발이 구문을 피하고 실제로 테스트가 필요하거나 디자인이 변경되어 클래스의 두 번째 인스턴스.

원하는 몇 가지 전역 변수 (일반적인 예는 오디오 출력, 열린 창 목록, 키보드 처리기 등)의 경우 서비스 로케이터 패턴을 권장 합니다 . 다른 구현 (예 : 실제 대 널 오디오 장치)으로 물건을 쉽게 교체하고 네임 스페이스를 오염시키지 않도록 모든 전역을 하나의 구조로 수집합니다.


+1. 서비스 로케이터 패턴 링크에 대한 좋은 답변과 감사합니다. 매우 흥미로운 내용입니다.
bummzack

1

코드의 일부를 마술처럼 일부 데이터에 대해 "알고"가질 수없는 경우 어떻게 든 전달해야합니다. 그러나 반드시 인수를 통해서만 전달되어야한다는 의미는 아닙니다.

예제의 경우, 자산을로드하고 저장하는 일종의 "AssetManager"가 없을 수 있으며 ScreenManager에는 해당 참조 (만들었을 때) 만 제공하면됩니까? 그런 의미에서 다른 객체로 래핑 된 애셋에 대한 참조를 전달하고 있으며 필요할 때 리프 함수로 전달하지 않고 초기화 할 때 한 번만 전달할 수 있습니다.

이제 IMHO는 당신이 원하는 유일한 종류의 AssetManager가 싱글 톤 일 수도 있습니다. 함정을 이해하고 특별히 피하도록 코드를 작성하십시오 (단일 톤이 여러 스레드에서 동시에 액세스되고 차단 해야하는 무언가를 할 때마다 포크로 찌를 것이라고 가정).


1

Jason D가 옳다고 생각합니다. 이것이 내가 처리하는 방법입니다.

Game에는 AssetManager라는 인스턴스가 있습니다.이 개체는 이름으로 모든 자산을 가져올 수 있습니다.

게임에서 :

assetManager = new AssetManager();
screenManager = new ScreenManager();
screenManager.assetManager = assetManager;

ScreenManager에서 :

screen = new Screen();
screen.assetManager = assetManager;

화면에서 :

myAsset = assetManager.getBitmp("lava.png");

이제 모든 화면에서 필요한 모든 자산에 액세스 할 수 있습니다. 이것은 전역 또는 싱글 톤을 사용하는 것보다 더 복잡하거나 미친 것이 아니며 충돌없이 동일한 응용 프로그램에서 2 개의 게임 인스턴스를 실행하는 옵션이 있습니다. 한 번은 8 개의 미니 게임으로 구성된 게임을 만들어야했는데 모두 동일한 기본 클래스 / 프레임 워크를 공유했습니다. 이 참조 전달 스타일을 사용하기 위해 모든 글로벌 / 싱글 릿을 리팩터링해야했으며 결코 되돌아 보지 않았습니다. 글로벌이어야하는 것은 오디오, 네트워킹, i / o 등과 같이 물리적으로 한 번만 존재할 수있는 것입니다.


0

Factory 패턴을 사용하여 Singleton을 바꿀 수 있습니다 . 그런 다음 팩토리 클래스는 만들 수있는 인스턴스 수를 제어 할 수 있으며 나중에 인스턴스가 둘 이상 필요할 때 쉽게 변경할 수 있습니다 AssetManager. 이 기사에 명시된 바와 같이 :

많은 문제가 발생하지 않는 Singleton의 모든 유연성을 제공합니다.


다소 제한된 또 다른 가능성은 클래스를 정적으로 만드는 것입니다 (자산 관리자에는 적합하지 않으며 정적 클래스가있는 언어에서만 가능하다고 생각합니다). 그러나 상속 / 다형성이 필요하지 않은 경우에만 작동합니다. 매우 유연하지 않은 솔루션입니다.

정적 메소드는 화강암만큼 유연합니다. 하나를 사용할 때마다 프로그램의 일부를 구체적으로 캐스팅합니다. 발이 딱딱 해지는 것을 보면서 발이 끼지 않도록하십시오. 언젠가는 dang PrintSpooler 클래스의 또 다른 구현이 필요하며 인터페이스, 팩토리 및 구현 클래스 집합이어야한다는 사실에 놀랄 것입니다. 도!

이것은 정적 메소드에 관한 것이지만 정적 클래스에도 적용될 수 있습니다.


"클래스를 정적으로 만든다"는 것은 무엇을 의미합니까? C ++에는 정적 클래스가 없습니다. 정적 메소드 만 가지고 있습니까? 그렇다면 네임 스페이스 대신 클래스를 갖는 것이 왜 귀찮습니까?

1
@Joe : 글쎄, 질문은 내가 이해 한 C ++에 초점을 맞추지 않았습니다. C # 또는 Java에서는 정적 클래스를 만들 수 있으며이를 참조하고 있습니다. 또한 내가 말했듯이 정적 클래스는 대부분 최적의 솔루션이 아니지만 드물게 전역처럼 작동 할 수 있습니다.
Michael Klement
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.