Oop 依赖抽象有什么明显的缺点吗?
在阅读了关于稳定抽象原则(SAP)的维基之后,我想知道是否有人知道依赖抽象而非具体的缺点(我想,这超过了优点)Oop 依赖抽象有什么明显的缺点吗?,oop,design-patterns,architecture,package-design,Oop,Design Patterns,Architecture,Package Design,在阅读了关于稳定抽象原则(SAP)的维基之后,我想知道是否有人知道依赖抽象而非具体的缺点(我想,这超过了优点) SAP声明,包越稳定,它就应该越抽象。这意味着,如果一个包不太稳定(更有可能改变),那么它应该更具体。我真的不明白为什么会这样。当然,在所有情况下,无论稳定性如何,我们都应该依赖抽象并隐藏具体实现?首先,从您链接到的论文中: 稳定性不是衡量模块运行的可能性 改变相反,这是一个衡量改变模块难度的指标 因此,难以更改的内容(例如,在许多地方使用)应该是抽象的,以使扩展变得容易/可能 是的,
SAP声明,包越稳定,它就应该越抽象。这意味着,如果一个包不太稳定(更有可能改变),那么它应该更具体。我真的不明白为什么会这样。当然,在所有情况下,无论稳定性如何,我们都应该依赖抽象并隐藏具体实现?首先,从您链接到的论文中: 稳定性不是衡量模块运行的可能性 改变相反,这是一个衡量改变模块难度的指标 因此,难以更改的内容(例如,在许多地方使用)应该是抽象的,以使扩展变得容易/可能 是的,也有缺点。这是改变的容易。更改具体的代码比更改抽象和代码更容易、更快 毫无疑问,在任何情况下,无论稳定与否,我们都应该依靠它 抽象和隐藏具体的实现 这是真的。但是抽象的层次不同。动态示例:如果我要求您计算正方形对角线的长度,那么您可能只需要使用内置的
double sqrt(double)
函数。它是抽象的吗?对我们不知道是否使用了牛顿法,或者它是直接委托给cpu的
但是,如果我们想要创建一个sqrt函数并依赖于它的某种物理计算库呢?在这种情况下,前面的抽象就足够了吗?可能我们不希望(以统一的方式)处理矩阵、相对错误、任意长度的数字、所需数量的内核/线程的并行化、可能委托给gpu,它应该为其他扩展做好准备,因为迟早会有人希望它处理NAN和虚数
所以它仍然是sqrt函数,但抽象级别要高一点。这只是因为很多代码都依赖于它。哪种功能更容易更改
这意味着如果包不太稳定(更有可能更改)
那么它应该更加具体。我真正不明白的是为什么
应该是这样
抽象是软件中很难改变的东西,因为一切都依赖于它们。如果您的包经常更改并且提供了抽象,那么依赖它的人在您更改某些内容时将被迫重写大量代码。但是,如果您的不稳定包提供了一些具体的实现,那么在更改后需要重写的代码将少得多
因此,如果您的包经常更改,那么它最好提供具体内容,而不是抽象内容。否则。。。谁会用它?;) 罗伯特·C·马丁描述事物的方式总是相当晦涩。他的观点总是很好,但需要一点解释——“传入与传出耦合”,啊!马丁写作方式的另一个特点是,它总是在描述性和规定性之间模糊不清(“愿意”或“应该”?) “稳定性” 首先,理解Martin如何定义“稳定性”很重要。他根据产生稳定性度量的传入和传出耦合对其进行了定义:
instability = efferent / (efferent + afferent)
“传入”和“传出”是如此模糊的术语。为了简单起见,让我们用“传出依赖项”代替“传出耦合”,用“传入依赖项”代替“传入耦合”。因此,我们有:
instability = outgoing / (outgoing + incoming)
这与改变的可能性是完全分离的,与改变的困难有着千丝万缕的联系。尽管令人困惑,但根据这个定义,“稳定”的软件包仍然可能一直在变化(当然,这会很糟糕,而且很难管理)
如果您使用上述公式得到一个被零除的错误,那么您的包既没有被使用,也没有使用任何东西
稳定依赖原则
要在上下文中理解Martin关于SAP的观点,更容易从SDP(稳定依赖原则)开始。它说:
包之间的依赖关系应该是
包装的稳定性。包应仅依赖于包
它们比现在更稳定
这很容易理解。更改设计的成本与传入依赖项的数量(和复杂性)成级联关系。可能任何在大规模代码库中工作过的人都能很快理解这一点,因为一个中心设计变更可能最终想要打破代码库中10000个非常复杂的部分
因此,依赖关系应该(会?)流向不变、扎根牢固、毫不动摇的部分,就像一棵树从叶子流向根部一样
根应该具有的稳定性度量可以归结为零传出耦合(零传出依赖)。也就是说,这个稳定的“根”包不应该依赖于其他任何东西。换句话说,它应该完全独立于外部世界。根据马丁的标准,这就是定义“最大稳定性”的特征:完全独立性
Maximum independence = "stable root" (as I'm calling it)
Maximum dependence = "unstable leaf" (as I'm calling it)
考虑到这种完全独立、超稳定的根设计,我们如何能够在不影响接口/设计的情况下轻松地扩展和更改其实现,从而恢复一定程度的灵活性?这就是抽象的意义所在
稳定抽象原则
抽象允许我们将实现与接口/设计分离。
因此,稳定抽象原则应运而生:
最稳定的包应该最抽象。
不稳定的包装应为混凝土。抽象性