有效C ++的第23项规定:首选非成员非友元函数到成员函数。
该项目的全部目的是鼓励封装,以及封装灵活性和功能扩展性,但我的问题是,在采取这些建议时你走了多远?
例如,您可以拥有自己的类,私有数据成员,然后通过将公共函数减少为私有数据成员的访问者和/或更改者来采用极简主义方法。然后,每个其他功能都可以是非成员功能。
但是,您是否愿意在可能牺牲代码清晰度的情况下增加封装以及访问器和变更器?画线在哪里?
答案 0 :(得分:9)
首先,不是每个人都同意这个建议。我不认为我见过任何人,只有Meyers(编辑:和Herb Sutter)给出了这个建议,我只看到它在C ++的上下文中给出。例如,在Java或C#中创建“非成员非友元函数”实际上是不可能的,因为Java和C#没有自由函数,而Ruby开发人员(例如)更喜欢有意创建成员函数的"humane interfaces"非成员可以做同样的事情,只是为了让这些功能的呼叫者的生活更轻松。
即使你接受了迈耶斯的建议,你也应该更喜欢非会员的非朋友功能到会员功能(我认为这是一个很好的建议,它确实帮助我更好地应用封装来考虑封装类的实现甚至从其成员函数),这是唯一需要考虑的设计轴。
面向对象设计的关键概念是对象做某事。对象不仅仅是其他代码所做的一组setter和getter。相反,它应该附加行为 - 也就是说,它应该有做事情的方法 - 它应该封装它们如何做这些事情的细节。如果你按照这种方法使用OO,那么将Meyers的建议推向极致,因为你确实伤害了封装而不是帮助它:你最终通过getter和setter暴露所有类的内部实现变量而不是隐藏它们以便只有类的方法(代表类负责做东西的代码,这是你开始上课的唯一原因)可以得到它。
那么回答你关于迈尔斯的建议还有多远的问题:如果可以合理地使用类的非朋友非成员函数实现它们,那么不必要将函数转换为成员函数公共接口,但不要破坏类的公共接口,并通过将实现暴露给来避免使其成为某个成员而违反其封装。并确保你平衡封装与其他问题和其他方法(包括,如果你的团队决定走这条路线,一个完整的人性化界面的利弊)。
答案 1 :(得分:3)
退一步考虑一下班级的目的:一个工作在做什么?什么类不变量必须确保以最佳方式完成这项工作?子类化和覆盖在类的目的中扮演什么角色?
根据访问者和变异器来构建所有是绝对不合适的:这几乎会与OOP根源的状态和行为的结合离婚,或掩盖没有合理框架的行为问题是在这种“假装”的变异器和访问器的面纱下获取或设置属性。
只使用 访问器和mutators的类是一个特殊情况 - 可能是传统C类struct
的一步,因为能够保留一些不变量,但“只是勉强” ; - 。)
一个好的OOP设计中的大多数类都会有行为 - 就像我喜欢泛型编程一样,使用C ++的一个原因是它的多种范例的强大组合,其中OOP不能被删除! - )
答案 2 :(得分:2)
实际上,只为你班级的私人变量提供访问者和变异者实际上并不是极简主义(或者在某种意义上可能是极简主义但不是“最相关”的意义),因为你现在提出的更多一般与世界的接口。封装的想法是,您的类的界面应该尽可能地受到限制,同时允许客户端代码完成工作。
遵循这种方法,将来更容易更改底层实现,这首先是封装的重点。
答案 3 :(得分:0)
一般来说,访问者背后的想法是变异器,需要以某种方式保护变量。如果变量具有应该仅由一个实体更新的敏感信息,则可以在mutator中包含该检查。一个简单的事实是,大多数访问器和更改器只是一种形式(以至于C#现在具有自动属性)并且不是必需的。底线是,用你的判断。如果它需要限制访问,那么添加一个mutator。如果获取/设置变量无关紧要,则不需要访问器和增变器。
答案 4 :(得分:0)
研究如何将东西打包成合适尺寸的单位是一种主观艺术。
我制作非成员非朋友的问题之一是,如果内部结构的变更需要添加新的公共接口以支持现有的非成员,或者非成员现在被承认为朋友,你显示出比之前设计的更紧密的联系。现在做出任何改变都可能成本很高。
虽然可以说在逻辑层面上非成员函数可以与类一起打包并构成类的接口,但是如果方法的位置发生变化,开发人员将需要进行一些代码更改,因此
表示接口的实现不是错误的关于Eiffel的一个好处,一个没有参数的方法以与变量相同的方式访问,因此在这些元素之间进行更改是透明的。