티스토리 뷰


유일무이한 인스턴스를 만들어 사용하기

 

 프로그래밍을 하다 보면 여러 개의 클래스에서 한 개의 객체에 접근하고 싶을 때가 많습니다. 다음과 같은 클래스가 있다고 가정해봅니다.

 

Code – Singleton.cs

class Singleton

{

    private int a;

 

    public Singleton()

    {

        a = 0;

    }

 

    internal void SetA(int val)

    {

        this.a = val;

    }

 

    internal int GetA()

    {

        return a;

    }

}

 

간단한 클래스이니 주석이나 설명은 생략하겠습니다.

 

이 클래스의 객체를 생성하여 a값을 접근하는 두 개의 각각의 클래스가 있다고 생각해봅시다.

 

Code – ClassA.cs

class ClassA

{

    Singleton singleton;

 

    public ClassA()

    {

        singleton = new Singleton();

    }

 

    internal void SetA(int val)

    {

        singleton.SetA(val);

    }

 

    internal void PrintA()

    {

        Console.WriteLine(singleton.GetA());

    }

}

Code – ClassB.cs

class ClassB

{

    Singleton singleton;

 

    public ClassB()

    {

        singleton = new Singleton();

    }

 

    internal void PrintA()

    {

        Console.WriteLine(singleton.GetA());

    }

}

 

ClassA에만 SetA함수가 존재하는 것 말고는 ClassA ClassB는 모두 Singleton클래스의 객체를 만들어 a값을 사용하는 동일한 클래스들입니다. (사실 프로그래밍을 하다 보면 다음 예제보다는 훨씬 복잡해지겠죠..)

 

그럼 메인 프로그램을 보겠습니다.

CODE – Program.cs

class Program

{

    static void Main(string[] args)

    {

        ClassA a = new ClassA();

        a.SetA(10);

        a.PrintA();

 

        ClassB b = new ClassB();

        b.PrintA();

    }

}

 

프로그램에서는 ClassA ClassB의 객체를 생성하여 a값을 셋팅하고 프린트 합니다.

제가 원하던 결과는

10

10

이 나오는 것이었습니다.

 

하지만 무심하게도 결과는

10

0

이 나오지요..

 

지금과 같은 경우 각각 ClassA ClassB에서 Singleton클래스의 객체를 따로따로 생성해주기 때문에 생기는 문제입니다.

 

그렇다면 ClassA ClassB에서 Singleton클래스의 객체를 따로 생성하지 않도록 코드를 변경하면 원하는 결과를 얻을 수 있을 것입니다.

 

ClassA ClassB에서 Singleton의 객체를 생성하지 않고 생성자에서 객체를 받아 사용하는 방법으로 변경해 보도록 하겠습니다.

 

Code – 변경된 ClassA.cs

class ClassA

 {

     Singleton singleton;

 

     public ClassA(Singleton s)

     {

         this.singleton = s;

     }

 

     internal void SetA(int val)

     {

         singleton.SetA(val);

     }

 

     internal void PrintA()

     {

         Console.WriteLine(singleton.GetA());

     }

 }

Code – 변경된 ClassB.cs

class ClassB

{

    Singleton singleton;

 

    public ClassB(Singleton s)

    {

        this.singleton = s;

    }

 

 

    internal void PrintA()

    {

        Console.WriteLine(singleton.GetA());

    }

}

 

변경된 ClassA ClassB를 사용하기 위해서는 각각의 생성자에 Singleton객체를 넘겨주어야 합니다. 하나의 Singleton클래스 객체를 생성하여 각각의 생성자에 넘겨누는 최종 프로그램의 소스를 만들어 보겠습니다.

 

Code – Program.cs

class Program

{

    static void Main(string[] args)

    {

        Singleton singleton = new Singleton();

 

        ClassA a = new ClassA(singleton);

        a.SetA(10);

        a.PrintA();

 

        ClassB b = new ClassB(singleton);

        b.PrintA();

    }

}

 

자 이제 우리가 원하는 결과를 얻을 수 있겠지요.

 

지금까지의 방법이 지금껏 제가 사용하던 방법이었습니다. 하지만 이 방법에는 큰 단점이 있는데, 공유할 객체가 한 개 내지 두 개와 같이 적을 경우엔 괜찮겠지만 여러 개의 객체를 공유해야 하는 경우 생성자가 쓸데없이 복잡해질 뿐 아니라 소스가 계속 변경되고, 슬슬 머리가 아파오기  시작한다는 것입니다.

 

