MVVM+Command Pattern+Dependency Injection #1에 이어지는 Ninject를 사용한 Dependency Injection편이에요 :D

Ninject는 ‘번개처럼 빠른 인젝션’을 모토로 한 DI 프레임워크죠. Unity랑 비교해 보면 Unity가 전통적인 개발자에게 친숙한 인터페이스와 네이밍을 가진 반면 Ninject는 모던하고 약간은 팬시한 문법과 네이밍을 가졌고 다양한 조건에서 좀 더 자동화된 인젝션을 지원한다는 느낌이 들어요. 여튼 이 전에 만든 프로젝트를 정말로 약간만 수정하면 Unity를 Ninject로 바꿀 수 있어요. 이것이 바로 모듈화된 프로그래밍의 장점이죠 ;~)

우선 프로젝트 먼저 받아가시고요~


1. Ninject 라이브러리 참조

당연하겠지만 제일 먼저 라이브러리를 교체해야겠죠?

image

Ninject는 현재 5개의 어셈블리로 이루어져 있고 3개는 Extension, 2개는 거의 필수로 사용되는 어셈블리죠. 그 중 필수인 Ninject.Core와 Ninject.Conditions를 참조하면 돼요.
※Ninject.Condition 역시 사실 필수는 아니지만 프로젝트가 조금만 커져도 거의 필수로 사용됩니다.

2. ServiceConfiguration 수정

/// 
/// 서비스에서 사용할 인젝션 매핑을 설정하는 클래스
/// 
public class ServiceConfiguration : StandardModule
{
    public override void Load()
    {
        bool isBlend = App.IsBlend;
        bool isWeb = !isBlend;

        // IPhotoSerivce에 대한 바인딩 설정
        // 블렌드에서 실행할 때 바인딩만 다르게 표현
        if (isBlend)
        {
            Bind< IPhotoService>().To< MockPhotoService>();
        }
        else
        {
            Bind< IPhotoService>().To< LivePhotoService>().Only(When.Context.Variable("ServiceName").IsNotDefined);
            Bind< IPhotoService>().To< LivePhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Live"));
            Bind< IPhotoService>().To< FlickrPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Flickr"));
            Bind< IPhotoService>().To< DaumPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Daum"));
            Bind< IPhotoService>().To< NaverPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Naver"));
        }

        // 공통으로 사용할 바인딩
        // 서비스 제공자의 목록을 싱글턴으로 바인딩
        Bind< PhotoServiceProviderList>().ToConstant< PhotoServiceProviderList>(
            new PhotoServiceProviderList
            {
                new PhotoServiceProvider { Name = "Live", DisplayName = "Live", LogoSource = "/MashupImageSearch;component/Resource/WindowsLive2_20.png" },
                new PhotoServiceProvider { Name = "Flickr", DisplayName = "Flickr", LogoSource = "/MashupImageSearch;component/Resource/flickr2_20.png" },
                new PhotoServiceProvider { Name = "Daum", DisplayName = "Daum", LogoSource = "/MashupImageSearch;component/Resource/daum_20.png" },
                new PhotoServiceProvider { Name = "Naver", DisplayName = "Naver", LogoSource = "/MashupImageSearch;component/Resource/naver_20.png" },
            }
        ).Using< SingletonBehavior>();
    }
}

Unity가 UnityContainer를 제공했던 것과 유사하게 Ninject는 StandardModule을 제공해요. Ninject에서 Module이란 하나의 서비스를 구성하는 DI들의 바인딩 구성을 정의하죠.

바인딩(매핑)을 등록하는 문장도 굉장히 다른데요, RegisterType()이라는 하나의 메서드로 인자의 위치나 배열에 따라 순서를 정했던 Unity와 달리 Ninject는 Bind<타입>().To<타입>().조건… 과 같은 명시적인 형식을 가지고 있어요. 마치 LINQ 표현식의 발전 과정을 보는 듯하죠.

