1.图书馆应该自给自足吗?
库当然可以使用,例如,依赖注入框架。但是,是否要将 DI 框架强加于库的用户是有争议的。
2. Liskov 的替换原则如何适用于多态性?
Liskov 的替换原则是关于能够将一种实现与另一种实现互换(替换),并且无论行为如何,用户遵守合同时都不应出现错误。例如,如果您只使用类中的方法,将 a 换成CanonPrinter
anEpsonPrinter
仍然应该允许您打印Printer
。调用某些东西Printer
但有一个特定的实现(佳能,爱普生)是多态性。
3. 将接口与实现分开放在一个文件夹中是一个坏习惯吗?
您是否希望将接口与其实现分开,这是个人偏好。我不这样做;我什至没有每个实现的接口。我认为只有为您的项目增加价值的实现接口才有意义。
4. 在接口上限制泛型类型而不仅仅是在它们的实现中也是一个坏习惯吗?
如果您认为泛型类型应该限制为特定类型,那么您应该在有意义的地方这样做(因此,这可能在接口上)。例如,拥有 aIPrintableDocument<TPrinter>
并且不限TPrinter
于Printer
对象是没有意义的,所以我会这样做。
5. 当对象关系既是“can do”又是“is a”时,接口是否优于抽象类?
确实,大多数人使用抽象类来表示可做关系,而接口则表示可以做关系。原因:一个类只能从一个基类继承,但可以从多个接口继承。从本质上讲,这意味着一个类只能是一件事(aEmployee
是 a Person
),但可以做多个(它可能ICopyDocuments
、、、IWalkAbout
)IMakeCoffee
。当一个界面同时存在时,无论您做什么都取决于您的偏好。
您的大多数问题都与类(或接口)的契约有关:契约指定用户(另一个类,其他人的代码)可以对类及其成员做什么和不能做什么,并且它指定用户做什么可能期望类会做和不会做。
例如:用户只有在匹配参数类型时才能将对象作为参数传递给方法。该类只会将对象作为与返回类型匹配的返回值返回。用户只能调用类中定义的成员,并且类将确保这些方法按照约定行事(即不抛出错误、无副作用、永不返回 null)。
泛型类型约束也是契约的一部分。我什至考虑合同的文档部分:当它声明一个方法永远不会抛出异常时,那么任何实现都不应该抛出异常。没有强制执行它的语法或编译器规则,但它是合同的一部分。合同的某些部分由编译器或运行时强制执行,而其他部分则不是。
当一个类或接口有一个特定的契约时,任何子类或实现都可以代替它,并且当该子类或实现遵守契约时它仍然可以工作。如果它遵守合同,那就是 Liskov 的替代原则 (LSP)。并且很容易偏离它,许多程序员都会这样做。有时你别无选择。
.NET Framework 中违反 LSP 的一个明显示例是ReadOnlyCollection<T>
类。它实现了IList<T>
接口,但它的许多方法没有实际实现。因此,如果您传递一个期望 aIList<T>
的ReadOnlyCollection<T>
用户并且该用户尝试调用list.Add
,那么将引发异常。因此,您不能总是替换IList<T>
,ReadOnlyCollection<T>
因此它违反了 LSP。