프로그래밍/C#

[C#] 디자인패턴-싱글톤 패턴(Singleton Pattern)

Victory_HA 2022. 7. 3. 18:39

싱글톤 패턴

  • 싱글톤 패턴은 해당 객체의 메모리를 정적으로 할당하여 하나의 객체에만 접근하는 방법입니다.
  • 따라서 프로그램이 동작하는 동안 최초로 생성된 객체 하나에만 접근하게 되므로 데이터를 접근하고 수정이 용이합니다.
  • 생성자를 다른 곳에서 새롭게 선언을 하더라도 이미 정적으로 선언된 객체가 반환되기 때문에 중복되어 생성되는 것을 방지 할 수 있습니다.
  • 초기 객체를 생성을 하게 되면 정적 메모리에 올라가기 때문에 이후 호출하는데 아주 빠르게 접근할 수 있는 장점이 있습니다.
  • 인스턴스를 생성할 때 매개변수를 지정하는 것을 문제가 추천하지 않습니다.
    • 인스턴스를 여러번 호출할 수 있는데, 매개변수가 달라지는 경우 문제가 될 수 있습니다.
    • 매개변수를 사용하여 인스턴스에 액세스 하는 경우엔 팩토리 패턴이 더 적합합니다.

예제1

  • 아래 예제에서 TableServer 클래스에서 servers라는 List를 생성합니다.
  • TableServer()는 servers에 항목을 추가
  • GetNextServer() : servers의 다음 항목을 반환

TableServer.cs

public class TableServer
{
    private List<string> servers = new List<string>();
    private int nextServer = 0;

    public TableServer()
    {
        servers.Add("1");
        servers.Add("2");
        servers.Add("3");
        servers.Add("4");
    }
    public string GetNextServer()
    {
        string output = servers[nextServer];

        nextServer += 1;

        if (nextServer >= servers.Count)
            nextServer = 0;

        return output;
    }
}

Program.cs

public class Program
{
    static void Main(string[] args)
    {
        var test = new TableServer();

        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("server is : " + test.GetNextServer());
        }

        Console.ReadLine();
    }
}

출력 결과


예제2

  • TableServer 인스턴스를 2개 생성합니다.
  • Host1GetNext(), Host2GetNext() 메서드 생성합니다.
public class Program
{
    static TableServer host1 = new TableServer();    
    static TableServer host2 = new TableServer();

    static void Main(string[] args)
    {
        var test = new TableServer();

        for (int i = 0; i < 5; i++)
        {
            //Console.WriteLine("server is : " + test.GetNextServer());
            Host1GetNext();
            Host2GetNext();
        }

        Console.ReadLine();
    }

    private static void Host1GetNext()
    {
        Console.WriteLine("host1 is : " + host1.GetNextServer());
    }

    private static void Host2GetNext()
    {
        Console.WriteLine("host2 is : " + host2.GetNextServer());
    }
}

출력 결과

  • host1, host2 각각의 리스트를 출력합니다.
  • 이것은 Program 클래스 내부에 두개의 인스턴스가 존재하기 때문에 가능합니다.
  • 하지만, 첫번째 결과와 동일하게 출력을 하고싶다면 어떻게 해야할까요.
  • 우선 인스턴스끼리 동기화를 시켜줘야합니다.

예제3 (싱글톤 패턴 사용)

  • 예제2의 문제를 해결하기 위해 싱글톤 패턴을 사용해봅시다.
  • 우선 생성자를 private로 변경합니다.
private TableServer()
{
    servers.Add("1");
    servers.Add("2");
    servers.Add("3");
    servers.Add("4");
}
  • _instance 멤버를 선언 및 초기화합니다.
    • static을 사용함으로써 클래스를 인스턴스화하지 않고도 액세스 할 수 있습니다만,
    • private을 사용하여 인스턴스화 하지지 않고 액세스 하는 이슈를 없앱니다.
    • readonly를 사용함으로써 생성된 인스턴스의 값에 다른 인스턴스가 overwrite할 수 없게 됩니다.
private static readonly TableServer _instance = new TableServer();
  • GetTableServer()생성합니다.
    • public으로 지정함으로 써 TableServer의 인스턴스에서 GetTableServer()를 호출할 수 있습니다.
    • 이 메서드에선 TableServer클래스의 인스턴스를 반환합니다.
    • _instance를 반환함으로써, new TableServer()에 의하여 인스턴스가 생성됩니다.
    • 이 방법은 TableServer클래스를 인스턴스화 하는 유일한 방법입니다.
    • GetTableServer()를 호출하기 전까지 인스턴스가 생성되지 않습니다.
public static TableServer GetTableServer()
{
    return _instance;
}
  • host1, host2 인스턴스 생성
    • 인스턴스화 하는 방법이 달라졌으니, Program 클래스에서 host1,host2 인스턴스(객체) 생성 방법도 달라졌습니다.
static TableServer host1 = TableServer.GetTableServer();
static TableServer host2 = TableServer.GetTableServer();

Program.cs

public class Program
{
    static TableServer host1 = TableServer.GetTableServer();
    static TableServer host2 = TableServer.GetTableServer();
    static void Main(string[] args)
    {
        //var test = new TableServer();

        for (int i = 0; i < 10; i++)
        {
            //Console.WriteLine("server is : " + test.GetNextServer());
            Host1GetNext();
            Host2GetNext();
        }

        Console.ReadLine();
    }

    private static void Host1GetNext()
    {
        Console.WriteLine("host1 is : " + host1.GetNextServer());
    }

    private static void Host2GetNext()
    {
        Console.WriteLine("host2 is : " + host2.GetNextServer());
    }
}

TableServer.cs

public class TableServer
{
    private static readonly TableServer _instance = new TableServer();
    private List<string> servers = new List<string>();
    private int nextServer = 0;

    private TableServer()
    {
        servers.Add("1");
        servers.Add("2");
        servers.Add("3");
        servers.Add("4");
    }

    //public으로 지정함으로 써 TableServer의 인스턴스에서 GetTableServer()를 호출할 수 있습니다.
    public static TableServer GetTableServer()
    {
        return _instance;
    }
    public string GetNextServer()
    {
        string output = servers[nextServer];

        nextServer += 1;

        if (nextServer >= servers.Count)
            nextServer = 0;

        return output;
    }
}

출력 결과

  • 예제2의 문제가 해결됐습니다.
  • host1과 host2에서 GetNextServer()를 호출하여 인스턴스를 요청했고, 동일한 인스턴스를 얻었습니다.
  • 그리고 Host1GetNext(), Host2GetNext()에서 동일한 인스턴스의 GetNextServer()를 호출했기 예제2의 문제점이 해결된 것입니다.

  • 싱글톤 패턴은 다른 App사이에서 데이터를 공유할 때 유용할 것입니다.

'프로그래밍 > C#' 카테고리의 다른 글

[WPF] INotifyPropertyChanged 구현  (0) 2022.10.07
[WPF] Textbox 값 변경 이벤트  (0) 2022.10.07
[C#] ? 와 ?? 연산자  (0) 2022.06.22
[C#] 의존성 주입 (Dependency Injection)  (0) 2022.06.06
[C#] 람다연산자 =>  (0) 2022.06.06