C #에서 제어 반전을 사용하는 방법

제어 반전과 종속성 주입을 모두 사용하면 애플리케이션의 구성 요소 간의 종속성을 끊고 애플리케이션을보다 쉽게 ​​테스트하고 유지 관리 할 수 ​​있습니다. 그러나 제어 반전과 종속성 주입은 동일하지 않습니다. 둘 사이에는 미묘한 차이가 있습니다.

이 기사에서는 제어 패턴의 반전을 조사하고 C #의 관련 코드 예제를 사용하여 종속성 주입과 어떻게 다른지 이해합니다.

이 문서에 제공된 코드 예제를 사용하려면 시스템에 Visual Studio 2019가 설치되어 있어야합니다. 아직 복사본이없는 경우 여기에서 Visual Studio 2019를 다운로드 할 수 있습니다. 

Visual Studio에서 콘솔 애플리케이션 프로젝트 만들기

먼저 Visual Studio에서 .NET Core 콘솔 애플리케이션 프로젝트를 만들어 보겠습니다. Visual Studio 2019가 시스템에 설치되어 있다고 가정하고 아래에 설명 된 단계에 따라 Visual Studio에서 새 .NET Core 콘솔 애플리케이션 프로젝트를 만듭니다.

  1. Visual Studio IDE를 시작합니다.
  2. "새 프로젝트 만들기"를 클릭합니다.
  3. "새 프로젝트 만들기"창의 표시된 템플릿 목록에서 "콘솔 앱 (.NET Core)"을 선택합니다.
  4. 다음을 클릭하십시오. 
  5. 다음에 표시된 "새 프로젝트 구성"창에서 새 프로젝트의 이름과 위치를 지정합니다.
  6. 생성을 클릭합니다. 

그러면 Visual Studio 2019에 새로운 .NET Core 콘솔 애플리케이션 프로젝트가 생성됩니다.이 프로젝트를 사용하여이 문서의 후속 섹션에서 제어 반전을 살펴볼 것입니다.

역전 제어 란 무엇입니까?

IoC (Inversion of Control)는 프로그램의 제어 흐름이 반전되는 디자인 패턴입니다. 제어 패턴의 반전을 활용하여 애플리케이션의 구성 요소를 분리하고, 종속성 구현을 바꾸고, 종속성을 모의하고, 애플리케이션을 모듈 식 및 테스트 가능하게 만들 수 있습니다.

의존성 주입은 반전 제어 원리의 하위 집합입니다. 즉, 종속성 주입은 제어 반전을 구현하는 한 가지 방법 일뿐입니다. 예를 들어 이벤트, 대리자, 템플릿 패턴, 팩토리 메서드 또는 서비스 로케이터를 사용하여 제어 반전을 구현할 수도 있습니다.

제어 디자인 패턴의 반전은 개체가 어떤 활동을 수행하기 위해 의존하는 개체를 생성해서는 안된다고 말합니다. 대신 외부 서비스 또는 컨테이너에서 이러한 개체를 가져와야합니다. 이 아이디어는 "전화하지 마세요. 전화하겠습니다"라는 할리우드 원칙과 유사합니다. 예를 들어, 애플리케이션이 프레임 워크에서 메소드를 호출하는 대신 프레임 워크는 애플리케이션에서 제공 한 구현을 호출합니다. 

C #의 제어 반전 예제

주문 처리 애플리케이션을 빌드 중이고 로깅을 구현하려고한다고 가정하십시오. 단순성을 위해 로그 대상이 텍스트 파일이라고 가정합니다. 솔루션 탐색기 창에서 방금 만든 콘솔 응용 프로그램 프로젝트를 선택하고 ProductService.cs 및 FileLogger.cs라는 두 개의 파일을 만듭니다.

    공용 클래스 ProductService

    {

        개인 읽기 전용 FileLogger _fileLogger = new FileLogger ();

        공개 무효 로그 (문자열 메시지)

        {

            _fileLogger.Log (메시지);

        }

    }

    공용 클래스 FileLogger

    {

        공개 무효 로그 (문자열 메시지)

        {

            Console.WriteLine ( "FileLogger의 내부 로그 메서드.");

            LogToFile (메시지);

        }

        private void LogToFile (문자열 메시지)

        {

            Console.WriteLine ( "메소드 : LogToFile, 텍스트 : {0}", message);

        }

    }

앞의 코드 조각에 표시된 구현은 정확하지만 제한이 있습니다. 데이터를 텍스트 파일에만 로깅하도록 제한됩니다. 어떤 식 으로든 데이터를 다른 데이터 원본이나 다른 로그 대상에 기록 할 수 없습니다.

유연하지 않은 로깅 구현

데이터를 데이터베이스 테이블에 기록하려면 어떻게해야합니까? 기존 구현은이를 지원하지 않으며 구현을 변경해야합니다. FileLogger 클래스의 구현을 변경하거나 DatabaseLogger와 같은 새 클래스를 만들 수 있습니다.

    공용 클래스 DatabaseLogger

    {

        공개 무효 로그 (문자열 메시지)

        {

            Console.WriteLine ( "DatabaseLogger의 내부 로그 메서드.");

            LogToDatabase (메시지);

        }

        private void LogToDatabase (문자열 메시지)

        {

            Console.WriteLine ( "메소드 : LogToDatabase, 텍스트 : {0}", 메시지);

        }

    }

