MEF(Managed Extensibility Framework)




내용


.NET 에는 MEF 라는 것이 있습니다. 동적으로 손쉽게 객체를 로드할때 쓰곤 합니다. 예를들어 어떤 프로그램을 만들었습니다. 그리고 사용자가 plugin 을 만들 수 있도록 제공하고자 합니다. C++ 이나 java 같은 다른 언어에서는 이를 위해서 인터페이스를 제공하고, 빌드된 파일을 동적으로 읽어와서 일일이 체크해 주어야 했습니다. 그리고나서 호출하곤 했지요.

이런 복잡한 부분을 손쉽게 .NET 에서는 제공해 줍니다.




기존방식


동물원(zoo) 클래스를 만들고자 합니다. 이 녀석은 동물(Animal)들을 가지고 있습니다. 아래처럼 말이죠.





    [Zoo.cs]



public class Zoo
{
    IEnumerable<IAnimal> Animals { get; set; }



    public void MakeSound()
    {
        foreach(IAnimal animal in this.Animals)
        {
            animal.MakeSound();
        }
    }
}


이제 IAnimal 을 살펴보지요.



    [IAnimal.cs]



interface IAnimal
{
    void MakeSound();
}


Main 함수에서는 동물원(zoo)객체를 생성하고, 동물들의 소리를 들어볼 것입니다.


    [Main]



Zoo zoo = new Zoo();
zoo.MakeSound();


동물은 코끼리(Elephant), 호랑이(Tiger), 개(Dog) 가 있습니다.

각각은 아래처럼 생겼습니다.



    [개]



public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("멍멍");
    }
}


    [코끼리]



public class Elephant : IAnimal
{
    public void MakeSound() {
        Console.WriteLine("뿌우~");
    }
}


    [호랑이]



public class Tiger : IAnimal
{
    public void MakeSound() {
        Console.WriteLine("어흥~");
    }
}


자, 이제 어떻게 해야 될까요? 아래처럼 생성자에서 일일이 IAnimal 을 찾아서 생성 및 등록을 해 주어야 할까요?


    [좋지 않은 방식]


public Zoo()
        {
            List<IAnimal> animals = new List<IAnimal>();
            animals.Add(new Elephant());
            animals.Add(new Tiger());
            animals.Add(new Dog());

            this.Animals = animals;
        }



이 방법에는 조금 문제가 있네요. IAnimal 이 늘어나면, 매번 이곳 Zoo 클래스의 생성자 부분을 수정해 주어야 합니다. 그리고 사용자에게 plug-in 형태로 제공할 수도 없고요. 아니면,, zoo 객체를 생성하는 쪽에서, 객체 생성 후에 일일이 IAnimal 을 찾아서 넣어 줄 수도 있겠지만, 위 방식과 큰 차이가 없어 보입니다.



MEF 사용하기


이런 경우에 편리하게 쓸 수 있는 것이 바로 MEF(Managed Extensibility Framework)입니다. 기존 Zoo 클래스와 Animal 클래스에 코드를 추가해 봅시다.



    [Zoo 클래스]



public class Zoo
    {
        [ImportMany(typeof(IAnimal))]
        IEnumerable<IAnimal> Animals { get; set; }

// 생략...


    [Elephant 클래스]


[Export(typeof(IAnimal))]
public class Elephant : IAnimal
{
// 생략...




    [Tiger 클래스]



[Export(typeof(IAnimal))]
public class Tiger : IAnimal
{
// 생략...


    [Dog 클래스]



[Export(typeof(IAnimal))]
public class Dog : IAnimal
{
// 생략...


이렇게 외부에 노출할 클래스는 Export 를, 그리고 가져다 쓰는 쪽에서는 Import 어트리뷰트를 사용하면 됩니다. 이렇게 준비를 하고, 실제 load 하는 코드를 작성해 주어야 합니다.


참고로 단일 객체를 불러오려면 Import 를 쓰고, List 처럼 여러개를 불러오려면 ImportMany 를 써 줍니다. 위 샘플에서는 동물들을 가져올 것이므로 ImportMany 를 써 주었습니다. Export 와 Import 의 기준은 파라미터로 전달한 type 이 됩니다.



이제 CompositionContainer 라는 녀석을 통해서 실제로 객체를 로드해야 합니다. Import 하는 대상은 '디렉토리 안에 존재하는 dll', '어셈블리' 입니다. plugin 이라면 아무래도 디렉토리 기준으로 가져올 것이며, 개발시 편의를 위해서라면 어셈블리 기준으로 가져오게 되겠죠. 물론 두 방법 모두를 이용할 수도 있습니다. 코드를 보시는게 이해하는데 도움이 될 듯 합니다.



    [Main]



Zoo zoo = new Zoo();
            
// 만약 해당 directory 가 없다면, 오류가 발생합니다. 생성전에 유효한 directory 인지 확인 필.
// 디렉토리에서 검색
DirectoryCatalog dirCatalog = new DirectoryCatalog("plugins", "*.dll");

// 어셈블리에서 검색
AssemblyCatalog asmCatalog1 = new AssemblyCatalog(Assembly.GetExecutingAssembly());
AssemblyCatalog asmCatalog2 = new AssemblyCatalog(Assembly.GetAssembly(typeof(MainWindow)));

// AggregateCatalog 에 추가해 줌
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(asmCatalog1);
catalog.Catalogs.Add(asmCatalog2);

CompositionContainer container = new CompositionContainer(catalog); // 검색 대상 정도로 생각하시면 됩니다.
container.ComposeParts(zoo);    // zoo 객체에 Import Attribute 를 동작시킴

// 확인용 메소드 호출
zoo.MakeSound();



위 코드처럼 CompositionContainer 를 가지고 직접 실행해도 되지만, 래퍼를 하나 만들어서 쓰는것이 더 편할듯 합니다.





'Microsoft > WPF' 카테고리의 다른 글

Drag Canvas 만들기  (0) 2014.11.13
TreeView Template 을 통한 Canvas 만들기  (1) 2014.11.12
화면에 노출되는 프로퍼티는?  (1) 2014.05.13
TypeConverter  (0) 2014.05.12
XAML 의 문법구조  (0) 2014.05.12