메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

한빛랩스 - 지식에 가능성을 머지하다 / 강의 콘텐츠 무료로 수강하시고 피드백을 남겨주세요. ▶︎

IT/모바일

Advanced C# 4. DP - Monostate, Utility, Singleton Pattern

한빛미디어

|

2003-02-05

|

by HANBIT

14,434

저자: 한동훈(traxacun at unitel.co.kr)

Singleton 패턴에 대해서는 이전에 자세히 설명한 적이 있으며, 나중에 다시 한 번 간략하게 소개할 것이다. Singleton 패턴과 멀티 스레드에서 발생할 수 있는 문제점과 해결 방안에 대해서 알고 싶은 분은 디자인 패턴으로 알아본 Double Checked Lock(DCL)를 참고하기 바란다. C#에서는 Singleton 클래스를 작성할 수 있는 문법을 제공하기 때문에 GoF의 책이나 다른 곳에 소개된 코드와는 전혀 다른 모습으로 나타난다.

Singleton Pattern

C#에서 간단한 Singleton 클래스는 다음과 같이 작성할 수 있다.


[그림 1] Singleton 클래스 다이어그램

클래스 다이어그램에서 새로운 선을 볼 수 있는데 위와 같은 선은 자기 자신을 참조하는 것을 의미한다. 코드를 살펴보면서 비교해보기 바란다.


[그림 2] Singleton 패턴

Singleton 패턴은 클래스의 인스턴스를 하나만 생성하는 것을 보장하여 다른 클래스에서 단일 액세스(global access point)를 제공하기 위해 사용된다. Singleton 패턴은 보통 sealed 클래스로 선언되며 직접 인스턴스를 생성하지 못하게 하기 위해 생성자를 private으로 선언한다. 위 클래스는 Instance 속성을 사용할 때까지 인스턴스가 생성되지 않기 때문에 게으른 초기화(lazy initialization)라 한다. 결과에서 알 수 있는 것처럼 두 인스턴스가 동일한 인스턴스를 참조하고 있다는 것을 알 수 있다.

Monostate Pattern

Singleton 클래스와 비슷한 것으로 Monostate 패턴이 있다. Monostate 패턴은 Singleton 클래스와 달리 인스턴스 생성에 제한을 두지 않는다. Monostate 패턴을 살펴본 다음에 Singleton 패턴과의 차이점에 대해서 살펴보자.


[그림 3] Monostate 클래스 다이어그램


[그림 4] Monostate 클래스


[그림 5] Tester 클래스

실행결과는 다음과 같다.

m1: 1
m2: 2
m1: 3

press any key to end...


Singleton 패턴이 상속을 금지시키며, 상속을 하는 경우에는 코드에 복잡함이 추가되며 멀티 스레드 환경에서 쉽게 깨지는 경우가 생기지만 Monostate 패턴의 장점은 Monostate에서 Monostate로의 상속을 위해 복잡한 작업을 할 필요가 전혀 없다는 것이다. 메서드나 속성은 모두 인스턴스 수준으로 선언하지만 모든 필드는 static으로 선언되기 때문에 내부 데이터를 공유하는 데 전혀 문제가 발생하지 않는다.

다음은 Monostate 패턴의 상속을 다룬 것이다.

Monostate 상속


[그림 6] InheritedMonostate 클래스

위에서 주의할 점은 Monostate 클래스의 Next() 메소드에 virtual등의 키워드를 사용하지 않은채 InheritedMonostate에서 Next() 메소드를 재정의한다는 것이다. Monostate 클래스의 소스 코드는 이전과 동일하다. 다음은 InheritedMonostate 클래스의 소스 코드다.


[그림 7] InheritedMonostate 클래스

Tester 클래스를 조금만 수정하고 실행해보면 상속받은 InheritedMonostate도 마찬가지로 잘 동작한다는 것을 알 수 있다. 만약 멀티 스레드 환경을 고려해야한다면 Monostate 클래스의 Next() 메서드를 다음과 같은 형태로 수정하기만 하면 된다.


[그림 8] 클래스 수준 잠금

