问题标签 [solid-principles]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
3 回答
648 浏览

oop - 好的面向对象设计能否像好的关系数据库设计一样形式化?

在数据库世界中,我们有规范化。您可以从设计开始,逐步完成步骤,最终得到数据库的正常形式。这是在数据语义的基础上完成的,可以认为是一系列设计重构。

在面向对象的设计中,我们有 SOLID 原则和其他各种针对良好设计的临时指南。

您是否认为可以为 OO 定义等价的范式,这样一系列重构步骤可以将一段程序代码(或分解不良的 OO 设计)移动到正确的(在某种定义明确的意义上)公式中相同的功能?

(注:很高兴制作这个社区维基)

0 投票
2 回答
1102 浏览

inheritance - 为什么将实例声明为超类型却将其实例化为子类型,加上里氏替换原则

几天来,我一直在尝试理解 Liskov 替换原则,在使用非常典型的 Rectangle/Square 示例进行一些代码测试时,我创建了下面的代码,并提出了 2 个关于它的问题。

问题 1:如果我们有超类/子类关系,为什么我们要将一个实例声明为超类型,但将其实例化(新建)为子类型?

我理解为什么,如果我们通过接口进行多态性,我们希望以这种方式声明和实例化变量:

但是,现在我在旧的编程类和一些博客示例中回忆起它,当通过继承使用多态性时,我仍然会看到一些示例,其中一些代码会以这种方式声明变量

在我下面的代码中,Square 继承自 Rectangle,所以当我以这种方式创建一个新的 Square 实例时:

它仍然可以被视为一个矩形,或者添加到一个通用的矩形列表中,那么为什么有人仍然想将它声明为 Rectangle = new Square() 呢?是否有我没有看到的好处,或者需要这样做的场景?就像我说的,我下面的代码工作得很好。

}

尽管这应该违反 Liskov 替换原则,但我得到以下输出:

"宽度:90,高度:80

ConsoleApp.Program+矩形

宽度:80,高度:80

ConsoleApp.Program+Square

宽度:80,高度:80 ConsoleApp.Program+Square

问题 2:那么,此代码示例为何或如何破坏 LSP?仅仅是因为所有边相等的 Square 不变量打破了 Rectangle 不变量,边可以独立修改吗?如果是这个原因,那么 LSP 违规只是理论上的吗?或者,在代码中,我怎么能看到这个代码违反了原则?

编辑:在我正在阅读的一篇 LSP 博客文章中提出了第三个问题,但没有答案,所以就是这个

问题 3:开闭原则指出我们应该通过新的类(继承或接口)引入新的行为/功能。因此,例如,如果我在基类中有一个 WriteLog 方法,它没有先决条件,但是我引入了一个新的子类,它覆盖了该方法,但只有在事件非常关键时才实际写入日志....如果这是新的预期功能(对子类型进行强化的前提条件),这仍然会破坏 LSP 吗?在这种情况下,这两个原则似乎相互矛盾。

提前致谢。

0 投票
3 回答
72 浏览

coding-style - “可拨号”电源原理(又名?)

作为一名设计师,我喜欢提供满足功率/简单性平衡的界面。例如,我认为 LINQ 设计者遵循了这一原则,因为他们提供了点表示法和查询表示法。第一个功能更强大,但第二个更易于阅读和遵循。如果您不同意我对 LINQ 的评估,请尝试理解我的观点;LINQ 只是一个例子,我的帖子不是关于 LINQ。

我把这个原则称为“可拨号电源”。但我想知道其他人怎么称呼它。当然有些人会说“KISS”是常用词。但我认为 KISS 是一种超集,或一种“消费主义”实践。再次使用 LINQ 作为我的示例,在我看来,一个总是尝试使用查询表示法而不是点表示法的程序员团队正在练习 KISS。因此,LINQ 设计者实践了“可拨号电源”,而 LINQ 消费者实践了 KISS。两人一起创作美妙的音乐。

编辑我再举一个例子。想象一个日志工具有两个签名,允许两种用途:

这两个签名的目的是满足这些需求:

拥有这些重载代表“可拨号能力”,因为消费者可以选择简单接口或功能强大的接口。喜欢 KISS 的消费者大部分时间会使用更简单的签名,并在需要电源时允许看起来“忙碌”的签名。这也有助于自我记录,因为使用强大的签名会告诉读者代码对性能至关重要。如果记录器只有强大的签名,那么就没有“可拨电源”。

所以这是一个完整的循环。如果尚不存在的话,我很乐意保留我自己的“可拨号电源”造币,但我不禁认为我错过了这种做法的明显名称。

ps 另一个相关但与“可拨号电源”不同的例子是 Scott Meyer 的原则“使接口易于正确使用,而难以错误使用”。

0 投票
1 回答
2589 浏览

design-patterns - 适配器模式与 Liskov 替换

适配器设计模式用于将类的接口(目标)转换为客户期望的另一个接口(适配器)。Adapter 让不兼容的类可以一起工作,因为它们的接口不兼容。

适配器模式可以通过两种方式实现,继承(适配器模式的类版本)和组合(适配器模式的对象版本)。

我的问题是关于使用继承实现的适配器模式的类版本。

这是绘图编辑器的示例:

图1:

我们想复用TextView类来实现TextShape,但是接口不同,所以TextView和Shape对象不能互换使用。