아래 코드 스 니펫에 표시된대로 ProductService 클래스 내에 DatabaseLogger 클래스의 인스턴스를 만들 수도 있습니다.

공용 클래스 ProductService

    {

        개인 읽기 전용 FileLogger _fileLogger = new FileLogger ();

        개인 읽기 전용 DatabaseLogger _databaseLogger =

         new DatabaseLogger ();

        공개 무효 LogToFile (문자열 메시지)

        {

            _fileLogger.Log (메시지);

        }

        공개 무효 LogToDatabase (문자열 메시지)

        {

            _fileLogger.Log (메시지);

        }

    }

그러나 이것이 작동하더라도 응용 프로그램의 데이터를 EventLog에 기록해야하는 경우 어떻게해야합니까? 디자인이 유연하지 않으며 새 로그 대상에 기록해야 할 때마다 ProductService 클래스를 변경해야합니다. 이것은 번거로울뿐만 아니라 시간이 지남에 따라 ProductService 클래스를 관리하기가 매우 어렵게 만듭니다.

인터페이스로 유연성 추가 

이 문제에 대한 해결책은 구체적인 로거 클래스가 구현할 인터페이스를 사용하는 것입니다. 다음 코드 조각은 ILogger라는 인터페이스를 보여줍니다. 이 인터페이스는 두 개의 구체적인 클래스 FileLogger 및 DatabaseLogger에 의해 구현됩니다.

공용 인터페이스 ILogger

{

    무효 로그 (문자열 메시지);

}

FileLogger 및 DatabaseLogger 클래스의 업데이트 된 버전은 다음과 같습니다.

공용 클래스 FileLogger : ILogger

    {

        공개 무효 로그 (문자열 메시지)

        {

            Console.WriteLine ( "FileLogger의 내부 로그 메서드.");

            LogToFile (메시지);

        }

        private void LogToFile (문자열 메시지)

        {

            Console.WriteLine ( "메소드 : LogToFile, 텍스트 : {0}", message);

        }

    }

공용 클래스 DatabaseLogger : ILogger

    {

        공개 무효 로그 (문자열 메시지)

        {

            Console.WriteLine ( "DatabaseLogger의 내부 로그 메서드.");

            LogToDatabase (메시지);

        }

        private void LogToDatabase (문자열 메시지)

        {

            Console.WriteLine ( "메소드 : LogToDatabase, 텍스트 : {0}", 메시지);

        }

    }

이제 필요할 때마다 ILogger 인터페이스의 구체적인 구현을 사용하거나 변경할 수 있습니다. 다음 코드 조각은 Log 메서드를 구현 한 ProductService 클래스를 보여줍니다.

공용 클래스 ProductService

    {

        공개 무효 로그 (문자열 메시지)

        {

            ILogger 로거 = new FileLogger ();

            logger.Log (메시지);

        }

    }

여태까지는 그런대로 잘됐다. 그러나 ProductService 클래스의 Log 메서드에서 FileLogger 대신 DatabaseLogger를 사용하려면 어떻게해야합니까? 요구 사항을 충족하기 위해 ProductService 클래스에서 Log 메서드의 구현을 변경할 수는 있지만 디자인을 유연하게 만들 수는 없습니다. 이제 제어 반전 및 종속성 주입을 사용하여 설계를보다 유연하게 만들어 보겠습니다.

종속성 주입을 사용하여 제어 반전

다음 코드 조각은 생성자 주입을 사용하여 구체적인 로거 클래스의 인스턴스를 전달하기 위해 종속성 주입을 활용하는 방법을 보여줍니다.

공용 클래스 ProductService

    {

        개인 읽기 전용 ILogger _logger;

        공공 ProductService (ILogger 로거)

        {

            _logger = 로거;

        }

        공개 무효 로그 (문자열 메시지)

        {

            _logger.Log (메시지);

        }

    }

마지막으로 ILogger 인터페이스의 구현을 ProductService 클래스에 전달하는 방법을 살펴 보겠습니다. 다음 코드 조각은 FileLogger 클래스의 인스턴스를 만들고 생성자 주입을 사용하여 종속성을 전달하는 방법을 보여줍니다.

static void Main (string [] args)

{

    ILogger 로거 = new FileLogger ();

    ProductService productService = new ProductService (logger);

    productService.Log ( "Hello World!");

}

그렇게함으로써 우리는 통제권을 뒤집 었습니다. ProductService 클래스는 더 이상 ILogger 인터페이스 구현의 인스턴스를 만들거나 사용할 ILogger 인터페이스 구현을 결정할 책임이 없습니다.

제어 반전 및 종속성 주입은 객체의 자동 인스턴스화 및 수명주기 관리를 지원합니다. ASP.NET Core에는 제한된 기능 집합이있는 간단한 기본 제공 제어 컨테이너 반전이 포함되어 있습니다. 필요가 간단한 경우이 기본 제공 IoC 컨테이너를 사용하거나 추가 기능을 활용하려는 경우 타사 컨테이너를 사용할 수 있습니다.

여기의 이전 게시물에서 ASP.NET Core에서 제어 반전 및 종속성 주입을 사용하는 방법에 대해 자세히 알아볼 수 있습니다.