2 つのクラス ライブラリを含むプロジェクトがあります。アプリケーションパラメーターを使用して、プログラムでそれらを切り替える必要があります。
if(arg == "a")
using LibraryA;
if(arg == "b")
using LibraryB;
namespace Project
{
public class MyClass
{
// my code here
}
}
2 つのクラス ライブラリを含むプロジェクトがあります。アプリケーションパラメーターを使用して、プログラムでそれらを切り替える必要があります。
if(arg == "a")
using LibraryA;
if(arg == "b")
using LibraryB;
namespace Project
{
public class MyClass
{
// my code here
}
}
疎結合アプリケーションを構築したい場合は、依存性注入パターンの詳細を読むことをお勧めします。これは、そのようなデザインを構築する方法を説明した素晴らしい記事です。(最初の 4 レッスン)
これは非常に複雑な要件であり、これを乗り越えるためには複数のパターンとプラクティスをまとめる必要があります。関連する原則へのリンクを試みながら進んでいきます。
最初に取り組むべき問題は、共通のインターフェイスを持つように 2 つのクラス ライブラリを調整することです。これは、説明したようにそれらを交換可能にするために必要です。通常、これは両方のオブジェクトが実装するインターフェイスを作成するのと同じくらい簡単ですが、ライブラリの1つしか制御できないと述べました。その場合、アダプターパターンを利用して、制御できないライブラリを強制的に共通インターフェイスを実装する必要があります。
現在、これら 2 つのクラスがLibrary1.dll
とにあるとしますLibrary2.dll
(Library1.dll は、制御できるクラスです)...
// in Library1.dll
public class Foo
{
public int DoSomething() { ... }
}
// In Library2.dll
public class Foo
{
public int DoSomething() { ... }
}
まず、共通インターフェースを定義する必要があります。これはコア/共有ライブラリに存在する必要があります...
// In Shared.dll
public interface IFoo
{
int DoSomething();
}
これで、ライブラリ 1 を制御できるようになったので、通常の方法で共通インターフェイスを簡単に実装できます...
// 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();
}
}
次に、オブジェクトを直接使用するのではなく、共通のインターフェイスを使用するようにすべてのクライアント コードを変更する必要があります。以前はどこでこのようなことがあったでしょうか...
if(arg == "a")
using LibraryA;
if(arg == "b")
using LibraryB;
namespace Project
{
public class MyClass
{
public void Bar()
{
var foo = new Foo();
foo.DoSomething();
}
}
}
これで、コードはインターフェイスのみを使用する必要があります...
namespace Project
{
public class MyClass
{
public void Bar(IFoo foo)
{
foo.DoSomething();
}
}
}
ここで、新しい問題が発生しました。使用する のバージョンをどのように判断すればよいのIFoo
でしょうか? かLibrary1.Foo
、それともShared.Foo2Wrapper
?
依存性注入を使用して、この問題を解決できます。コントロール コンテナーの反転はオブジェクトを提供し、特定の条件に基づいてさまざまな種類のオブジェクトを提供するように構成できます。これは、 StructureMap (私の個人的なお気に入りの IoC コンテナー)で使用されるものと同様の構文を使用した疑似コードの例です...
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>();
GetInstance<IFoo>()
IoC コンテナーを呼び出すと、コマンド ラインでの構成方法に応じて、Library1.Foo
または aが返されます。Shared.Foo2Wrapper
ここで、クライアント コード内の以前にあったすべての場所を調べてnew Foo()
、 に置き換える必要がありcontainer.GetInstance<IFoo>()
ます。
それがあなたを動かしてくれることを願っています:)
以下は、目的を達成する方法の例です。
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 . 既存のオブジェクトのメソッドを乗っ取って、ダミーのメソッドに置き換えることができます。私はまだこれで遊んでいませんが、有望に見えます。