是否应该更改 TextView 类以符合形状界面?也许不是。

TextShape 可以通过以下两种方式之一使 TextView 界面适应形状的界面:

  1. 通过继承Shape的接口和TextView的实现(Adapter模式的类版)
  2. 通过在 TextShape 对象内部组成一个 TextView 实例,并使用 TextView 实例(适配器模式的对象版本)实现 TextShape 的接口。

类适配器

图 2:

现在的问题:-)。TextShape 是否继承自 Shape 尤其是 TextView 是有效的“是”关系?如果不是,这是否违反了Liskov 的替代原则

0 投票
2 回答
3899 浏览

php - Dependency Inversion Principle in PHP

Since PHP is a loosely typed language, how can the DIP principle be applied in PHP?

A practical example would be greatly appreciated.

0 投票
2 回答
105 浏览

oop - 动态 GetHash 函数的类设计

我需要计算几种类型实例的内部哈希码(某些类型是相互派生的)。这里有两个方面是动态的,可以独立变化。只有请求散列的客户端知道要使用什么散列算法以及要包含什么属性。

  1. 用于哈希计算的实际算法可能会改变。
  2. 哈希计算应考虑每种类型的哪些成员可以更改。

您将如何围绕这些要求设计您的类型?

0 投票
5 回答
889 浏览

solid-principles - 编写只做一件事并做好的程序

我可以通过封装、依赖注入最少知识原则你不需要它来掌握“做一件事”的部分;但是我如何理解第二部分“做好”?

给出的一个例子是完整性的概念,在同一篇 YAGNI 文章中给出:

例如,在允许添加项目、删除项目或修改项目的功能中,完整性也可用于推荐“重命名项目”。

但是,我发现这样的推理很容易被滥用到功能蠕变中,从而违反了“做一件事”部分。

那么,什么是查看某个功能属于“做得好”类别(因此,将其包含到函数/类/程序中)或其他“做一件事”类别(因此,排除它)的试金石?

第一部分“做一件事”最好通过 UNIX 的ls命令来理解,作为一个反例,因为它包含过多的标志来格式化其输出,这些标志应该完全委托给另一个外部程序。但是我没有一个很好的例子来看到第二部分“做得好”。

什么是一个很好的例子,删除任何进一步的功能会使其“做得不好”?

0 投票
4 回答
9050 浏览

functional-programming - 用于函数式编程的 SOLID

我来自 OOP 语言,熟悉面向对象设计的 SOLID 原则。其中一些似乎适合函数式编程模型,而其他部分在缺乏状态的世界中毫无意义。是否有类似的重构功能代码的原则?

0 投票
1 回答
1498 浏览

asp.net-mvc-3 - 在 SOLID 应用程序架构中使用 Ninject

我从 MVC3 开始,想使用一些灵活的架构,所以我阅读了数十篇博客,一本书(Pro ASP.NET MVC 3),阅读了有关 SOLID 原理的内容,最后得到了我喜欢的应用程序结构(或在至少我是这么认为的,到目前为止,因为我还没有在它上面构建任何东西):

在此处输入图像描述

在这个结构中:

  • 域保存 POCO 类并定义服务接口
  • 服务实现服务接口并定义存储库接口
  • 数据实现存储库接口
  • WebUI 和域使用服务
  • 服务使用存储库
  • WebUI、服务和数据依赖于 POCO 类的域

域使用服务的主要原因是验证 POCO (IValidatable) 类的 Validate 方法上的唯一键。

我开始使用这种结构构建一个参考应用程序,但到目前为止,我遇到了两个问题:

  1. 我正在使用带有存储库单元测试的 Data.Tests 项目,但还没有找到在模型上注入(使用 Ninject)服务实现(在构造函数中或其他方式中)的方法,因此 Validate 方法可以在服务上调用 CheckUniqueKey。

  2. 我还没有找到任何关于将 Ninject 连接到 TEST 项目的参考资料(很多用于 WebUI 项目)。

我在这里想要实现的是能够从 EF 切换到 DAPPER 之类的其他东西,只需更改 DATA 程序集即可。

更新

现在(截至 2011 年 8 月 9 日)Ninject 正在工作,但我想我错过了一些东西。

我有一个带有两个构造函数的 CustomerRepository:

在 TestInitialize 上:

在客户类上:

在这种情况下使用 Ninject 的最佳方法是什么?

任何帮助将不胜感激。

答案,有点

到目前为止,我将考虑这个问题。我可以让 Ninject 正常工作,但看起来实现 SOLID 的依赖倒置原则 (DIP) 需要更多时间。

在这方面,我不得不将域、服务和数据混为一谈,我将在其他时间创建另一个问题,并让项目暂时按照通常的方式进行。

谢谢大家。

0 投票
5 回答
547 浏览

c# - 推荐一个设计模式

我正在处理的应用程序处理工作项。根据工作项的状态,有许多可用的操作。“完成”“取消”“重新分配”等...

为了提供操作的功能,我目前有一个看起来像这样的界面......

然后根据工作项的其他细节,我有了接口的具体实现。举个例子……

问题是,如果我想添加一个新动作,比如说......“委托”我必须更新接口,这当然会对所有实现产生影响。

这是否违反了打开/关闭原则?你能推荐一个可能对我有帮助的设计模式或重构吗?