最近看到的:
http://www.royaloo.com/bjarne/interviews/bs_artima1.htm
类应该强制执行不变式
Bjarne Stroustrup:根据我的经验,当且仅当你确定你的类中有一个不变式时,你应该设计一个具有接口的类以及一个隐藏的表示。
Bill Venners:您所说的不变式是什么意思?
Bjarne Stroustrup:是什么让一个对象成为一个有效的对象?不变式允许你说出一个对象的表示何时良好何时不好。以vector作为一个非常简单的例子,一个vector知道它容纳了n个元素,它有一个指向这n个元素的指针。这儿不变式就是指指针指向一块内存区域,而这块内存区域可以容纳n个元素。如果它容纳了n-1或者n+1个元素,那就出现了bug。如果指针为0,那也是bug,因为该指针并未指向任何东西,这就表示了它违背了一个不变式。所以你必须分辨出哪些对象有意义,哪些是好的,哪些是坏的。这样你就可以提炼出维护不变式的接口。这是检查成员函数合理性的一种途径,同时也是判断一个操作是否应该成为成员函数的一种方式。那些不需要与内部表示混在一起的操作最好被安排到类外。这样一来,你就可以得到一个整洁、小巧而容易理解和维护的接口。
Bill Venners:这就是说,不变式表示一个类存在的正当性,因为类本身承担起了维护该不变式的责任?
Bjarne Stroustrup:没错。
Bill Venners:这样说来,不变式就是类中各个不同的数据成员之间的一种关系?
Bjarne Stroustrup:是这样的。如果每个数据成员都可以被赋予任何值,那就没什么太大必要做成一个类。以一个简单的“名字—地址”数据结构为例,如果任何字符串都是合法的名字,任何字符串都是合法的地址,那么它本质上就是一个结构,而不是一个类,请使用struct关键字来声明它。千万别把名字和地址作为私有数据成员隐藏起来,然后再提供类似于get_address、set_address、get_name以及set_name这样的成员函数来存取它们。或者更糟糕地,提供一个拥有get_name和set_name之类的虚函数的抽象基类,然后在一个派生类中 重写它们,这种做法纯粹是挖空心思而已,绝无必要。
Bill Venners:您的意思是因为那些类有且仅有一种具体的实现,所以把它们声明为class是不必要的 。可是有一种辩解认为:如果你将数据成员存取操作封装为函数,那么你就可以灵活地改变这个类的具体实现方式了。
Bjarne Stroustrup:大部分情况下是这样的,但有些实现是你不会去改变的:你并不会经常去改变一个整数、或者一个点、或者一个复数等类的实现。如果真的 需要改变它们的话,你应该在某个地方做好设计决策。
下一层次,当你要从原始的数据结构转移到真正的类的时候,让我们再次以名字-地址为例:你应该不会将这个类命名为name_and_address吧?也许你可以把它称为personnel_record或者mailing_address。在这个层次上,你认为名字和地址都不仅仅只是字符串而已。也许你想把名字拆开为first name、middle name和last name来分别存储,或者你决定采用一种由你自己确定语义的字符串来存储这三个部分,你也可以决定是否判断地址的有效性,并根据这些性质将字符串分为first address、second address、城市、洲、国家以及邮编 之类的东西。
当开始进行这样的分解工作时,你就应该开始考虑更改这个类的具体实现的可能性了。这时,你要开始做出决定:是真的往类中加入一个私有数据成员,并使用继承 ,还是仅仅只使用一个平凡的类,并固定其表现形式,或者你希望为数据提供一个抽象接口,这样它们就可以拥有不同的表现形式。这里的重点不是“如何决定”,而是“做出决定”。你不能毫无章法可言地将一些类和函数堆砌在一起 ,如果你决定采用私有数据成员的话,你需要先定义一些确切的语义。
整个事情的思路是:在构造函数中,建立好成员函数进行操作时所必需的环境。换句话来说,构造函数建立了不变式。而为了建立不变式,你通常需要分配一些资源。在析构函数中,你可以清理环境并切释放资源 ,这些资源可以是内存、文件、同步锁以及socket连接等,凡是你能想到的,都可以在析构函数中被释放。
设计简单的接口
Bill Venners:您刚才提到不变式可以帮助我们确定什么东西是应该放到接口 中的,可以解释得更详细一点吗?我现在试着复述一遍您刚才所说的概念,看看我是否已经理解了它。第一点,所有和不变式相关或能够操作不变式的功能都应该放到类中。
Bjarne Stroustrup:对!
Bill Venners:任何仅仅使用数据 而不会对不变式产生影响的功能就不需要放到类中。
Bjarne Stroustrup:我来给个例子具体说明一下 ,有些操作是你一定要与具体实现进行直接的交互才能完成的。如果一个操作会改变一个vector的大小,那你最好也让 其同时改变vector内容纳的元素的数量。如果你仅仅需要读出这个大小变量的话,那么肯定应该存在这样的一个成员函数。但除 了这些需要直接与不变式打交道的基本函数外,还存在许多以这些函数为基础的其他函数,比如对vector的高效存取、查找和搜索操作等 ,这些函数就最好不要被设计为成员函数。
作为另一个例子,让我们再来看看日期类。所有能够改变年、月、日的操作应该被作为该类的成员函数,而那些诸如搜索下一个周末、周日的函数则可以建构在基本成员函数之上。我曾经 看到过一个拥有60到70个成员函数的日期类,那个类的设计者把所有操作都放到类里面去了,甚至包括find_next_Sunday这样的函数也不例外,实际上这些函数在逻辑上与这个类没有任何关系,如果你将这个函数作为该类的成员变量,那么这些函数就可以接触到类中的具体数据成员,这就意味着如果你想改变日期的表现形式的话,你需要 修改近60个函数,在近60个地方修改 (译注:可能还不止:))。
作为一种替代,如果你为这个日期类建立一个具有相互联系的简单接口的话,那么出于逻辑相关或者性能等方面的考虑,这个类中应该只会有5到10个左右的成员函数 — 虽然我想不出日期类有什么性能问题可言,不过它的确是思考问题时一个重要的焦点。然后以这5到10个基本操作为基础,你再把另外的50个操作放到一个支撑库中。 最近这种思维方式被越来越多的人所接受。甚至在Java中,你也可以在拥有一个容器的同时拥有一个由静态方法所构成的支撑库。
可惜的是,尽管我已经为这种思想作了近20年的宣传工作,人们仍然倾向于把所有东西都放到类和继承体系中去。关于上面提到的日期问题,我还见过这样的解决方案:提供一个基类,该基类有一些基本的操作和被声明为protected的数据成员。日后当你需要 添加一些新的工具函数时,你需要从这个基类派生出一个新类,然后再在新类中加上新的工具函数。相信我,你的系统就是被这些东西弄得一团糟的,把这些工具函数放到派生类中毫无道理可言。将这些工具函数分别独立实现可以让我们自由 地组合使用这些工具函数。由于你写的函数和我写的函数是完全独立的,所以可以自由组合它们。如果我和你都从那个日期基类派生出新类型,然后通过往新类型中添加新函数的方法来实现各自的工具函数,那么第三人将很难同时使用我们的函数库,在这里,类继承体系 即被滥用了。
分享到:
相关推荐
Rodin不变式是拓扑学中的一个重要概念,特别是在微分拓扑领域,它与辫群、同调理论和流形的结构紧密相关。这个概念由数学家Bill Thurston的弟子Paul Rodin提出,用于研究二维流形的光滑分类问题。在计算机科学中,...
本资源“考试类精品--计算机视觉算法岗面试准备:常考算法的实现.zip”是一个针对计算机视觉算法岗位面试的复习资料,包含了常用算法的实现。尽管标签为空,但我们可以从文件名“ahao4”推测这可能是一个内部代码或...
高等代数是数学的一个核心分支,它探讨了线性映射、向量空间、行列式、特征值等概念。在2014-2015学年第二学期的数学类高等代数期末考试中,涉及到的主要知识点如下: 1. **正交变换与标准型**: 正交变换是指在...
要确定其类型,我们需要分析其系数矩阵的特征值和行列式。如果特征值中有两个相同的非零值,那么该曲面可能是椭球或双曲抛物面;如果所有特征值都不相等,则为椭球面;如果特征值有一对相反的非零值,可能是柱面或...
不变因子是矩阵的行列式在经过初等行变换后的形式,初等因子是与矩阵的特征值相关的因子,而Jordan标准形则是矩阵在特定基下的一种简化形式,包含了所有特征值的信息。 4. **线性变换与矩阵表示**:第四部分中,...
这个性质基于线性代数中的秩的性质,可以通过矩阵的秩不变性、秩的加减法则以及矩阵乘法的秩关系进行证明。 七、向量的线性组合: 已知a, b, c不共面,证明线性组合r = (r, b, c)(a, b, c)a + (r, c, a)(a, b, c)b ...
3. **数字文档管理**:在企业环境中,IT部门可能会使用文档管理系统来组织、存储和检索这类技术文档,确保信息的安全性和可访问性。 4. **云计算和协同工作**:现代IT技术允许团队成员在不同地点共享和协作编辑此类...
桁架结构是工程结构中的一种,通常由杆件组成,形成几何不变体系,能有效地承受和传递荷载。在装配式建筑中,轻钢框架因其重量轻、强度高、施工速度快等优点被广泛应用。这种框架结构一般由冷弯薄壁型钢制成,具有...
这种电路在许多应用中都有所使用,包括便携式电子设备、太阳能电源系统以及电动车电池管理系统等。在电子政务中,可能涉及到的BOOST电路可能是为了提供稳定的电源,确保电子设备正常运行。 BOOST电路的基本结构主要...
我们证明,在该模型中,Kane-Mele不变式是Z2不变式,它是由系统的Kramers简并性引起的具有全局边界条件的1 + 1维Dirac算子的单参数族的谱流的模。 该频谱流被定义为一个整数,该整数计算从负向非负流动的Dirac算子...
飞轮的工作原理基于牛顿第二定律,即物体的角动量保持不变,除非外力矩作用于它。当系统需要能量时,飞轮减速,释放储存的能量;而在系统过剩能量时,飞轮加速,储存能量。这种能量的动态平衡使得飞轮成为一种高效且...
不变方法是研究特定拉格朗日主义者违反CP的有力方法。 该方法对于处理离散族对称性特别... 对于每种情况,我们构造CP奇数弱基不变式,并使用它们讨论各自的CP性质。 我们发现CP违反对Δ(27)表示的数量和类型很敏感。
传统的焊接平台可能固定不变或者只有有限的调整能力,而“两摇臂七连杆移动式”设计则意味着该平台可以实现多角度、多方向的灵活调整,适应不同车型和部件的焊接需求。 七连杆结构通常在机械工程中用于实现复杂运动...
在自准直式共焦测量中,光源发出的光束经过透镜系统后,其传播方向保持不变,这样可以确保在整个测量过程中,光路的稳定性,减少因环境因素导致的误差。 二、共焦成像技术 共焦成像是光学显微镜技术的一种,它只...
传统的墙体和梁的连接方式往往固定不变,难以应对建筑物在使用过程中可能发生的沉降、变形或热胀冷缩等问题。而可调式结构则允许在一定范围内进行微调,以补偿这些不可预见的影响,从而保持整体结构的稳固。 设计上...
本文将深入探讨“0.1um的高精度接触式传感器 -- GT2系列”,这一类传感器因其极高的精度和可靠性而备受关注。GT2系列传感器是针对微米级别的测量任务设计的,尤其适用于需要精确控制和检测的场合。 1. **定义与工作...
此外,PDF(便携式文档格式)文件的使用,如压缩包内的"图纸打印方法及装置.pdf",允许用户在多种设备上查看和打印图纸,且保持原样不变,增强了协作和文档管理的便利性。 打印设置也是不可忽视的一环。比如,可以...
"交变旋转"是指叶轮的转动方式并非恒定不变,而是按照一定的规律变化,这样可以更好地模拟人体心脏的自然血流模式,减少对血液成分的破坏,降低血栓形成的风险。 悬浮技术则是该血泵的另一大亮点。传统的血泵通常...
传统的铰链设计通常是固定不变的,而主动式铰链则具备动力驱动和控制逻辑,能够在碰撞发生时改变机罩的运动轨迹。这种铰链系统可能包含电动马达、液压或气压驱动装置,以及复杂的控制系统,确保在满足安全标准的同时...