디자인 패턴은 소프트웨어 디자인 및 아키텍처의 중용한 부분이다. 디자인 패턴이란 ‘Creational Design Patterns’, ‘Structural Design Patterns’, ‘Behavioral Design Patterns’와 같이 3가지 유형으로 나누어 볼 수 있다(C-SharpCorner 2013). 애플리케이션이 시작될 때 어떤 클래스가 최초 한 번만 메모리를 할당하고(Static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴(JEONG_AMATEUR 2017)인 ‘Singleton Design Pattern’에 대해 간략하게 정리하였다. Singleton과 Static 함수의 차이점은 Singleton인 경우 멤머 변수 등을 가지며 생성자/소멸자에서 뭔가를 해야 할 경우 그리고 Static 함수로 구성하는 경우는 멤버 변수 및 this 포인트를 쓸 필요 없으며 생성자/소멸자에서 뭔가 할 필요가 없을 때(KLDP 2013) 이다.
Singleton 패턴의 장단점에 대해서는 ‘Singleton Design Pattern In C#’ 글을 참고하자.
- What is Singleton Design Pattern?
- Advantages of Singleton Pattern
- Disadvantages of Singleton Pattern
- Singleton class vs. Static methods
- How to Implement Singleton Pattern in your code
아래의 소스(콘솔예제)는 기본적인 사용법과 스레드(thread)에 안정적인 사용법을 보여준다.
Default singleton
using System;
namespace SingletonDemo
{
public sealed class SingletonDefault
{
private static int counter = 0;
private static SingletonDefault instance = null;
public static SingletonDefault GetInstance
{
get
{
if (instance == null)
instance = new SingletonDefault();
return instance;
}
}
private SingletonDefault()
{
counter++;
Console.WriteLine("Counter Value D : " + counter.ToString());
}
public void PrintMessage(string message)
{
Console.WriteLine(message);
}
}
}
Lazy singleton (thread)
using System;
namespace SingletonDemo
{
public sealed class SingletonLazy
{
private static int counter = 0;
private static readonly Lazy<SingletonLazy> instance = new Lazy<SingletonLazy>(() => new SingletonLazy());
public static SingletonLazy GetInstance
{
get
{
return instance.Value;
}
}
private SingletonLazy()
{
counter++;
Console.WriteLine("Counter Value S : " + counter.ToString());
}
public void PrintMessage(string message)
{
Console.WriteLine(message);
}
}
}
사용예
using System;
namespace SingletonDemo
{
internal class Program
{
private static void Main(string[] args)
{
if (args == null || args.Length == 0)
{
Console.WriteLine("Code when no arguments are supplied");
}
else
{
Console.WriteLine("Code when arguments are supplied");
}
Console.WriteLine("-----------------------------");
SingletonDefault D1 = SingletonDefault.GetInstance;
D1.PrintMessage("D1 messageD1");
SingletonDefault D2 = SingletonDefault.GetInstance;
D2.PrintMessage("D2 messageD2");
Console.WriteLine("-----------------------------");
SingletonLazy S1 = SingletonLazy.GetInstance;
S1.PrintMessage("S1 messageS1");
SingletonLazy S2 = SingletonLazy.GetInstance;
S2.PrintMessage("S2 messageS2");
SingletonLazy.GetInstance.PrintMessage(Environment.NewLine + "Test...End...");
Console.ReadLine();
}
}
}
// Generics
using System;
using System.Threading.Tasks;
namespace ConsoleExam
{
internal class Program
{
private static async Task Main()
{
Console.WriteLine(await Singleton<Hello>.Go.PrintMessage("가가가 - ") + Singleton<Hello>.Go.Time + "s");
Singleton<Hello>.Go.Name = "World";
Console.WriteLine(await Singleton<Hello>.Go.PrintMessage("나나나 - ") + Singleton<Hello>.Go.Time + "s");
var instance = Singleton<Hello>.Go;
instance.Name = "Total";
instance.Time = 3;
Console.WriteLine(await instance.PrintMessage("닭닭닭 - ") + instance.Time + "s");
}
}
public class Singleton<T> where T : class, new()
{
private Singleton() { }
private static readonly Lazy<T> instance = new(() => new T());
public static T Go { get { return instance.Value; } }
}
public class Hello
{
private static int counter = 0;
public string Name { get; set; }
public double Time { get; set; }
public Hello()
{
Name = "Hello";
Time = 1;
counter++;
Console.WriteLine("Counter : " + counter.ToString());
}
public async Task<string> PrintMessage(string message)
{
var str = Name + " : " + message;
await Task.Delay(TimeSpan.FromSeconds(Time));
return str;
}
}
}