避免从C移植到面向对象的陷阱,对您有用的是什么?


12

我已经使用过程语言进行编程已有一段时间了,我对问题的第一反应是开始将其分解为要执行的任务,而不是考虑存在的不同实体(对象)及其关系。

我在OOP上过一门大学课程,并且了解封装,数据抽象,多态性,模块化和继承的基础知识。

我阅读了/programming/2688910/learning-to-think-in-the-object-directional-way/programming/1157847/learning-object-directional-thinking,并将查看这些答案中指出的一些书籍。

我认为我的一些中型到大型项目将受益于OOP的有效使用,但是作为一个新手,我想避免耗时的常见错误。

根据您的经验,这些陷阱是什么?解决这些陷阱的合理方法是什么?如果您能解释为什么它们是陷阱,以及您的建议如何有效解决该问题,将不胜感激。

我正在考虑类似“是否有大量观察者和修饰符方法并使用私有变量,或者是否有用于合并/简化它们的技术?”这样的思路。

如果有充分的理由混合使用方法,我不担心将C ++用作纯OO语言。(尽管有节制,但让人想起使用GOTO的原因。)

谢谢!


2
不值得一个完整的答案,而是一个重要的问题,它使我花了很长时间才能接受(即我读了很多时间,但把它当成幻想的话题):与成员函数相比,更喜欢免费的功能。这使您的课程保持最少,这是件好事。
stijn 2011年

@stijn本质上,您是在说如果不需要将它放在班级中,请不要放在那里。例如,我看到了许多实用程序成员函数,这些函数很容易是到目前为止我所读代码中的自由函数。
斯蒂芬

对,就是那样。如果您搜索“更喜欢非成员非朋友”,则会发现很多有关它的信息。最后归结为坚持单一责任原则
stijn 2011年

Answers:


10

我学到的一大件事是从外部开始设计类。在开始考虑实现之前就设计接口。这将使该类对您的用户(使用类的用户)直观得多,而不是编写基础算法并构建类并根据需要编写新的公共成员函数。


7
除此之外,我们可以“有意编程”,也就是说,编写一些示例代码以使用新类,看看哪种类型的方法和策略使它易于使用。然后,将该信息用作实现的基准。

2
理论上很棒-设计界面就可以完成。但这在实践中并不那么简单。接口设计和实现通常在连续的迭代中并存,直到最终的接口明确为止。到那时,您也可能拥有最终的实现。
Gene Bushuyev 2011年

9

首先,是暴露过多信息的陷阱。默认值应该private不是public

在那之后出现了太多的获取/设定者。假设我有一个数据成员。我真的需要其他班级的数据吗?好吧,吸气。我真的真的需要在对象的使用期限内更改此数据吗?然后做一个二传手。

大多数新手程序员都默认为每个数据成员创建一个getter / setter方法。这会使界面混乱,通常是错误的设计选择。


2
是的,在那些从表面上理解封装以使数据私有化然后再用吸气剂和装料器吹制封装的人中,它很受欢迎。
Gene Bushuyev 2011年

Java是唯一仍然需要get / set方法的流行语言。其他每种语言都支持属性,因此公共数据成员和属性之间在语法上没有区别。即使在Java中,如果您还控制该类的所有用户,则拥有公共数据成员也是没有罪的。
凯文·克莱恩

公共数据成员很难维护。使用getter / setter(或属性),可以在更改内部数据表示形式时保留接口,而无需更改任何外部代码。从消费者的角度来看,在属性上语法上与成员变量相同的语言中,这一点并不成立。
tdammers

到目前为止,我认为我正在将私有成员“看似”作为函数中的局部变量……对于fn而言,世界其他地方不需要了解它们的任何信息……如果fn更改时,仅需要接口是一致的。我不倾向于使用垃圾邮件获取/设置程序,因此我可能走在正确的轨道上,实际上,看我编写的缓冲区类,根本没有公共数据成员。谢谢!
斯蒂芬

2

