1

두 개의 클래스 라이브러리가있는 프로젝트가 있습니다. 나는 난 당신이 의존성 주입 패턴에 대한 자세한 내용을 제안, 당신이 느슨한 커플 응용 프로그램을 빌드하려면프로그램 매개 변수와 함께 사용하는 C# 스위치 라이브러리

if(arg == "a") 
    using LibraryA; 
if(arg == "b") 
    using LibraryB; 

namespace Project 
{ 
    public class MyClass 
    { 
    // my code here 
    } 
} 
+3

왜이 기능이 필요합니까? 왜 둘 다 참조하지 않습니까? –

+0

매우 까다 롭습니다. 나중에 두 개의 라이브러리를 참조하십시오. 나중에 인수를 기준으로 사용합니다. – andy

+2

인터페이스가 아닌 코드의 구체적인 유형을 언급하는 경우 컴파일시 특정 유형 또는 변수가 고정됩니다. 이 라이브러리들 사이에 공통점이 있습니까 (아마도 네임 스페이스와 유형 이름 제외)? –

답변

1

같은 응용 프로그램 매개 변수로, 프로그래밍 방식으로 그들 사이에 뭔가를 전환 할 필요가있다. 이 디자인을 구현하는 방법을 desscribed nice article입니다. (처음 4 레슨)

0

이것은 매우 복잡한 요구 사항이며 이것을 해결하기 위해 여러 가지 패턴 &을 모아야합니다. 내가 따라 가면서 관련 원칙을 시도해 보도록하겠습니다.

첫 번째 문제는 두 클래스 라이브러리가 공통 인터페이스를 갖도록 정렬하는 것입니다. 이는 설명대로 상호 교환 할 수 있도록하기 위해 필요합니다. 일반적으로 두 객체가 구현하는 인터페이스를 만드는 것만 큼 간단 할 것입니다. 그러나 라이브러리 중 하나를 제어 할 수 있다고 언급했습니다. 이 경우 adapter pattern을 사용하여 공용 인터페이스를 구현할 수없는 라이브러리를 강제 변환해야합니다.

// in Library1.dll 
public class Foo 
{ 
    public int DoSomething() { ... } 
} 

// In Library2.dll 
public class Foo 
{ 
    public int DoSomething() { ... } 
} 

먼저 우리는 우리의 공통 인터페이스를 정의 할 필요가 ... 우리가 현재 Library1.dllLibrary2.dll (Library1.dll 우리가 제어 할 수있는 하나)이 두 클래스를 말해봐. 이것은 ... 우리가 도서관 하나를 제어 할 수 있기 때문에, 우리는 쉽게이 일반적인 방법으로 공통의 인터페이스를 구현 할 수 있습니다 ... 지금 핵심/공유 라이브러리에

// In Shared.dll 
public interface IFoo 
{ 
    int DoSomething(); 
} 

을 있어야합니다

// In Library1.dll 
public class Foo : IFoo 
{ 
    public int DoSomething() { ... } 
} 

Library2.dll을 제어 할 수 없기 때문에 어댑터 클래스를 만들어야합니다. 이 클래스의 목적은 공통 인터페이스를 구현하는 것이며 모든 동작은 Library2.Foo 실제로 위임됩니다. 결과적으로 이것은 Library2.Foo 객체가 우리의 공용 인터페이스를 구현하도록합니다.

// In Shared.dll 
public class Foo2Adapter : IFoo() 
{ 
    private Library2.Foo _realFoo; 

    public Foo2Adapter() 
    { 
     _realFoo= new Library2.Foo(); 
    } 

    public int DoSomething() 
    { 
     _realFoo.DoSomething(); 
    } 
} 

이제 개체 대신 직접 공용 인터페이스를 사용하도록 모든 클라이언트 코드를 수정해야합니다. 당신이

namespace Project 
{ 
    public class MyClass 
    { 
     public void Bar(IFoo foo) 
     { 
      foo.DoSomething(); 
     } 
    } 
} 

이제 우리는 새로운 문제가 ...
if(arg == "a") 
    using LibraryA; 
if(arg == "b") 
    using LibraryB; 

namespace Project 
{ 
    public class MyClass 
    { 
     public void Bar() 
     { 
      var foo = new Foo(); 

      foo.DoSomething(); 
     } 
    } 
} 

지금 코드는 인터페이스를 사용한다 ... 이런 일이 있었다 수도 이전에 어디에서, 어떻게 우리가 버전을 알 수 있습니까 사용하는 IFoo? Library1.Foo 또는 Shared.Foo2Wrapper입니까?

