请提前原谅我的伪代码!
阅读 SOLID 原则。使用接口的 SOLID 原则有几个原因。接口允许您解耦对实现的依赖。您可以更进一步,使用 StructureMap 之类的工具,真正让耦合消失。
你可能习惯的地方
Widget widget1 = new Widget;
这明确表示您要创建一个新的 Widget 实例。但是,如果您在另一个对象的方法内部执行此操作,您现在是在说另一个对象直接依赖于 Widget 的使用。所以我们可以说类似
public class AnotherObject
{
public void SomeMethod(Widget widget1)
{
//..do something with widget1
}
}
我们在这里仍然依赖于 Widget 的使用。但至少这更可测试,因为我们可以将 Widget 的实现注入到 SomeMethod 中。现在,如果我们改用接口,我们可以进一步解耦。
public class AnotherObject
{
public void SomeMethod(IWidget widget1)
{
//..do something with widget1
}
}
请注意,我们现在不需要 Widget 的特定实现,而是要求任何符合 IWidget 接口的东西。这意味着可以注入任何东西,这意味着在日常使用代码时,我们可以注入 Widget 的实际实现。但这也意味着当我们想要测试这段代码时,我们可以注入一个 fake/mock/stub(取决于您对这些术语的理解)并测试我们的代码。
但是我们怎样才能更进一步。通过使用 StructureMap,我们可以进一步解耦这段代码。在最后一个代码示例中,我们的调用代码看起来像这样
public class AnotherObject
{
public void SomeMethod(IWidget widget1)
{
//..do something with widget1
}
}
public class CallingObject
{
public void AnotherMethod()
{
IWidget widget1 = new Widget();
new AnotherObject().SomeMethod(widget1);
}
}
正如您在上面的代码中看到的,我们通过传入一个符合 IWidget 的对象来删除 SomeMethod 中的依赖项。但是在 CallingObject().AnotherMethod 中我们仍然有依赖。我们也可以使用 StructureMap 来移除这个依赖!
[PluginFamily("Default")]
public interface IAnotherObject
{
...
}
[PluginFamily("Default")]
public interface ICallingObject
{
...
}
[Pluggable("Default")]
public class AnotherObject : IAnotherObject
{
private IWidget _widget;
public AnotherObject(IWidget widget)
{
_widget = widget;
}
public void SomeMethod()
{
//..do something with _widget
}
}
[Pluggable("Default")]
public class CallingObject : ICallingObject
{
public void AnotherMethod()
{
ObjectFactory.GetInstance<IAnotherObject>().SomeMethod();
}
}
请注意,在上面的代码中,我们没有实例化另一个对象的实际实现。因为所有东西都是为 StructurMap 连接的,所以我们可以允许 StructureMap 根据代码运行的时间和地点传入适当的实现。现在代码非常灵活,我们可以通过配置或在测试中以编程方式指定我们想要使用哪个实现。此配置可以即时完成或作为构建过程的一部分等。但它不必在任何地方硬连线。