当我越过鸿沟时,我决定采用以下方法:

0)我开始慢,只有中小型程序应用程序,并且没有关键任务工作。

1)一个简单的第1遍映射,即我将如何以OO样式从头开始编写程序-当时对我来说最重要,而且这是主观的-找出了所有基类。我的目标是在基类中尽可能地封装。纯虚拟方法可用于基类中的所有可能对象。

2)然后,下一步是创建派生类。

3)最后一步是-在原始过程代码中,将数据结构与观察者/修饰符代码分开。然后使用数据隐藏并将所有通用数据映射到基类中,在子类中包含整个程序中不通用的数据。对过程观察者/修饰符代码的处理相同-所有“遍地使用”的逻辑都进入了基类。仅对数据子集起作用的视图/修改逻辑进入派生类。

如果您非常了解过程代码和数据结构,这是主观的,但它是快速的。代码检查中的失败是指基类中没有出现一些数据或逻辑,而是在各处使用了数据或逻辑。


2

看看其他使用OOP获得良好风格感的成功项目。我建议您看一下Qt,这是我在制定自己的设计决策时总会期待的项目。


是! 在一个人成为某个事物的主人之前,他学会了模仿在他之前工作过的伟大大师。学习他人实现的良好代码是学习的好方法。我建议您从简单的设计入手,逐步提高理解力。
Gene Bushuyev 2011年

1

根据与您交谈的人的不同,OOP中的所有数据都应该是私有的或受保护的,并且只能通过访问器和更改器获得。总的来说,我认为这是一个很好的做法,但是在某些情况下我会偏离该规范。例如,如果您有一个类(比如说用Java),其唯一目的是将一些数据打包到一个逻辑单元中,那么将这些字段设为公开是有意义的。如果它是不可变的封装,只需将它们标记为final并在构造函数中对其进行初始化。这将类(在这种情况下)减少为一个结构(实际上,使用C ++,您实际上应该将其称为结构。工作原理类似于类,但是默认可见性是公共的,并且您的意图更清晰),但是我认为您会发现在这些情况下使用它要舒适得多。

您绝对希望做的一件事是必须在增变器中检查一个字段的一致性,并将其公开。


3
我还建议,如果您有只保存公共数据的类,则应将它们声明为structs-并没有语义上的区别,但它阐明了意图,并使它们的使用显得更加自然,特别是对于C程序员而言。

1
是的,如果您使用的是C ++,我在这里表示同意(这个问题确实提到了),但是我的大部分工作都是使用Java进行的,不幸的是我们没有struct。

您需要清楚地区分聚合类(不常见的情况)和具有功能的类(一般情况)。后者必须进行封装并只能通过公共接口访问其成员,任何数据都不应公开。
Gene Bushuyev 2011年

1

肯特·贝克(Kent Beck)的书《实现模式》是如何使用而非滥用面向对象机制的极佳基础。


1

如果有充分的理由混合使用方法,我不担心将C ++用作纯OO语言。(尽管有节制,但让人想起使用GOTO的原因。)

在我看到这一点之前,我真的没有想到我可以提供很多对话。我必须不同意这种看法。OOP只是可以在C ++中使用的范例之一。坦率地说,我认为这不是其最强大的功能之一。

从面向对象的角度来看,我认为C ++实际上有点不足。例如,在这方面,具有非虚拟功能的想法就是反对。我曾与那些不同意我的人争论过,但就我而言,非虚拟成员根本不适合该范式。多态是OO的关键组成部分,具有非虚函数的类在OO上不是多态的。因此,作为一种面向对象的语言,与Java或Objective-C之类的语言相比,我认为C ++实际上相当弱。

另一方面,在通用编程中,C ++相当不错。我听说它也提供了更好的语言,但是对象和泛型函数的结合是非常强大和富有表现力的。此外,它可以在编程时间和处理时间上都非常快。我认为C ++确实在这方面表现出色,尽管可以承认C ++可能更好(例如,对概念的语言支持)。有人认为他们应该坚持OO范式,并在goto语句的顺序上以不道德的程度对待其他人,因为他们根本没有考虑这种范式,这的确被遗漏了。