dependency injection을 사용하면이 문제를 해결할 수 있습니다. 컨트롤 컨테이너를 반전하면 개체가 제공되며 특정 조건에 따라 여러 종류의 개체를 제공하도록 개체를 구성 할 수 있습니다. 다음은 StructureMap (내 개인적으로 좋아하는 IoC 컨테이너)에서 사용되는 것과 유사한 sytax를 사용하는 psuedocode 예제입니다.

var container = new IocContainer(); 

if (arg == "a") 
    container.For<IFoo>().Use<Library1.Foo>(); 

else if (arg == "b") 
    container.For<IFoo>().Use<Shared.Foo2Adapter>(); 

var foo = container.GetInstance<IFoo>(); 

이제 우리는 IoC 컨테이너가 다시 우리를 줄 것이다 GetInstance<IFoo>()를 호출 할 때 중 하나 Library1.Foo 또는 명령 행에 의해 구성 방식에 따라 Shared.Foo2Wrapper. 이제 클라이언트 코드에서 이전에 new Foo()이있는 모든 위치를 거쳐 container.GetInstance<IFoo>()으로 바꿔야합니다.

나는 당신을 움직일 수 있기를 바랍니다. :)

-1

다음은 당신이 원하는 것을 성취 할 수있는 방법의 예입니다. 모의 객체에

using System; 
namespace StackOverflowDemo.Applications.TestFrameworkDemo.Data 
{ 

    public interface IDataSource 
    { 
     string GetTitle(int id); 
    } 

    public class Database: IDataSource 
    { 
     public string GetTitle(int id) 
     { 
      string result; 
      //logic to connect to a database and retrieve a value would go here 
      switch (id) 
      { 
       case 1: result = "DB First Title"; break; 
       case 2: result = "DB Second Title"; break; 
       default: throw new KeyNotFoundException(string.Format("ID '{0}' not found",id)); 
      } 
      return result; 
     } 

    } 
} 

using System; 
using StackOverflowDemo.Applications.TestFrameworkDemo.Data; 
namespace StackOverflowDemo.Applications.TestFrameworkDemo.DataTest 
{ 

    public class DatabaseMock : IDataSource 
    { 
     public string GetTitle(int id) 
     { 
      string result; 
      switch (id) 
      { 
       case 1: result = "DBMock First Title"; break; 
       case 2: result = "DBMock Second Title"; break; 
       default: throw new KeyNotFoundException(string.Format("ID '{0}' not found", id)); 
      } 
      return result; 
     } 

    } 
} 


using System; 
using StackOverflowDemo.Applications.TestFrameworkDemo.Data; 
namespace StackOverflowDemo.Applications.TestFrameworkDemo.Logic 
{ 
    public class SomeBusinessObject 
    { 
     private IDataSource myData; 
     public SomeBusinessObject(IDataSource myData) 
     { 
      this.myData = myData; 
     } 
     public void OutputTitle(int id) 
     { 
      Console.WriteLine(myData.GetTitle(id)); 
     } 
    } 
} 

using System; 
using StackOverflowDemo.Applications.TestFrameworkDemo.Data; 
//using StackOverflowDemo.Applications.TestFrameworkDemo.DataTest; //we don't need the using statement if we use the whole path below, which I think relates to your question 
using StackOverflowDemo.Applications.TestFrameworkDemo.Logic; 
namespace StackOverflowDemo.Applications.TestFrameworkDemo 
{ 
    class Program 
    { 
     public static void Main(string[] args) 
     { 
      IDataSource myData; 
#if(DEBUG) 
      myData = new StackOverflowDemo.Applications.TestFrameworkDemo.DataTest.DatabaseMock(); 
#else 
      myData = new Database(); 
#endif 
      SomeBusinessObject sbo = new SomeBusinessObject(myData); 
      sbo.OutputTitle(1); 
      Console.WriteLine("Done"); 
      Console.ReadKey(); 
     } 
    } 
} 

더 많은 정보는 여기에 있습니다 : http://msdn.microsoft.com/en-us/library/ff650441.aspx

는 Channel9에서 또한 물건의 부하있다 :

http://channel9.msdn.com/search?term=test+driven+development은 또는 당신이에 관심이있을 수 : http://msdn.microsoft.com/en-us/library/hh549175(v=vs.110).aspx. 기존 객체의 메소드를 납치하고 더미 메소드로 대체 할 수 있습니다. 나는 아직 이걸 가지고 놀지 못했지만 유망 해 보인다.

관련 문제