동적 조건 식은 조금 복잡한데요, Unity가 단순히 이름만 가지고 바인딩을 결정했던 것과는 달리 Ninject는 굉장히 다양한 시나리오와 커스터마이징이 가능한 파라미터들로 바인딩을 결정할 수 있어요. Only(When.Context.Variable(“키”).조건…과 같이 “오직 컨텍스트에 특정 변수가 조건을 만족할 때만 바인딩”으로 글을 읽듯이 읽을 수 있죠. 굉장히 가독성이 좋은 문법이라고 봐요. 다만 지네릭과 람다 익스프레션 문법을 굉장히 많이 활용하기 때문에 익숙하지 않다면 어려울 수 있겠네요.

마지막으로 Unity의 RegisterInstance()메서드는 Ninject는 Bind<타입>().ToConstant<타입>() 으로 표현하는 걸 볼 수 있고 인젝션할 대상의 라이프 타임 관리를 .Using<라이프타임 동작>() 과 같이 할 수있어요. 여기에서 서비스 제공자의 목록은 항상 같으므로 단순한 SingletonBehavior로 지정했죠.

3. ServiceLocator 수정

/// 
/// 서비스에서 사용할 설정 모듈에서 타입을 제공하는 클래스
/// 
public class ServiceLocator
{
    #region Singlton 컨테이너
    private static IKernel _kernel;
    /// 
    /// 클래스 초기화.
    /// 서비스에서 사용할 DI 컨테이너를 초기화합니다.
    /// 
    static ServiceLocator()
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel(new ServiceConfiguration());
        }
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 인젝션이 처리된 결과
    public static T Get< T>()
    {
        return _kernel.Get< T>();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 파라미터 이름
    /// 인젝션이 처리된 결과
    public static T Get< T>(string name)
    {
        return _kernel.Get< T>(With.Parameters.Variable("ServiceName", name));
    }
    #endregion Singlton 컨테이너

    /// 
    /// 설정에 따라 이미지 검색 뷰모델의 인스턴스를 반환합니다.
    /// 
    public ImageSearchViewModel ImageSearchViewModel
    {
        get { return Get< ImageSearchViewModel>(); }
    }

    /// 
    /// 설정에 따라 포토 서비스 제공자의 목록을 반환합니다.
    /// 
    public PhotoServiceProviderList PhotoServiceProviders
    {
        get { return Get< PhotoServiceProviderList>(); }
    }
}		

Unity에서는 UnityContainer가 인젝션 바인딩(매핑) 정보를 보관하고 관리하는 역할을 모두 수행하지만 Ninject는 Kernel이 관리하는 Module에서 인젝션 바인딩 정보를 이용하여 인젝션을 관리해요. 특별히 다른 점은 없고 다만 파라미터를 줄 때도 Kernel.Get<타입>(With.Parameters.Variable(“변수이름”, 변수값) 같은 식으로 명시적이고 가독성 있는 문법을 지원해요.

이 정도 수정만으로 Unity를 Ninject로 대체하는 마이그레이션이 끝났어요. 실행 결과는 당연히 동일하고요. 간단하죠?

DI 자체에 대해서는 언제 한번 자세히 다루고 싶지만 아직 내공이 딸려서 정확한 설명은 힘드네요. 시간도 부족하고요. 우선은 지금까지 했던 프로젝트를 기반으로 지식을 확장해가면서 경험을 쌓아도 충분할거라고 봐요.

저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo

매쉬업 이미지 검색 + MVVM모델 + CommandPattern 예제에서 길버트님이 MVVM과 Command Pattern을 활용하여 화면 표시를 담당하는 View와 데이터 및 비즈니스 로직을 담당하는 Model, 그리고 View에 표시될 데이터와 View의 흐름 제어를 담당할 ViewModel이 적용될 수 있는 멋진 데모를 보여주셨죠.

사용된 패턴과 용어 정리

MVVM은 MVC 패밀리(?)의 일족으로 MVP(Model View Presentation)과 매우 유사해요. 사실 전 MVC/MVP/MVVM은 모두 같은 원리이고 구현의 관점이 약간씩 다를 뿐이라고 생각해요. 여튼 중요한 건 뷰와 데이터를 어떻게 분리한 상태에서 컨트롤 할 것이냐죠.

Commnad Pattern은 주로 애플리케이션 전역에서 동일하게 받을 수 있는 사용자의 제어 명령에 대한 이벤트를 효과적이고 표준적으로 받기 위한 구현 방법이에요. 커맨드 패턴은 특히 MVVM처럼 뷰와 모델이 분리된 환경에서 매우 효과적으로 코드를 짤 수 있게 도와주는 역할을 하죠.

Dependency Injection(DI) Pattern은 클래스의 독립성을 극한까지 끌어올리기 위해 고안된 방법 중 하나에요. 인젝션의 핵심은 어떤 클래스가 참조하거나 이용하는 인스턴스를 그 클래스가 생성하지 않고 외부에서 생성자, 프로퍼티 또는 메서드를 사용하여 ‘주입’inject시켜 보다 투명하고 분리된 클래스를 작성하는 데 있어요.

이 글에서는 DI에대한 설명은 접고 그냥 어떻게 활용되었는지를 보도록 하고 자세한 정보는 다음 링크들을 체크해 보세요.

길버트님의 예제 리모델링!

백문이 불여일타! 일단 샘플 받아서 열어보시고요~


동작은 길버트님의 예제와 완벽하게 동일하므로 동작 완구는 생략했어요. 리모델링 과정을 알아보죠.
※보통 프로그래밍에서 어떤 코드를 좀 더 적절하게 나누고 최적화하는 과정을 리팩터링이라고 합니다. 여기에서는 리모델링으로 표현했음에 주의하세요.

1. 문제점 분석

우선 기존 프로젝트에서 어떤 부분을 왜 고쳐야 하는지 생각해보죠. 기존 프로젝트에서는 각 포토 서비스를 IPhotoService라는 인터페이스만 상속받으면 되는 독립 클래스로 만들수 있었어요. 한 가지 문제는 이렇게 새 서비스를 만들어도 View와 ViewModel이 추가된 서비스를 알 수 없기 때문에 View와 ViewModel 모두를 고쳐야 한다는 점이죠. 이는 말하자면 Model의 독립성은 확보했지만 View와 ViewModel이 Model에 종속되어 있는 그림이 되죠. 특히 View는 ComboBox 컨트롤에 하드코딩으로 목록이 들어있는 형태라서 만약 디자이너 프로세스와 개발 프로세스가 나뉘어 있다면 Model이 추가될 때마다 디자이너가 View를 만져줘야 하는 상황이 발생하겠죠.

이를 해결하려면 View는 Model에 대해 전혀 알 필요가 없어야 하고 ViewModel은 Model의 추가/변경/삭제에 대해 코드의 수정 없이 대처할 수 있어야 해요.

2. View를 독립시키기

OOP의 기본 중에 기본이죠. 하나의 클래스는 그 이름으로 대표되는 최소한의 일만 수행하도록 쪼개고 또 쪼개는 것. 기존 프로젝트에서 콤보 박스에 들어가는 데이터를 생각해보면 각 서비스 제공자의 이름(Flickr, Live…)과 로고 이미지로 구성되어 있었죠? 그리고 이 부분이 새 Model이 추가될 때마다 함께 수정해야 할 부분이었고요. 이 부분을 분리해보죠.

image

포토 서비스 제공자의 이름과 로고를 처리하기 위해 IPhotoServiceProvider와 PhotoServiceProvider 클래스를 추가했고 내용은…

/// 
/// 기본적인 포토 서비스 제공자의 메타 정보를 관리하기 위한 클래스
/// 
public class PhotoServiceProvider : IPhotoServiceProvider
{
    /// 
    /// 서비스 제공자의 이름을 설정하거나 반환합니다.
    /// 
    public string Name { get; set; }

    /// 
    /// 화면에 표시할 이름을 설정하거나 반환합니다.
    /// 
    public string DisplayName { get; set; }

    /// 
    /// 서비스 제공자의 로고 이미지 소스를 설정하거나 반환합니다.
    /// 
    public string LogoSource { get; set; }
}

public class PhotoServiceProviderList : List< IPhotoServiceProvider>
{
}

간단하죠? 그리고 이 데이터는 하나가 아닌 여럿으로 구성될 것이므로 지네릭 타입의 List도 하나 선언해줬어요.

이제 View에서 데이터 템플릿을 사용하여 하드 코딩된 디자인을 걷어내보죠.

    
        
            
                
                
            
        
    

아직 데이터 바인딩의 소스를 설정하지 않았지만 DataTemplate을 사용해서 기존 디자인 형식을 그대로 가져왔고 대신 Image의 Source와 TextBlock의 Text 속성을 위에서 만든 IPhotoServiceProvider가 제공하는 속성으로 바인딩을 해줬어요. 데이터 바인딩의 소스는 나중에 추가하기로 하죠.

자 이제 View는 Model에 변경이 있더라도 영향을 받지 않도록 준비가 되었어요. 그러나 아직 데이터 소스를 어디에서 가져와서 언제 어떻게 설정할지는 미지수인데요, 이를 위해 드디어 Dependency Injection기법을 사용하여 몇 가지를 준비해보죠.

3. Dependency Injection 프레임워크 선택

DI는 개념이 그렇게까지 복잡한 것은 아니라 물론 직접 구현할 수도 있겠죠. 그러나 언제나 그렇지만 이런 표준적인 라이브러리는 다른 사람이 만든 훌륭한 구현체가 있기 마련이죠. 바로 제가 라이브러리 문서화의 중요성에서 언급한 Unity Application Block이나 Ninject처럼 말이죠. 이 중에서 좀 더 보편적이고 무엇보다 마이크로소프트가 운영하는 Pattern&Practice 프로젝트에서 만들어진 Unity를 사용할께요. 기회가 되면 Ninject도 사용해 볼 거에요.

imageimage

Unity Application Block for Silverlight를 다운로드 받으면 Microsoft.Practices.Unity.dll이란 어셈블리를 찾을 수 있는데요, 이걸 프로젝트에서 참조하면 돼요. 테스트 할 수 있도록 프로젝트의 DLL폴더에 어셈블리 파일을 포함시켜뒀으니 이걸 사용하셔도 되고요.

4. DI 설정 및 전역 서비스 제공 클래스 작성

DI는 외부에서 작성되어 애플리케이션 전역 혹은 일부에 영향을 주게 돼요. 저는 YouCard라는 짱멋진 프로젝트에 영향을 받아 DI 바인딩(또는 매핑) 정보를 저장하고 관리하는 ServiceConfiguration 클래스와 이 설정으로부터 애플리케이션이 사용할 인스턴스화된 오브젝트를 애플리케이션 전역에 제공할 ServiceLocator 클래스를 만들었어요.

먼저 ServiceConfiguration을 보면…

/// 
/// 서비스에서 사용할 인젝션 매핑을 설정하는 클래스
/// 
public class ServiceConfiguration : UnityContainer
{
    /// 
    /// 생성자.
    /// 여기에서 애플리케이션이 공통으로 사용할 컨테이너를 설정합니다.
    /// 
    public ServiceConfiguration()
    {
        // 디자인일 때와 그렇지 않을 때 서비스 바인딩을 달리 합니다.
        if (App.IsBlend)
        {
            RegisterType< IPhotoService, MockPhotoService>();
        }
        else
        {
            // 일반일 때에는 각 서비스의 바인딩을 이름으로 구분하여 등록합니다.
            RegisterType<  IPhotoService,  LivePhotoService>();    // 이름이 없을 때는 라이브를 사용
            RegisterType<  IPhotoService,  LivePhotoService>("Live");
            RegisterType<  IPhotoService,  FlickrPhotoService>("Flickr");
            RegisterType<  IPhotoService,  DaumPhotoService>("Daum");
            RegisterType<  IPhotoService,  NaverPhotoService>("Naver");
        }

        // 공통으로 사용할 바인딩
        // 서비스 제공자의 목록
        PhotoServiceProviderList providers = new PhotoServiceProviderList
        {
            new PhotoServiceProvider { Name = "Live", DisplayName = "Live", LogoSource = "/MashupImageSearch;component/Resource/WindowsLive2_20.png" },
            new PhotoServiceProvider { Name = "Flickr", DisplayName = "Flickr", LogoSource = "/MashupImageSearch;component/Resource/flickr2_20.png" },
            new PhotoServiceProvider { Name = "Daum", DisplayName = "Daum", LogoSource = "/MashupImageSearch;component/Resource/daum_20.png" },
            new PhotoServiceProvider { Name = "Naver", DisplayName = "Naver", LogoSource = "/MashupImageSearch;component/Resource/naver_20.png" },
        };
        // 인스턴스 자체를 등록
        RegisterInstance< PhotoServiceProviderList>(providers);
    }
}

가장 핵심적인 건 ServiceConfiguration 클래스가 UnityContainer를 상속받고 있다는 점이죠. UnityContainer는 바로 Unity에서 DI에 대한 매핑 정보를 저장하고 관리하는 기본 클래스에요. 기본적으로 RegisterType 메서드로 FromT 타입에 대해 ToT 타입으로 주입Injection하도록 매핑하고 또한 RegisterInstance 메서드로 FromT타입에 대해 등록한 인스턴스를 주입하도록 매핑하죠. 여기에서는 이 정도로만 설명하고 DI에 대한 건 기회가 되면 다른 글에서 소개할께요.

여기서 한 가지 센스있는 코드가 있는데요^^, 바로 실버라이트가 디자인 모드(블렌드)에서 동작중인지 아니면 정상적으로 실행되고 있는지에 따라 타입 매핑을 다르게 했어요. 이를 통해 디자이너가 블렌드에서도 여기에서 주어진 샘플 데이터를 볼 수 있죠.

이제 새로운 Model이 추가된다면 새 Model 클래스를 작성하고 ServiceConfiguration에 새로 만들어진 Model 타입을 매핑하고 새 Model을 설명하는 PhotoServiceProvider를 추가해주기만 하면 돼요. WOW~!

다음으로 ServiceLocator를 보죠.

/// 
/// 서비스에서 사용할 컨테이너를 초기화하고
/// 인젝션에 필요한 정보를 담고 있는 싱글턴 클래스
/// 
public class ServiceLocator
{
    #region Singlton 컨테이너
    private static ServiceConfiguration _container;
    /// 
    /// 클래스 초기화.
    /// 서비스에서 사용할 DI 컨테이너를 초기화합니다.
    /// 
    static ServiceLocator()
    {
        _container = new ServiceConfiguration();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 인젝션이 처리된 결과
    public static T Get< T>()
    {
        return _container.Resolve< T>();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 옵셔널 문자열
    /// 인젝션이 처리된 결과
    public static T Get< T>(string name)
    {
        return _container.Resolve< T>(name);
    }
    #endregion Singlton 컨테이너

    /// 
    /// 생성자.
    /// 
    public ServiceLocator()
    {
    }

    /// 
    /// 설정에 따라 이미지 검색 뷰모델의 인스턴스를 반환합니다.
    /// 
    public ImageSearchViewModel ImageSearchViewModel
    {
        get { return Get< ImageSearchViewModel >(); }
    }

    /// 
    /// 설정에 따라 포토 서비스 제공자의 목록을 반환합니다.
    /// 
    public PhotoServiceProviderList PhotoServiceProviders
    {
        get { return Get< PhotoServiceProviders>(); }
    }
}

ServiceLocator는 ServiceConfiguration에 대한 싱글턴 인스턴스를 하나 가지고 있으면서 애플리케이션에서 사용될 인젝션 대상 클래스의 인스턴스를 얻을 수(Get) 있도록 도와주죠. 그리고 바인딩의 편의를 위해 ImageSearchModelView와 PhotoServiceProviders 속성은 별도로 노출하고 있어요. 이 ServiceLocator는 말 그대로 애플리케이션 전역에서 접근할 수 있는 DI 컨테이너를 서비스하는 클래스이죠.

5. 애플리케이션 전역에 DI 컨테이너를 제공할 수 있도록 준비

애플리케이션 전역에서 ServiceLocator를 접근할 수 있도록 ServiceLocator의 인스턴스를 하나 만들어야죠. 여기에서 App.xaml의 진가가 나오는데요, 바로 App.xaml의 Resources에 ServiceLocator의 새 인스턴스를 하나 등록해두면 애플리케이션 전역에서 StaticResource로 접근할 수 있게 돼요. App.xaml을 보면…

    
        
    

View의 DataContext의 Source를 전역 리소스인 ServiceLocator로 지정했고 데이터의 경로Path는 ImageSearchViewModel로 지정했어요. 즉, ServiceLocator 클래스의 인스턴스에서 ImageSearchViewModel 속성을 읽어 오는거죠.

아주 간단하게 전역 리소스를 만들었죠? 다음으로 넘어가죠.

6. 전역 리소스에서 View에 데이터 바인딩

앞서 우리는 ServiceLocator를 전역에서 접근할 수 있도록 리소스로 만들었는데요, 이걸로 각 View의 ViewModel을 안전하게 접근하여 데이터 바인딩 할 수 있어요.

 

즉, ServiceLocator 클래스의 인스턴스에서 ImageSearchViewModel 속성으로 데이터 바인딩을 하는 거죠.

아직 하나 더 해줘야 할 게 있는데요, 앞서 DataTemplate만 설정하고 실제 데이터 소스를 설정하지 않은 ComboBox죠.

    
        
            
                
                
            
        
    

자, 이제 ViewModel을 독립시킬 차례에요.

7. ViewModel 리모델링

ViewModel은 View가 가질 데이터와 View를 표시하는 로직을 담고 Model로부터 데이터를 얻거나 넘겨주죠. 먼저 기존 ViewModel에서는 제공될 Model에 대한 정보를 이미 가지고 있었어요. 여러 개의 서비스를 사용할 수 있도록 각 서비스가 IPhotoService 인터페이스로 추상화가 되어있지만 결국 실체화된 인스턴스를 만들기 위해 Model의 Concrete 타입을 사용해요. 요컨대 ServiceTypes enum이 있었고 ComboBox에서 서비스를 변경하면 이 enum값에 따라 new LivePhotoService(); new FlickrPhotoService();와 같이 Model의 확정된 타입을 ‘생성’했었죠.

세부적인 코드는 첨부한 파일을 참고하시길 바라고 가장 핵심적으로 수정된 부분을 볼께요. 먼저 생성자 부분.

/// 
/// 기본 생성자
/// 
public ImageSearchViewModel(IPhotoService service)
{
    InitProperties(service);
    SearchCommands.Search.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(Search_Executed);
    SearchCommands.Navigate.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(Navigate_Executed);
    SearchCommands.ShowLargeImage.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(ShowLargeImage_Executed);
    SearchCommands.ChangeService.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(ChangeService_Executed);
    SearchCommands.NavigateOriginalImage.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(NavigateOriginalImage_Executed);
}

생성자에서는 파라미터로 IPhotoService를 받도록 했고 InitProperties로 전달하고 InitProperties에서는 클래스의 멤버 변수인 _photoService에 전달 받은 IPhotoService의 인스턴스를 ‘주입’하게 되죠. 그리고 만약 블렌드에서 동작중이라면 타이머를 돌려 검색을 시뮬레이션 하고 있고요.

다음으로 ComboBox에서 서비스를 변경했을 때 처리를 보죠.

void ChangeService_Executed(object sender, HugeFlow.CommandPattern.ExecutedEventArgs e)
{
    PhotoServiceProvider provider = e.Parameter as PhotoServiceProvider;
    _photoService = ServiceLocator.Get< IPhotoService>(provider.Name);   // 컨테이너에서 서비스의 참조 얻기

    if (_photoService != null)
    {
        _photoService.SearchImageCompleted += new PhotoSearchEventHandler(_photoService_SearchImageCompleted);
    }

    InitSearch();
}

전에는 각 서비스 모델 별로 직접 생성을 했지만 이제는 ServiceLocator로부터 주어진 PhotoServiceProvider의 이름으로 생성해야 할 타입을 ServiceLocator가 유추하고 생성하여 반환하도록 했어요. 이로써 ViewModel은 외부 Model의 어떤 Concrete타입에도 의존하지 않고 단지 Model들의 추상화된 인터페이스만으로도 완벽하게 동작할 수 있게 되었어요.

꽤 길어졌네요. DI란게 짧은 글 하나로 완벽하게 정리가 될 만한 내용도 아니고 언제 어떻게 적용해야 하는지에 대한 것도 경험이 필요한거죠. 저역시 이제 막 이런 패턴들을 시도하고 익히고 있는 중이에요. 뭔가 설명할 수 있을 만큼 익히면 다시 정리하여 포스팅하기로 할께요. 그리고 다음 포스팅은 똑같은 프로젝트를 Unity가 아닌 Ninject를 사용하여 구현해보도록 하죠.

저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo


티스토리 툴바