模板的元编程能力也非常出色。例如,查看Boost.Units库。该库为尺寸量提供类型支持。我在目前工作的工程公司中广泛使用了此库。它只是为可能的程序员甚至规范错误的一个方面提供了更多的即时反馈。如果不使用显式转换,则无法编译使用公式的程序,其中'='运算符的两边在维度上不相等。我个人没有使用任何其他可能实现这种语言的经验,当然也没有使用同样具有C ++功能和速度的语言。

元编程是一种纯粹的功能范例。

所以说真的,我认为您已经带着一些不幸的误解进入了C ++。除了OO之外,其他的范式也不能避免,它们应该被利用。对于正在处理的问题,请使用自然的范例。不要将对象强加于本质上不是对象容易发生的问题上。就我而言,OO甚至不是C ++的一半。


virtual关键字不是在C ++中提供虚拟类功能吗?至于goto的评论,我想表达的意思是,只要我理解推理,我就不必担心违反经验规则。但是,我一直在寻求使命令/程序避免使用OO的必要性。谢谢。
斯蒂芬

虚拟方法不是多态性的前提,它只是实现它的一种常见方式。实际上,其他所有条件都相等,然后将方法设为虚拟实际上会削弱封装性,因为您正在增加类的api的大小,并且很难确保遵循liskov等。仅在需要接缝的情况下才使虚拟成为可能,而在某个地方通过继承注入新的行为(尽管在OOP中也要谨慎对待继承)。为虚拟起见,虚拟环境不会使类“更多的面向对象”
sara 2016年

1

我想接受有关此问题的答案,但是我无法决定要使用复选标记的答案。因此,我投票反对原始作者,并将其创建为摘要答案。感谢所有花了几分钟的人,我发现您提供的见解为我提供了一个很好的指导,并让我放心,我并没有脱离轨道。

@nightcracker

首先,是暴露过多信息的陷阱。默认值应该是私有的,而不是公共的。在那之后出现了太多的获取/设定者。

我觉得我过去曾观察到这个问题。您的评论使我还记得,通过隐藏基础变量及其实现,我可以自由更改其实现,而不会破坏任何依赖于它们的内容。

多米尼克·古尔托(Dominic Gurto)

在开始考虑实现之前就设计接口。Gene Bushuyev接口的设计和实现通常在连续的迭代过程中齐头并进,直到最终的接口明确为止。

我认为多米尼克的评论是一个理想的理想,但我认为吉恩的评论确实触及了现实。到目前为止,我已经在实践中看到了这一点……并感觉好一点,这并不罕见。我认为,随着我成为一名程序员的成熟,我会倾向于更完整的设计,但是现在我仍然受苦于编写一些代码。

想要最好的

我开始慢,只有中小型程序应用程序,并且没有关键任务工作。在原始过程代码中,将数据结构与上位/修饰符代码分开

这很有道理...我喜欢让事情一直在工作的想法,但是可以通过类重构一些非关键的东西。

下午

您绝对不想做的一件事是必须在增变器中检查一个字段的一致性,并将其保留为公开

我已经知道一段时间了,这是封装数据的优势之一...能够强制保持一致性,并且对于这种情况,条件/范围/等等。

疯狂的埃迪

有人认为他们应该坚持OO范式,并在goto语句的顺序上以不道德的程度对待其他人,这是因为他们不看这种范式而错过了。模板的元编程能力也非常出色。

我最初在Crazy Eddie的答案中错过了很多东西,我想是因为我没有读过其中提到的某些主题……例如元编程。我认为CE帖子中的重要信息是C ++是功能和样式的融合,因此每种功能和样式都应发挥最大的潜能...包括当务之急。

再次感谢所有回应!


0

最大的陷阱是认为OOP是灵丹妙药,或者是“一个完美的范例”。

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.