我不确定这是否适用于StackOverflow,或者更确切地说可能适用于Programmers @ StackExchange。如果这应该去那里,请在下面的评论中告诉我,我会移动它:)
无论如何 - 回到原点。我从未使用接口和Constructor / Property依赖注入等进行过多编程。所以我对它了解得太多了。我一直在阅读一些文章,主要是this,并发现这是一种有趣的技术,可以使我的软件更加灵活和可测试。
所以关闭我开始重构一个现有的应用程序(C#),我遇到了两难选择,下面两个选项之一更好:
选择1 - 函数中的最低依赖性要求。为构造函数留下一些注入(使用接口时的实现决策)
public interface IDriver
{
bool Start();
bool Stop();
bool Read(uint[] signal1, uint[] signal2);
}
public class MyDriver : IDriver
{
public MyDriver(ISettings settings)
{
//remember ISettings in a local var
}
//interface implementation
}
选择2 - 函数调用中所有必需的依赖项。
public interface IDriver
{
bool Start();
bool Stop();
bool Read(ISettings settings, uint[] signal1, uint[] signal2);
}
public class MyDriver : IDriver
{
//implementation of the interface
}
现在选择2可能是错的,对吧?因为某些实现可能实际上不需要ISettings工作。我目前实施IDriver使用ISettings的事实并不意味着它将在一年左右的时间内完成,因此合乎逻辑的方法是使用方法1.
所以我的问题是:我应该如何严格制作界面,以及如何在界面和实现之间混淆?我不希望实现影响我设计界面的方式 另外,有没有人知道有关该主题的好文章?
感谢。
答案 0 :(得分:2)
接口应该由使用接口的客户端定义和拥有。正如Agile Principles, Patterns, and Practices解释的那样,“clients [...]拥有抽象接口”(第11章)。因此,如果客户端需要这个(您的选项1):
public interface IDriver
{
bool Start();
bool Stop();
bool Read(uint[] signal1, uint[] signal2);
}
然后那应该是界面。其他所有内容都是实现细节,应该放在构造函数中。
答案 1 :(得分:0)
不仅仅是严格,还是合同必要性的问题。
您的阅读功能是否合同需要ISettings?可能没有。
认为它与signal1和signal2变量没有区别。你在接口的Read方法定义中有signal1和signal2的原因是因为它们是合同的一部分,并且对于用作输入的接口的每个实现都是必需的。
但ISettings听起来像某个特定实现需要的东西,而其他一些则不会。 (如Loggers,CacheManagers,Repositories等)
所以你是对的,而且往往更好,方法#1会更好。它保持界面清洁和仅限于确切的合同输入/输出。
答案 2 :(得分:0)
彻底研究系统需求可以解决许多问题,并帮助您更自信地设计应用程序。所以首先要多思考,直到你达到一个可以争论和推理你将要做的事情为止的点。
其次恕我直言,两种方法都可以。第一个作为' raja'指出是干净和有吸引力的,我不会再重复他说的话。但请考虑这种情况:如果稍后需要配置IDriver
实现者。然后通过某种设置解决了许多问题。即使在目前这一刻你认为它是不必要的(我承认这是YAGNI原则所说的),你可以提供空设置(NullObject pattern):
public Driver : IDriver
{
public bool Read(ISettings settings, uint[] signal1, uint[] signal2)
{
if (settings.PreventSomeThing)
{
.....
}
}
}
public NullSetting : ISetting
{
public bool PreventSomething = false;
....
}