我有一个包含两个类库的项目。我需要使用应用程序参数以编程方式在它们之间切换,例如
if(arg == "a")
using LibraryA;
if(arg == "b")
using LibraryB;
namespace Project
{
public class MyClass
{
// my code here
}
}
我有一个包含两个类库的项目。我需要使用应用程序参数以编程方式在它们之间切换,例如
if(arg == "a")
using LibraryA;
if(arg == "b")
using LibraryB;
namespace Project
{
public class MyClass
{
// my code here
}
}
If you want to build loose-couple application, i suggest you read more about Dependancy Injection pattern. This is a nice article, desscribed how to build such design. (First 4 lessons)
这是一个相当复杂的要求,您必须将多种模式和实践结合起来才能通过它。随着我的进展,我将尝试链接到相关的原则。
要解决的第一个问题是对齐两个类库,以便它们具有公共接口。这是必要的,以使它们如您所描述的那样可互换。通常这就像创建两个对象都实现的接口一样简单 - 但您提到您只能控制其中一个库。在这种情况下,您需要利用适配器模式来强制您无法控制的库来实现您的通用接口。
Library1.dll
假设我们目前在and中有这两个类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();
}
现在因为我们可以控制库一,我们可以很容易地让它以通常的方式实现公共接口......
// 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 容器时,将根据命令行的配置方式返回 aLibrary1.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 。它允许您劫持现有对象的方法并用虚拟方法替换它们。我还没有玩过这个,但它看起来很有希望。