여기서 단일 인스턴스 와 Singleton 디자인 패턴 을 구분하는 것이 중요합니다 .
단일 인스턴스 는 단순히 현실입니다. 대부분의 앱은 한 번에 하나의 구성, 한 번에 하나의 UI, 한 번에 하나의 파일 시스템 등 만 작동하도록 설계되었습니다. 유지해야 할 상태 나 데이터가 많으면 확실히 하나의 인스턴스 만 갖고 가능한 한 오래 유지해야합니다.
싱글 톤 디자인 패턴 은 매우 특정한 유형 의 단일 인스턴스, 특히 다음과 같은 유형 입니다.
- 전역 정적 인스턴스 필드를 통해 액세스 할 수 있습니다.
- 프로그램 초기화 또는 최초 액세스시 작성됩니다.
- 공개 생성자가 없습니다 (직접 인스턴스화 할 수 없음).
- 명시 적으로 해제하지 마십시오 (프로그램 종료시 암시 적으로 해제).
이 특정 설계 선택으로 인해 패턴에 몇 가지 잠재적 인 장기 문제가 발생합니다.
- 추상 또는 인터페이스 클래스를 사용할 수 없음
- 서브 클래스 불가능
- 응용 분야에서 높은 결합 (수정하기 어려움)
- 테스트하기 어려움 (단위 테스트에서 위조 / 조롱 할 수 없음);
- 변경 가능한 상태의 경우 병렬화하기 어려움 (광범위한 잠금이 필요함);
- 등등.
이 증상들 중 어느 것도 실제로 단일 인스턴스에만 해당되는 것이 아니라 싱글 톤 패턴 일뿐입니다.
대신 무엇을 할 수 있습니까? 싱글 톤 패턴을 사용하지 마십시오.
질문에서 인용 :
아이디어는 데이터를 저장하고 동기화하는 앱에이 장소를 두는 것이 었으며, 열린 새 화면은 서버에서 다양한 지원 데이터를 반복적으로 요청하지 않고도 필요한 부분을 대부분 쿼리 할 수 있습니다. 끊임없이 서버에 요청하면 너무 많은 대역폭이 필요합니다. 일주일에 수천 달러의 추가 인터넷 요금을 청구하고 있기 때문에 받아 들일 수 없었습니다.
이 개념에는 힌트가 있지만 확실하지 않은 이름이 있습니다. 이것을 캐시 라고 합니다 . 화려하고 싶다면 "오프라인 캐시"또는 원격 데이터의 오프라인 복사본이라고 부를 수 있습니다.
캐시는 싱글 톤일 필요는 없습니다. 그것은 수 여러 캐시 인스턴스에 대한 동일한 데이터를 가져 오는 피하려는 경우 하나의 인스턴스가 필요; 그렇다고해서 실제로 모든 사람에게 모든 것을 노출 시켜야한다는 의미는 아닙니다 .
가장 먼저 할 일은 캐시 의 다른 기능 영역 을 별도의 인터페이스로 분리하는 것입니다. 예를 들어 Microsoft Access를 기반으로 세계에서 가장 최악의 YouTube 복제본을 만들고 있다고 가정 해 보겠습니다.
MSAccessCache
▲
|
+ ----------------- + ----------------- +
| | |
IMediaCache IProfileCache IPageCache
| | |
| | |
VideoPage MyAccountPage MostPopularPage
여기에는 미디어, 사용자 프로필 및 정적 페이지 (예 : 프론트 페이지)와 같이 특정 클래스가 액세스해야하는 특정 유형의 데이터를 설명하는 몇 가지 인터페이스 가 있습니다. 이 모든 것은 하나의 메가 캐시 로 구현 되지만 대신 인터페이스를 허용하도록 개별 클래스를 설계하므로 인스턴스의 종류에 상관하지 않습니다. 프로그램이 시작될 때 실제 인스턴스를 한 번 초기화 한 다음 생성자와 공용 속성을 통해 인스턴스를 특정 인터페이스 유형으로 캐스트하기 시작하면됩니다.
그건 그렇고, 의존성 주입 이라고 합니다; 일반 클래스 디자인 이 호출자로부터 자신 의 인스턴스를 인스턴스화 하거나 전역 상태를 참조 하는 대신 호출자의 종속성을 허용 하는 한 Spring 또는 특수 IoC 컨테이너를 사용할 필요가 없습니다 .
왜 인터페이스 기반 디자인을 사용해야합니까? 세 가지 이유 :
코드를보다 쉽게 읽을 수 있습니다. 인터페이스 에서 종속 클래스가 의존 하는 데이터를 정확하게 이해할 수 있습니다 .
Microsoft Access가 데이터 백엔드에 가장 적합하지 않다는 것을 알게되면 SQL Server라고하겠습니다.
SQL Server가 미디어에 특히 적합하지 않다는 것을 알고 있다면 시스템의 다른 부분에 영향을주지 않고 구현 을 중단 할 수 있습니다 . 그것이 바로 추상 추상화의 힘이 들어오는 곳입니다.
한 걸음 더 나아가려면 Spring (Java) 또는 Unity (.NET)와 같은 IoC 컨테이너 (DI 프레임 워크)를 사용할 수 있습니다. 거의 모든 DI 프레임 워크는 자체 수명 관리를 수행하며 특정 서비스 를 단일 인스턴스로 정의 할 수 있습니다 (종종 "싱글 톤"이라고 부르지 만 이는 친숙 함을위한 것입니다). 기본적으로 이러한 프레임 워크는 인스턴스를 수동으로 전달하는 원숭이 작업의 대부분을 저장하지만 반드시 필요한 것은 아닙니다. 이 디자인을 구현하기 위해 특별한 도구가 필요하지 않습니다.
완벽을 기하기 위해 위의 디자인도 이상적이지 않다는 점을 지적해야합니다. 캐시를 처리 할 때 (있는 그대로) 실제로 완전히 별도의 레이어 가 있어야합니다 . 다시 말해, 이와 같은 디자인 :
+-IMediaRepository
|
캐시 (일반) --------------- +-IProfileRepository
▲ |
| +-IPageRepository
+ ----------------- + ----------------- +
| | |
IMediaCache IProfileCache IPageCache
| | |
| | |
VideoPage MyAccountPage MostPopularPage
이것의 장점은 Cache
리팩토링하기로 결정한 경우 인스턴스 를 해체 할 필요가 없다는 것입니다 . 대체 구현을 제공하여 미디어가 저장되는 방식을 간단하게 변경할 수 있습니다 IMediaRepository
. 이것이 어떻게 결합되는지에 대해 생각하면 캐시의 물리적 인스턴스를 하나만 작성하므로 동일한 데이터를 두 번 가져올 필요가 없습니다.
세계의 모든 단일 소프트웨어가 높은 응집력과 느슨한 결합의 정확한 표준에 맞게 설계 될 필요는 없습니다. 프로젝트의 규모와 범위, 팀, 예산, 마감일 등에 따라 달라집니다. 그러나 최상의 디자인이 무엇인지 (단일 대신 사용) 요구하는 경우에는 이것이됩니다.
추신 다른 사람들이 언급했듯이 종속 클래스가 캐시를 사용하고 있다는 것을 인식하는 것이 가장 좋은 아이디어는 아닙니다. 즉, 결코 신경 쓰지 않아야하는 구현 세부 사항입니다. 즉, 전체 아키텍처는 여전히 위의 그림과 매우 유사하게 보일 것 입니다. 개별 인터페이스를 Caches 라고 부르지 는 않습니다 . 대신 서비스 또는 이와 유사한 이름을 지정하십시오 .