确定。我对OO和库设计的某些方面有一些疑问。
图书馆应该自给自足吗?例如,它可以使用外部依赖注入框架,还是应该以更轻量级的方式实现它?
Liskov的替换原则如何适用于多态,你不知道方法或类的行为?你只是希望它能像它应该的那样工作吗?
在源代码部分,将接口保存在单独的文件夹(例如,/interfaces
)中是不是习惯了?
在接口上划分泛型类型(where T : type
)也不仅仅是在它们的实现中划定一个坏习惯? (这个我不这么认为,但只是确认一下)。
当对象关系为“可以做”和“是”时,接口是否优于抽象类,并且不需要方法等的默认实现?
就是这样。谢谢你的时间=)
答案 0 :(得分:3)
<强> 1。图书馆应该自给自足吗?
库肯定可以使用,例如,依赖注入框架。但是,是否要强制DI框架的用户使用DI框架是debatable。
<强> 2。 Liskov的替换原则如何适合多态?
Liskov的替换原则是关于能够将一个实现与另一个实现(替换)交换,而无论行为,当用户遵守合同时应该没有错误。例如,如果仅使用CanonPrinter
类中的方法,则交换EpsonPrinter
Printer
仍应允许您进行打印。调用Printer
但具有特定实现(Canon,Epson)的东西是多态的。
第3。将接口保持在与其实现分开的文件夹中是不是一个坏习惯?
您是否希望将接口与其实现区分开来是个人偏好。我不这样做;我甚至没有为每个实现提供接口。我认为只有为项目增加价值才有实现的接口才有意义。
<强> 4。限制接口上的泛型类型而不仅仅是在它们的实现中也是一个坏习惯吗?
如果您认为泛型类型应限于特定类型,那么您应该在有意义的地方执行此操作(因此,这可能在接口上)。例如,拥有IPrintableDocument<TPrinter>
而不限制TPrinter
到Printer
个对象不有意义,所以我会这样做。
<强> 5。当对象关系既“可以做”又“是”时,接口是否优于抽象类?
实际上,大多数人使用的抽象类是,的接口可以做关系。原因是:类只能从一个基类继承而是从多个接口继承。实质上,这意味着类只能一件事(Employee
是 Person
)但可以> em> multiple(可能是ICopyDocuments
,IWalkAbout
,IMakeCoffee
)。无论你做什么,当界面都取决于你的喜好。
您的大多数问题都与类(或接口)的合同有关:合同指定用户(另一个类,其他人的代码)可以做什么和不能与类及其成员一起,它指定用户可能期望将执行的类而将不会执行。< / p>
例如:用户 只能在对象与参数类型匹配时将对象作为参数传递给方法。类将只返回对象作为与返回类型匹配的返回值。用户只能调用类中定义的成员,而类将确保这些方法根据契约行为(即不抛出错误,没有副作用,永不返回空)。
通用类型约束也是合同的一部分。我甚至考虑合同的文档部分:当它声明方法永远不会抛出异常时,那么任何实现都不应该抛出异常。没有强制执行它的语法或编译器规则,但它是合同的一部分。合同的某些部分由编译器或运行时强制执行,而其他部分则不是。
当一个类或接口有一个特定的契约时,任何子类或实现都可以代替它,当该子类或实现遵守契约时它仍然可以工作。如果它遵守合同,那就是利斯科夫的替代原则(LSP)。很容易偏离它,许多程序员都这样做。有时你别无选择。
在.NET Framework中违反LSP的一个明显示例是ReadOnlyCollection<T>
class。它实现了IList<T>
接口,但是它的许多方法都没有实际的实现。因此,如果您传递的用户期望IList<T>
ReadOnlyCollection<T>
并且用户尝试调用list.Add
,则会引发异常。因此,您无法始终将IList<T>
替换为ReadOnlyCollection<T>
,因此会违反LSP。
答案 1 :(得分:1)
我会尝试按顺序回答这些问题:
这取决于。承担外部依赖可能会限制库的有用性和范围,因为它只能在可用的依赖项中使用。然而,这可能没什么问题,并且优先尝试解决依赖性问题。如果您知道您的库的用户将始终使用特定的DI框架,我会围绕它构建库,而不是实现需要“适应”工作的东西。
“你不知道方法或类的行为?”您应该知道您正在使用的任何类型的合同。 LSP基本上说,无论你得到什么具体的类型,基类/接口契约总是有效的。这意味着您只需“使用类型”并期望它可以正常工作。
否 - 但这是私有的实施细节。但是,分离名称空间通常是一个坏主意,并且保持源代码树与名称空间匹配通常很好。
没有。如果某个接口真的只能用于特定类型,那么它应该具有接口的约束力。
一般而言,单一合约只应提供关系的一个方面。如果合同暗示“is-a”和“can-do”,它可能应该分成两个合同,也许是一个实现“can-do”接口的抽象类。
答案 2 :(得分:0)
我将在5点刺伤。
抽象类和接口之间的唯一区别是抽象类可以有类变量。如果抽象类只由viratual方法组成,我会选择一个接口。
答案 3 :(得分:0)
在#1 - http://jeviathon.com/2012/03/05/roll-your-own-syndrome/
基本上,关键是,当有人可能已经创建了强大的,经过测试和支持的软件时,为什么会“推出自己的”xyz框架。
花时间研究选择使用“正确”的外部库和框架,而不是重新发明轮子。
答案 4 :(得分:0)
“这取决于”。但重新发明自己的车轮几乎没有价值,特别是在人口密集(并且难以实施)的领域,如DI和ORM。特别是当您可以(a)bin-deploy您的依赖项或(b)将您的依赖项指定为nuget包时,如果您的库是nuget包(当然应该是)
LSP与多态性关系不大。它声明类(或方法)应该按照契约行事,事实上,你唯一知道的就是实现者的行为。
文件夹应与名称空间相对应。你把接口放在单独的命名空间中吗?我没有,我认为没有人应该这样做。当我只有一个实现接口(例如,单元测试引发)时,我更愿意将其实现保存在一个文件中。
不,不是。为什么会这样?
如果不需要默认实现,请获取接口。接口更易于模拟,也更容易实现,因为它不会破坏您的实现的继承。事实上,你甚至可以在一个类上实现几个接口,如果它以某种方式突出你的意图(例如,如果你有特定的服务处理安全性,它实现IAuthentication
和{{1的情况并不罕见}})。最近,只有在明确需要时才选择抽象类。