이것을 위해 객체를 하나만 만들어 사용할 수 있도록 Singleton이란 패턴이 존재합니다. (저도 오늘 책보면서 알게 되었습니다.) 그리고 이것을 알려드리고자 합니다.

 

아까 변경전의 소스에서 조금만 손을 본다면 ClassA ClassB의 생성자에 객체를 넘기지 않고도 한 개의 객체를 각각의 클래스에서 공유해서 사용할 수 있습니다.

 

먼저 한 개 이상의 객체가 생성되지 못하도록 Singleton클래스를 수정합니다.

최종 변경 Code – Singleton.cs

class Singleton

{

    private int a;

    private static Singleton singleton;

 

    private Singleton()

    {

        a = 0;

    }

 

    public static Singleton GetInstance()

    {

        if (singleton == null) singleton = new Singleton();

        return singleton;

    }

 

    internal void SetA(int val)

    {

        this.a = val;

    }

 

    internal int GetA()

    {

        return a;

    }

}

 

이 소스를 보면 의아한 점이 생길 것입니다. 객체를 생성하기 위해 new 를 사용하면 생성자에 접근하게 되는 데, 소스에서 보이는 생성자의 접근자가 private으로 되어있습니다. (private public 의 차이가 정확히 생각나지 않는 분은 그 공부부터 하세요) 접근자가 private으로 되어있다는 얘기는 외부에서 호출할 수 없다는 것입니다. 즉 다른 클래스에서 Singleton s = new Singleton();과 같이 사용할 수 없다는 것이죠.

 

 그렇다면 어떠한 방법으로 클래스의 객체를 생성하게 될까요? 답은 이곳에 있습니다.

private static Singleton singleton;

public static Singleton GetInstance()

{

    if (singleton == null) singleton = new Singleton();

    return singleton;

}

Singleton클래스의 내부에 전역적으로 선언된 static 객체가 있고, 이것을 리턴해주는 GetInstance() 메소드가 보이네요. 이 메소드 내부에서 new Singleton()을 통해 객체를 생성하는 것을 볼 수 있습니다. ‘아까 new를 통해서 생성할 수 없다고 하지 않았습니까?’ 아까도 말씀드렷다시피 new 를 사용하게 되면 생성자를 통해 객체를 생성한다고 하였는데 이와 같은 경우 클래스 내부에서 자신의 생성자에 접근하는 꼴이 되기 때문에 생성자가 private이라도 접근할 수 있는 것입니다. 이해가 될는지 모르겠네요.

 

 외부의 클래스에서 Singleton의 객체를 사용하기 위해서는 new 연산자를 통해 객체를 생성하는 것이 아니라 Singleton.GetInstance() 메소드를 호출해야 할 것입니다. 이제 변경된 Singleton클래스의 객체를 사용하기 위해 ClassA ClassB를 수정하겠습니다.

 

최종 변경 Code – ClassA.cs

class ClassA

{

    Singleton singleton;

 

    public ClassA()

    {

        singleton = Singleton.GetInstance();

    }

 

    internal void SetA(int val)

    {

        singleton.SetA(val);

    }

 

    internal void PrintA()

    {

        Console.WriteLine(singleton.GetA());

    }

}

최종 변경 Code – ClassB.cs

class ClassB

 {

     Singleton singleton;

 

     public ClassB()

     {

         singleton = Singleton.GetInstance();

     }

 

 

     internal void PrintA()

     {

         Console.WriteLine(singleton.GetA());

     }

 }

 

Program.cs는 변경 전 처음과 같습니다.

최종 변경 Code – Program.cs

class Program

 {

     static void Main(string[] args)

     {

         ClassA a = new ClassA();

         a.SetA(10);

         a.PrintA();

 

         ClassB b = new ClassB();

         b.PrintA();

     }

 }

 

그리고 원하는 결과를 얻을 수 있었습니다.

10

10

 

 

지금까지 알려드린 방법이 Singleton패턴의 가장 고전적인 방식이며 기초가 되는 방법입니다.

이렇게 사용하였을 경우 단일 프로그램에서는 잘 돌아가겠지만, 다중 스레드 프로그래밍 모델에서는 잘 돌아가지 않을 수 있습니다. (이것이 해결된 버전은 따로 문의를 주거나 싱글턴 패턴을 검색하여 좀 더 살펴보시기 바랍니다.)

반응형
댓글