我有一个包含两个类库的项目。 我需要以编程方式在应用程序参数之间切换它们,例如
if(arg == "a")
using LibraryA;
if(arg == "b")
using LibraryB;
namespace Project
{
public class MyClass
{
// my code here
}
}
答案 0 :(得分:1)
如果您想构建松散耦合应用程序,我建议您阅读有关依赖性注入模式的更多信息。 这是一个很好的article,描述了如何构建这样的设计。 (前4课)
答案 1 :(得分:0)
这是一项非常复杂的要求,您必须将多种模式和模式结合在一起。练习来解决这个问题。我会在尝试时将其与相关原则联系起来。
要解决的第一个问题是调整两个类库,使它们具有通用接口。这是必要的,以使它们可以像您描述的那样互换。通常这就像创建两个对象实现的接口一样简单 - 但是你提到你只能控制其中一个库。在这种情况下,您需要利用adapter pattern来强制您无法控制的库来实现您的公共接口。
假设我们目前在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
?
您可以使用dependency injection来解决此问题。控件容器的反转将为您提供对象,您可以将其配置为根据特定条件提供不同类型的对象。这是一个使用类似于StructureMap(我个人最喜欢的IoC容器)使用的sytax的伪代码示例...
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
或Shared.Foo2Wrapper
,具体取决于命令行的配置方式。我们现在需要浏览我们以前拥有new Foo()
的客户端代码中的所有位置,并将其替换为container.GetInstance<IFoo>()
。
我希望能让你感动:)
答案 2 :(得分:-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。它允许您劫持现有对象的方法并用虚拟方法替换它们。我还没玩过这个,但看起来很有希望。