Questions tagged «solid»

一组设计原则的助记符:单项责任,开放式,封闭式,Liskov替代,接口隔离,依赖性反转

5
接口(OOP)的语义约定是否比功能签名(FP)更具信息意义?
有人说,如果您将SOLID原理付诸实践,那么您最终会从事函数式编程。我同意本文的观点,但是我认为从接口/对象到函数/闭包的转换会丢失一些语义,并且我想知道函数式编程如何减轻这种损失。 从文章: 此外,如果您严格地应用接口隔离原理(ISP),您将了解您应该比角色头接口更喜欢角色接口。 如果您继续将设计推向越来越小的界面,那么最终您将获得最终的角色界面:使用单一方法的界面。这在我身上经常发生。这是一个例子: public interface IMessageQuery { string Read(int id); } 如果我依赖IMessageQuery,则隐式合同的一部分是调用Read(id)将搜索并返回具有给定ID的消息。 将此与对其等效功能签名的依赖进行比较 int -> string。没有任何其他提示,此功能可能很简单ToString()。如果您使用A实施IMessageQuery.Read(int id),ToString()我可能会指责您故意颠覆性! 那么,函数式程序员可以做什么来保留命名接口的语义?例如,创建具有单个成员的记录类型是常规做法吗? type MessageQuery = { Read: int -> string }

5
如何在C中应用接口隔离原则?
我有一个模块,例如“ M”,其中有一些客户端,例如“ C1”,“ C2”,“ C3”。我想将模块M的名称空间(即,其公开的API和数据的声明)分配给头文件,使得- 对于任何客户端,仅可见其所需的数据和API;模块的其余名称空间对客户端是隐藏的,即遵守接口隔离原则。 在多个头文件中不重复声明,即不违反DRY。 模块M与其客户端没有任何依赖关系。 客户端不受模块M未被其使用的部分中所做的更改的影响。 现有客户端不受添加或删除更多客户端的影响。 目前,我通过根据模块客户端的需求划分模块的命名空间来解决此问题。例如,在下面的图像中,显示了其3个客户端所需的模块名称空间的不同部分。客户要求有重叠。模块的名称空间分为4个单独的头文件-'a','1','2'和'3'。 但是,这违反了上述某些要求,即R3和R5。违反了要求3,因为这种划分取决于客户端的性质;在添加新客户端时,该分区也会更改并违反要求5。如上图右侧所示,在添加新客户端后,模块的命名空间现在分为7个头文件-'a ”,“ b”,“ c”,“ 1”,“ 2 *”,“ 3 *”和“ 4”。用于2个旧客户端的头文件发生更改,从而触发了其重建。 有没有办法以非人为方式在C中实现接口隔离? 如果是,您将如何处理上述示例? 我想象的是一个虚幻的假设解决方案- 该模块有1个胖头文件覆盖了整个名称空间。该头文件分为可寻址的部分和子部分,如Wikipedia页面。然后,每个客户端都有一个为其量身定制的特定头文件。特定于客户机的头文件只是指向胖头文件的节/子节的超链接的列表。如果构建模块指向模块头文件中的任何部分,则构建系统必须将特定于客户端的头文件识别为“已修改”。
15 c  interfaces  solid 

4
测试驱动的开发是否迫使我遵循SOLID?
我从TDD的实践者那里听到很多东西,TDD的优势之一是它迫使开发人员遵循SOLID原则(单一职责,开放式封闭,Liskov替代,接口隔离和依赖倒置)。但是对我而言,只需编写一些测试(主要是单元测试)就足以理解遵循SOLID(从而创建可测试的体系结构)非常重要。 TDD是否会迫使开发人员更积极地遵循SOLID,而不仅仅是编写单元测试?

