我有关于松散耦合和接口的概念/理论问题。
因此,使用接口的一种方法可能是封装某个构造函数所需的参数:
class Foo
{
public Foo(IFooInterface foo)
{
// do stuff that depends on the members of IFooInterface
}
}
因此,只要传入的对象实现了契约,一切都会起作用。根据我的理解,这里的主要好处是它可以实现多态性,但我不确定这是否与松散耦合有关。
让我们说为了论证,IFooInterface如下:
interface IFooInterface
{
string FooString { get; set; }
int FooInt { get; set; }
void DoFoo(string _str);
}
从松耦合的角度来看,不要在上面的构造函数中使用IFooInterface,而不是像这样设置Foo:
class Foo
{
public Foo(string _fooString, int _fooInt, Action<string> _doFoo)
{
// do the same operations
}
}
因为我想把Foo的功能放到另一个项目中。这意味着其他项目也必须引用IFooInterface,添加另一个依赖项。但是这样我就可以将Foo放到另一个项目中,它可以准确地表达它所需要的工作。显然我可以使用重载的构造函数,但是为了参数起见,我不想和/或不能修改Foo的构造函数。
最显着的缺点(至少对我而言)是,如果你有一个带有一堆原始参数的方法,它会变得丑陋且难以阅读。所以我有了创建一种包装函数的想法,它允许你仍然传入一个接口而不是所有原始类型:
public static Func<T, R> Wrap<T, R>(Func<T, object[]> conversion)
{
return new Func<T, R>(t =>
{
object[] parameters = conversion(t);
Type[] args = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
args[i] = parameters[i].GetType();
}
ConstructorInfo info = typeof(R).GetConstructor(args);
return (R)info.Invoke(parameters);
});
}
这里的想法是我可以找回一个函数,该函数接受符合Foo要求的某个接口的实例,但是Foo实际上并不知道该接口的任何信息。它可以像这样使用:
public Foo MakeFoo(IFooInterface foo)
{
return Wrap<IFooInterface, Foo>(f =>
new object[] { f.FooString, f.FooInt, f.DoFoo })(foo);
}
我听说有关接口如何启用松散耦合的讨论,但对此感到疑惑。
想知道一些有经验的程序员的想法。
答案 0 :(得分:3)
在你的初始例子中,你非常接近Parameter Object模式,尽管在这里使用一个简单的类(通常带有自动属性)更常见,而没有额外的接口抽象。
通常,当您听说将接口传递给构造函数时,它不是替换基元,而是作为依赖注入的一种形式。不是直接依赖MyFooRepository
,而是依赖于IFooRepository
,这将取消与特定实现的耦合。
答案 1 :(得分:2)
我的第一个想法是,您没有分别为Action<string>
和Action<int>
的设置者提供FooString
和FooInt
。 IFooInterface
的实现可能有关于这些setter的规则,并且可能需要访问界面上未公开的其他实现细节。
同样,您也应该接受Func<string>
和Func<int>
:IFooInterface
的实施可能有关于FooString
和FooInt
的规则随着时间的推移。例如,DoFoo
可能会重新计算这些值;你不能认为它们只是传递给永不改变的领域。
更进一步,如果getter,setter或DoFoo
需要访问公共状态,则在创建它们时,函数和操作将需要关闭同一组变量。在那时,你将做一些心理体操,以理解变量的生命周期和代表之间的关系。
状态和行为的配对正是类所表达的,实现细节的隐藏正是接口提供的。将这些概念分解为其组成元素当然是可以实现的,但它也打破了通过将成员分组为类型而获得的一致性。
换句话说,你可以给我面条,酱汁,蔬菜和汉堡包,但这不是意大利面和肉丸: - )