마찬가지로 상속된 InheritedMonostate 클래스도 멀티 스레드 환경을 고려해야한다면 위와 같이 수정하면 된다.


[그림 9] 상속된 클래스에서의 클래스 수준 잠금

[그림 9]에서 주의할 점은 Monostate 클래스에 대한 잠금이지 InheritedMonostate 클래스에 대한 잠금이 아니라는 점이다. 이미 잘 알고 있겠지만 상속받은 클래스의 인스턴스를 생성하면 부모 클래스의 인스턴스도 내부적으로 생성된다. 여기서 동기화가 필요한 부분은 Monostate 클래스의 static 필드이므로 상속 클래스에서도 Monostate 클래스에 대한 잠금을 사용해야 한다는 것이다.

Singleton vs Monostate

Singleton 패턴과 Monostate 패턴의 차이점은 다음과 같이 요약할 수 있다.
  • Singleton 패턴이 단일 구조를 보장하기 위한 것이라면 Monostate 패턴은 단일 행위(behavior of singularity)를 보장하기 위한 것이다.
  • Singleton 패턴은 인스턴스 필드에 상태를 저장하지만 Monostate 패턴은 static 필드에 상태를 저장한다.
  • Singleton 패턴은 lock(this)와 같이 인스턴스 수준의 잠금을 필요로하지만 Monostate 패턴은 lock( typeof( className ) )과 같이 클래스 수준의 잠금을 필요로 한다.
  • Singleton 패턴은 인스턴스 생성에 제약을 두지만 Monostate 패턴은 인스턴스 생성에 제한을 두지 않는다.
  • Singleton을 상속받는 것은 어렵지만 Monostate를 상속받는 것은 상대적으로 쉽다.
  • Singleton 패턴은 다양한 클래스를 상속받아 구현할 수 있지만 Monostate 패턴은 Monostate 클래스이외의 클래스를 상속받아 구현할 수 없다.
어떤 패턴을 사용할 것인가는 자신이 작성하는 코드의 종류에 따라 다를 것이다. Monostate 패턴은 Borg 패턴으로도 알려져있다.

Utility Pattern

Utility 패턴에는 특별한 것이 없다. 어쩌면 모든 기능들을 클래스로 모아 두어서 궁극적으로 "God Class"를 만들기 위해 쓰는 패턴인지도 모르겠다.(설마, 농담은 농담으로! :) Utility 패턴의 다른 이름은 SingleCall 패턴이다. Utility 패턴의 특징을 정리하면 다음과 같다.
  • Utility 패턴의 클래스는 sealed 클래스로 선언한다.
  • 인스턴스 생성을 하지 않는다. 생성자를 private으로 선언하며 필요한 경우에 인스턴스를 반환하는 메소드를 정의한다.(ex. AppDomain.CreateDomain())
  • 하나 이상의 static 메서드나 필드를 제공한다.
이와 같은 특징 때문에 Win32 API에 대한 래퍼 클래스를 작성하는데 Utility 패턴을 적용한다. 닷넷 프레임워크의 다양한 부분에 Utility 패턴이 적용되어 있으며 대표적인 것들은 다음과 같다.
  • System.Activator
  • System.AppDomain
  • System.Environment
  • System.IO.File
  • System.Runtime.InteropServices.Marshal
AppDomain.CreateDomain() 메서드는 기존 인스턴스를 반환하기 위해 Utility 패턴을 사용한 경우이며 System.IO.File 클래스의 모든 메서드가 static으로 선언되어 있다는 것을 알 수 있다. Exists(), Delete(), Copy() 등의 메서드를 사용해본 경험이 있을 것이다. 단순히 정보를 반환하기 위해 사용된 경우로는 System.Environment 클래스가 있다.

Win32 API에 대한 래퍼(Wrapper) 클래스를 작성하는 경우 대부분의 경우 API를 호출하고 결과를 받아오면 되기 때문에 Utility 패턴을 적용하기 가장 좋은 경우다. Win32 API 래퍼 클래스 작성에 대해서는 다음에 살펴볼 것이다.

참고자료
TAG :
댓글 입력
자료실

최근 본 상품0