3
RxJava类Flowable是否可以合法地使用460个方法?
我刚刚开始使用RxJava,它是Java对ReactiveX的实现 (也称为Rx和Reactive Extensions)。一些真正让我吃惊的庞大规模RxJava的可流动类:它具有460点的方法! 公平起见: 有很多方法被重载,这大大增加了方法的总数。 也许应该拆分此类,但是我对RxJava的知识和理解非常有限。谁创造RxJava的人肯定是非常聪明的,他们可以大概是选择创建提供有效的参数可流动有这么多的方法。 另一方面: RxJava是Microsoft的Reactive Extensions的Java实现,甚至没有Flowable类,因此,这不是盲目移植现有类并用Java实现的情况。 [ 更新:斜体前一点是事实不正确:微软的可观察类,其中有超过400的方法,被用来作为RxJava的基础上可观察类,并且可流动相似,可观察到,但手柄背压的大量数据。因此,RxJava团队正在移植现有的类。这篇文章应该是挑战的原设计可观察到由微软而不是RxJava的类悬浮剂分类] RxJava已有3年多的历史了,因此这并不是由于缺乏对良好(SOLID)类设计原理的了解而导致代码被错误设计的示例(与Java的早期版本一样)。 对于像Flowable这样大的类,其设计本质上是错误的,但也许不是。这个SE问题的一个答案一个类方法的数量限制是多少?建议答案是“ 拥有所需的多种方法 ”。 显然,有些类无论其语言如何,都合法地需要大量方法来支持它们,因为它们不会轻易分解为更小的东西,并且它们具有大量的特征和属性。例如:字符串,颜色,电子表格单元格,数据库结果集和HTTP请求。对于类来说,也许有几十种方法来表示这些东西似乎并不合理。 但是Flowable是否真的需要460种方法,还是它是如此之大以至于它必然是不良类设计的一个例子? [需要明确的是:这个问题,特别是涉及RxJava的可流动类,而不是一般的神的对象。]

