[C#] 인터페이스를 이용한 콜백

728x90
반응형

인터페이스와 다형성

인터페이스에 속한 메서드는 모두 가상 메서드에 속한다.

C# 컴파일러가 인터페이스의 메서드를 가상 메서드로 간주하기 때문에 virtual 예약어를 일부러 지정하지 못하게 막고 있다. 인터페이스를 상속받은 자식 클래스에서도 해당 메서드에 override 예약어를 지정하지 못하게 막는다.

virtual/override 예약어를 막는다기보다는 굳이 그럴 필요가 없으니 표시하지 못하게 한다는 표현이 더 어울린다.

인터페이스의 메서드는 가상 메서드이기 때문에 다형성의 특징이 그대로 적용된다.

 


인터페이스를 이용한 콜백

인터페이스에 포함된 메서드는 상속된 클래스에서 반드시 구현한다는 보장이 있다.

이런점을 이용해서 콜백 구현이 가능하다.

 

간단한 예제

간단한 예제를 하나 생성해서, Log를 찍기위해 인터페이스를 이용한 콜백기능을 만들어 보았다.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LogicApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Process.ItemProcess itemProcess = new Process.ItemProcess();
            itemProcess.Run();
        }
    }
}

ItemProcess 라는 Class 를 호출하는 부분을 Main 메소드에 넣어줍니다.

대부분의 예제들이 Main 메소드를 이용하지만, 저는 그냥 실행만 시켜주는 호출단만 생성하였습니다. 

 

 

 

Data.Item.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LogicApp.Data
{
    public class Item
    {
        public Process.ILogger Logger { get; set; }

        public Item(Process.ILogger logger)
        {
            this.Logger = logger;
        }

        public bool GetList()
        {
            Logger.SetLog();
            return true;
        }
    }
}

Item 객체의 생성자에서는 Class 가 아닌 ILogger 형태의 인터페이스를 받아주어 의존성을 주입해줍니다.

Item 객체를 생성할때 받은 ILogger를 이용해서, 어느 시점에서든 SetLog 메소드를 호출할 수 있는 구조를 만듭니다.

 

 

Process.ItemProcess.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LogicApp.Process
{
    public class ItemProcess : ILogger
    {
        public void Run()
        {
            Data.Item item = new Data.Item(this);
            item.GetList();
        }

        public void SetLog()
        {
            Console.WriteLine("ItemProcess.SetLog");
        }
    }
}

Main 메소드에서 가장 먼저 호출되는 부분입니다. 

ItemProcess에 ILogger를 상속받아 인터페이스 메소드를 구현하였으며, Data.Item 클래스 생성시, 자신의 객체를 this로 넘겨줍니다. ILogger 형태의 인터페이스로 생성자가 받아주기 때문에 구현부가 있는 자신을 인자로 넘겨줍니다.

이것이 인터페이스를 이용한 콜백입니다.

 


의존성 주입으로 변경

public class WebLogger : ILogger
{
    public WebLogger()
    {

    }
    public void SetLog()
    {
        Console.WriteLine("A");
    }
}



public interface ILogger
{
    void SetLog();
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LogicApp.Process
{
    public class ItemProcess
    {
        public void Run()
        {
            Data.Item item = new Data.Item(new WebLogger());
            item.GetList();
        }
    }
}

위와 같이 ILogger를 상속받아 별도의 클래스를 생성하여, Data.Item 클래스 생성시 넘겨주어도 됩니다.

인터페이스를 이용함으로써, ILogger 인터페이스를 상속받아 새로운 구현부가 존재하는 클래스를 넘겨줄수도 있는 형태로도 운영이 가능합니다. 이는 의존성 주입의 일부분으로 보셔도 됩니다.

 


 

콜백을 구현할 때 델리게이트와 인터페이스 중에 적당한 선택 기준이 있을까요? 사실 거의 대부분의 콜백 패턴에 대해 인터페이스를 사용하는 방법이 더 선호된다.

왜냐하면, 델리게이트는 각 메서드마다 정의해야 하는 불편함이 있지만, 인터페이스는 하나의 타입에서 여러 개의 메서드 계약을 담을 수 있기 때문이다.

대신 델리게이트는 "여러 개의 메서드"를 담을 수 있어서 한번의 호출을 통해 다중으로 등록된 콜백 메서드를 호출 할 수 있다는 고유의 장점이 있다.

 

따라서 다중 호출에 대한 필요성만 없다면 인터페이스를 이용해 콜백을 구현하는 것이 더 일반적입니다.

 

 

 

728x90