2
现实世界-Liskov替代原理
背景:我正在开发一个消息传递框架。该框架将允许: 通过服务总线发送消息 订阅消息总线上的队列 订阅消息总线上的主题 我们目前正在使用RabbitMQ,但我知道我们将在不久的将来迁移到Microsoft Service Bus(在Premise上)。 我计划创建一组接口和实现,以便当我们转向ServiceBus时,我只需要提供一个新的实现而无需修改任何客户端代码(即发布者或订阅者)。 这里的问题是RabbitMQ和ServiceBus不能直接翻译。例如,RabbitMQ依赖于交换和主题名称,而ServiceBus全部与命名空间和队列有关。同样,在ServiceBus客户端和RabbitMQ客户端之间没有公共接口(例如,两者都可以具有IConnection,但是接口是不同的-不同于公共名称空间)。 因此,就我而言,我可以创建如下界面: public interface IMessageReceiver{ void AddSubscription(ISubscription subscriptionDetails) } 由于这两种技术的不可翻译特性,上述接口的ServiceBus和RabbitMQ实现具有不同的要求。因此,我的IMessageReceiver的RabbitMq实现可能如下所示: public void AddSubscription(ISubscription subscriptionDetails){ if(!subscriptionDetails is RabbitMqSubscriptionDetails){ // I have a problem! } } 对我来说,上面的线打破了李斯科夫的可替代性规则。 我考虑过将其翻转,以便订阅可以接受IMessageConnection,但是RabbitMq订阅再次需要RabbitMQMessageConnection的特定属性。 因此,我的问题是: 我是否认为这会破坏LSP? 我们是否同意在某些情况下这是不可避免的,还是我错过了一些东西? 希望这很清楚,而且是话题!

4
单一职责模式对班级应该有多具体?
例如,假设您有一个控制台游戏程序,该程序具有往返于控制台的各种输入/输出方法。难道是聪明的,让他们在一个单一inputOutput类或把它们分解到更具体的类,如startMenuIO,inGameIO,playerIO,gameBoardIO,等使得每个班大约有1-5的方法呢? 同时,如果最好将它们分解,将它们放置在IO命名空间中是否明智,从而使它们更冗长,例如:IO.inGame等?

3
在实践中如何遵守开闭原则
我了解开放原则的意图。这是通过告诉您尝试在不进行修改的情况下进行扩展,以降低破坏已在修改过程中起作用的内容的风险。 但是,我在理解此原理在实践中的应用时遇到了一些麻烦。据我了解,有两种方法可以应用它。在可能的更改之前和之后: 之前:编程抽象并尽可能“预测未来”。例如,drive(Car car)如果将来将Motorcycles添加到系统中,则必须更改 方法,因此它可能违反了OCP。但是该方法drive(MotorVehicle vehicle)将来不太可能需要更改,因此它遵循OCP。 但是,很难预测未来并提前知道将对系统进行哪些更改。 之后:需要更改时,扩展类而不是修改其当前代码。 练习1并不难理解。但是,在实践2中,我很难理解如何申请。 例如(我从YouTube视频中获取了它):假设我们在类中有一个接受CreditCard对象的方法:makePayment(CraditCard card)。将一天Voucher添加到系统中。此方法不支持它们,因此必须对其进行修改。 首先实现该方法时,我们无法预测未来,无法以更抽象的术语进行编程(例如makePayment(Payment pay),因此现在我们必须更改现有代码。 练习2说,我们应该通过扩展而不是修改来添加功能。这意味着什么?我应该继承现有的类而不是简单地更改现有的代码吗?我是否应该围绕它进行某种包装,以避免重写代码? 还是该原则甚至不涉及“如何正确修改/添加功能”,而是涉及“如何避免必须首先进行更改(即从程序到抽象)”?

3
接口隔离原理的两个矛盾定义–哪个正确?
在阅读有关ISP的文章时,似乎对ISP有两个相互矛盾的定义: 根据第一定义(见1,2,3),ISP指出,实现该接口的类不应该被迫实施的功能,他们并不需要。因此,胖界面IFat interface IFat { void A(); void B(); void C(); void D(); } class MyClass: IFat { ... } 应该分成较小的接口,ISmall_1并且ISmall_2 interface ISmall_1 { void A(); void B(); } interface ISmall_2 { void C(); void D(); } class MyClass:ISmall_2 { ... } 因为这样我MyClass能够实现只需要(方法D()和C()),而无需被迫还提供了虚拟实现A(),B()并且C(): 但根据第二个定义(见1, 2,通过纳扎尔Merza答案),ISP指出MyClient调用方法MyService不应该知道的有关方法MyService,它不需要。换句话说,如果MyClient只需要的功能C()和D(),然后代替 class MyService { public void …

1
如何在继承层次结构中验证Liskov替换原理?
灵感来自这个答案: 里氏替换原则要求 是 前提条件不能在子类型中得到加强。 子条件不能弱化后置条件。 超类型的不变量必须保留在子类型中。 历史记录约束(“历史记录规则”)。只能通过对象的方法(封装)将其视为可修改的对象。由于子类型可能会引入父类型中不存在的方法,因此这些方法的引入可能会导致子类型中状态不允许在父类型中发生变化。历史记录约束禁止这样做。 我希望有人能发布违反这4点的类层次结构,以及如何相应地解决它们。 我正在寻找出于教育目的的详细说明,以了解如何识别层次结构中的四个点以及解决该问题的最佳方法。 注意: 我希望发布代码示例供人们使用,但是问题本身是关于如何识别错误的层次结构的:)

2
堆栈扩展LinkedList。是否违反了Liskov替代原则?
存在具有诸如add_first(),add_last(),add_after(),remove_first(),remove_last()和remove()之类的函数的LinkedList类。 现在有一个Stack类,提供了push(),pop(),peek()或top()等功能,并且为了实现这些方法,它扩展了LinkedList类方法。这是否违反了《里斯科夫替代原则》? 例如,考虑在add_after()情况下将节点添加到链接列表的第n个位置的情况。这可以在基类中完成,但不能在Stack类中完成。此处的后置条件是否已减弱,或者您是否修改add_after()方法以将其添加到堆栈顶部? 另外,如果不违反,这是不好的设计吗?以及如何使用LinkedList类实现Stack功能?


4
模拟是否违反开放/封闭原则?
前段时间,我在找不到的Stack Overflow答案上读了一句话,该句子解释您应该测试公共API,而作者说您应该测试接口。作者还解释说,如果更改了方法实现,则无需修改测试用例,因为这样做会破坏确保被测系统正常工作的契约。换句话说,如果方法不起作用,则测试应该失败,但这不是因为实现发生了变化。 当我们谈论嘲笑时,这引起了我的注意。由于模拟在很大程度上依赖于被测系统依赖项的期望调用,因此模拟与实现紧密相关,而不是与接口紧密相关。 在研究模拟与存根时,有几篇文章认为应该使用存根而不是模拟,因为它们不依赖于依赖项的期望,这意味着测试不需要了解测试实施中的基础系统。 我的问题是: 模拟是否违反开放/封闭原则? 在上一段中支持存根的论点中是否缺少某些内容,使存根与模拟不那么好? 如果是这样,什么时候可以模拟一个好用例,什么时候可以使用存根?


1
包装第三方代码是否是对用户进行单元测试的唯一解决方案?
我正在进行单元测试,并且在我的一个类中,我需要从一种方法中发送邮件,因此使用构造函数注入,我将注入Zend_MailZend框架中的类的实例。 现在有人认为,如果一个库足够稳定并且不会经常更改,那么就无需对其进行包装。因此,假设这Zend_Mail是稳定的并且不会改变并且完全满足我的需求,那么我就不需要包装器了。 现在看一下我的课程Logger,该课程取决于Zend_Mail: class Logger{ private $mailer; function __construct(Zend_Mail $mail){ $this->mail=$mail; } function toBeTestedFunction(){ //Some code $this->mail->setTo('some value'); $this->mail->setSubject('some value'); $this->mail->setBody('some value'); $this->mail->send(); //Some } } 但是,单元测试要求我一次测试一个组件,因此我需要模拟Zend_Mail该类。另外,我违反了依赖倒置原则,因为我的Logger课程现在依赖于具体而非抽象。 现在,我如何Logger在不进行包装的情况下进行隔离测试Zend_Mail? 该代码在PHP中,但不一定必须提供答案。这更多的是设计问题,而不是特定于语言的功能

3
应用SOLID原则
我对SOLID设计原则很陌生。我了解它们的原因和好处,但是我未能将它们应用到一个较小的项目中,我想将其重构为使用SOLID原理的实际练习。我知道没有必要更改运行正常的应用程序,但是无论如何我都希望对其进行重构,这样我才能获得未来项目的设计经验。 该应用程序具有以下任务(实际上还有很多任务,但让我们保持简单):它必须读取一个包含数据库表/列/视图等定义的XML文件,并创建一个SQL文件,该文件可用于创建ORACLE数据库架构。 (注意:请不要讨论为什么我需要它或为什么我不使用XSLT等,这是有原因的,但是它们不在主题之列。) 首先,我选择仅查看表和约束。如果忽略列,则可以通过以下方式声明它: 约束是表的一部分(或更准确地说,是CREATE TABLE语句的一部分),并且约束也可以引用另一个表。 首先,我将说明应用程序现在的外观(不应用SOLID): 目前,该应用程序具有“表”类,该类包含表所拥有的约束的指针列表以及引用该表的约束的指针列表。每当建立连接时,也会建立向后连接。该表具有createStatement()方法,该方法依次调用每个约束的createStatement()函数。所述方法本身将使用到所有者表和引用表的连接,以检索它们的名称。 显然,这根本不适用于SOLID。例如,存在循环依赖关系,这些依赖关系根据所需的“添加” /“删除”方法和一些大型对象析构函数使代码膨胀。 所以有两个问题: 我应该使用依赖注入来解决循环依赖吗?如果是这样,我想约束应该在其构造函数中接收所有者(以及可选的被引用)表。但是,如何在单个表的约束列表上运行呢? 如果Table类都存储自身的状态(例如,表名,表注释等)以及到约束的链接,那么考虑“单一职责原则”,这是一个还是两个“职责”? 如果情况2是正确的,我是否应该在管理链接的逻辑业务层中创建一个新类?如果是这样,1.显然不再相关。 应该将“ createStatement”方法作为Table / Constraint类的一部分,还是应该将它们移出?如果是这样,去哪儿?每个数据存储类(例如,表,约束等)一个管理器类?还是为每个链接创建一个管理器类(类似于3)? 每当我尝试回答这些问题之一时,我都会发现自己在某个地方盘旋。 如果您包括列,索引等,问题显然变得更加复杂,但是如果你们帮助我解决简单的Table / Constraint问题,我也许可以自己解决其他问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.