类和接口的设计

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

  我这篇文章的主旨是介绍一部分类和接口的高质量设计的准则。这些准则不但应该保证设计并且实现的类或者接口本身有高质量代码,而且更重要的是在工业领域应该尽可能的使代码的更新和维护不影响客户的活动,主要也就是保持二进制代码兼容(binary compatibility)和源代码兼容(source compatibility)。我希望这些准则能帮助刚从学校进入工业领域的朋友尽快适应更高标准的编程要求,尽快提升自己的设计能力。

文中以C++类的设计为讨论范围。

1 总提

  面向对象编程对于产出高质量,易维护的代码是非常有帮助的。面向对象编程的概念构建于三个基本特征之上:封装,继承,多态。在C++中,class是面向对象编程概念的核心和具体形式。class通过私有成员体现“封装”,通过直接继承或者组合体现“继承”,通过虚函数和动态绑定(dynamic binding)体现“多态”。class的设计质量直接决定了整个系统的质量。

从整体功能层面谈class设计,有这么三条原则:

单一功能原则(Single Responsibility Principle)

  一个class就其整体应该只提供单一的服务。如果一个class提供多样的服务,那么就应该把它拆分,反之,如果一个在概念上单一的功能却由几个class负责,这几个class应该合并。

开放/封闭原则(Open/Close Principle)

  一个设计并实现好的class,应该对扩充的动作开放,而对修改的动作封闭。也就是说,这个class应该是允许扩充的,但不允许修改。如果需要功能上的扩充,一般来说应该通过添加新类实现,而不是修改原类的代码。添加新类不单可以通过直接继承,也可以通过组合。

最小惊讶原理(Least Surprise Principle)

在重载函数,或者子类实现父类虚函数时,应该基本维持函数原来所期望的功能。比如:

class Pet {
public:
    
virtual Talk() = 0;
};

class Cat : public Pet {
public:
    
void Talk() { cout << "miao"; }
};

class Dog : public Pet {
public:
    
void Talk() { BiteOwner(); }
};

class Dog 在实现虚函数Talk的时候,没有像我们期望的那样输出狗吠声,而是咬起主人来了。这是应该避免的。

2 接口和实现

  在系统中,观察一个class有两个角度,从外部或者用户角度我们看到的是接口,从内部我们看到的是实现。因为系统肯定要不断修改,因此实现免不了不停的变化,但是接口又被要求尽量保持稳定。这两者的矛盾必须通过良好的设计尽量避免,基本原则就是将实现细节与接口隔离。下面列出几条比较具体点的:

接口的设计保持最小而完整

  精简接口函数个数,使每一个函数有代表性,函数功能恰好覆盖class的职能。一个最小的接口可以使维护简单,增加潜在的代码重用性,减少客户的迷惑,并且也可以缩小头文件长度和编译时间。当改进函数时,应该用类似函数名实现改进而保留原函数,代码注释里应该有相应的说明。可以增加新函数,但不能删除旧函数。

成员变量应该都为私有

  显而易见,public变量破坏封装性以及接口和实现的分离;protected变量也可能使客户编写继承类而依赖于父类的实现细节。

避免函数返回成员变量的指针或引用

  这么做也会使客户代码依赖于实现细节。

考虑是否禁用编译器缺省产生的函数

  这些函数包括:复制构造函数,赋值操作符(operator =)。如果我们不打算定义自己的版本而不禁用默认版本的话,可能使客户代码在不注意的情况下调用这些函数。当实现发生改动时就可能引起问题,比如class多了一个heap memory指针。如果我们允许对象拷贝,比较稳妥的方法是禁用它们,而定义一个专门的clone()函数。

3 兼容性(compatibility)

  不用说,兼容性是非常重要的。Intel和Microsoft之所以如此成功,其中一个重要方面就是他们的产品,不管是硬件还是软件,都做到了很好的兼容老产品。代码的兼容也是如此。难以想象,如果客户依赖于你的library产品,而要因为你的产品的更新而不断的重写他的代码,他还会继续用你的产品。

  代码兼容可以简单分为二进制兼容和源代码兼容。二进制兼容也就是说,客户的已编译代码可以在不用重新编译的情况下,直接使用你的不同版本的已编译代码。源代码兼容就是,如果你的代码更新了,客户的代码不需要修改,只需要重新编译就可正常运行。在C++中,接口一般是由头文件和library二进制代码提供,因此,任何可能造成library代码和旧的头文件不一致的情况都可能破坏二进制兼容,因为客户代码必须和新的头文件重新编译一次。

因此,遵循几条准则可以使你更轻松地解决兼容性问题:

不改变类的大小或者改变成员变量的顺序

  包括几个方面:不增加或减少成员变量;不修改成员变量类型;不改变成员变量的声明顺序;不改变虚函数的有无。显而易见,增加或减少成员变量会改变类的大小,并且需要更新头文件,从而可能造成与客户代码不兼容。类型的变化也可能引起类的大小的变化。成员变量的访问一般是由编译器按偏移量确定,顺序如果改变,偏移量也就会改变,破坏了二进制兼容。至于虚函数的有无,决定是否存在虚函数表指针,也就影响了类的大小和成员变量的顺序。

不使用inline函数

  inline函数声明于头文件中,并且被编译于客户代码中,如果inline函数访问了private成员,该成员又改变了顺序,那么inline函数虚要被重新编译,破坏了二进制兼容。

接口函数不使用虚函数

  虚函数的访问和成员变量类似,是通过虚函数表中的偏移。虚函数顺序的改变会影响偏移。因此,在条件允许时,应该避免使用public虚函数。比如:

class Picture {
public:
    
virtual void Draw();
};

应该改为

class Picture {
public:
    
void Draw();
private:
    
virtual void DoDraw();
};

void Picture::Draw()
{
    DoDraw();
}

不改变接口函数的顺序

在很多嵌入式系统中,链接库通过输出函数表(exported function table)暴露接口以节省空间。此时,对接口函数的访问也是通过索引值进行,因此改变顺序也会破坏兼容性。

避免使用函数缺省参数

给函数形参设定缺省值可以方便客户,但是可能破坏兼容。缺省值随头文件给出,缺省值的改变也就会引起兼容问题。

 

 

以上就是我能想到的了,希望能对大家有帮助。

如何设计一个C++的 阅读详情

相关推荐

8、Java 接口设计最佳实践

本文详细探讨了Java接口设计的最佳实践,重点包括减少及其成员的可访问性、使用getter方法实现封装、以及尽量减少的可变性等内容。通过遵循这些设计原则,可以提高代码的可维护性、安全性以及性能,适用于构建高效稳定的软件系统。

rgv23456789的博客 144

C++接口的定义与实现

1.接口的定义与实现 所谓的接口,即将内部实现细节封装起来,外部用户用过预留的接口可以使用接口的功能而不需要知晓内部具体细节。C++中,通过实现面向对象的编程,而在基中只给出纯虚函数的声明,然后在派生中实现纯虚函数的具体定义的方式实现接口,不同派生实现接口的方式也不尽相同,从而实现多态。现通过一个简单的实例讲解具体实现步骤(⊙o⊙)。 1.1接口的定义    通常在头文件中完成接口

zhanghui962623727的博客 4万+

设计--接口

NULL 博文链接:https://java12345678.iteye.com/blog/2025352

C++ 15 接口型(组合)设计模式总结

总览 核心 组合。 说明 适配器:将成员a,转化为b. 桥接: 自由组合,各自变化. 过滤器: 组合成员a,用于过滤a一般是某种条件. 组合模式: B+树,层次结构.不过是树的函数加多态形态. 装饰器模式: 自己组合自己,链表的形式. 外观模式: 组合其他,任意组合都需要对外提供集中简单的功能,不直接对外,getter,setter都是。 享元模式: 组合其他,以key:value的形式缓存. 代理模式: 组合,自己组合自己,装饰器相似,不过这个是增加处理步骤,装饰器..

rubikchen的博客 1150

C++中的接口设计准则

转载自:C++中的接口设计准则 1. 接口设计的重要性 软件设计就是让软件做你想做的事,软件设计一定需要接口(interface)设计,最后用C++实现。今天讨论的可能是其中最重要的一条守则,把你的接口设计得容易用对,不容易用错。 所谓的接口即你提供给用户使用你代码的途径。C++到处都充满了接口的概念,比如函数接口接口,模板接口。理想条件下,如果我们用错了接口编译器会报错,而如果编译器没有报错,那么我们用的接口就是对的。 ##2. 接口设计准则准则 2.1 要把接口设计得好用对、难用错 需要预先考虑到

Daniel Leng的博客 1552

《C++标准程序库》学习笔记(一)C++相关特性

  抱着本厚厚的《C++标准库》读了几天,想想也该写点关于用法的总结,一来怕今后容易忘记,二来将书上的事例重新敲一遍,巩固对程序库相关知识的了解。今天开第一篇,以后不固定更新。当然,笔者所读为该书为基于C++98的第一版,已有一定的年代感,不过虽然C++11的推出已有一定的时日,但是在普及上还需要一定的时间,因而,这本中文译本还是有一定的可读性的。这本书更新的版本为英文第二版,很遗憾还未出现其中文...

weixin_34014555的博客 157

接口设计

当实现实现接口后,被认为是该接口型在早期会使用常量接口接口只定义常量,而没有抽象方法,这会违反接口的定义设计组件时尽量让访问权限最小化,封装组件能够隐藏内部实现,让外部直接使用(降低耦合、组件独立、提升可重用)设计时遵守:字段私有、常量最好是不可变对象、使用方法访问字段不可变对象简单易实现、线程安全、满足原子性、可充当哈希表key,但是性能不好,每个值都需要一个对象,可以使用享元,缓存常用对象设计不可变对象遵守:字段私有、final;不被继承 final class

s44359487yad的博客 795

接口设计原则

接口是Java语言的核心,也是基本抽象单元。遵循以下的原则,可以帮助我们设计出有用、健壮灵活的接口

安静的阳光下午睡 990

《Effective Java》学习笔记--第4部分 接口设计实现最佳实践

Effective Java》共有多个章节,每个章节提供了若干条建议或最佳实践,整理了对应章节关键的实践内容,用于自己后续代码开发过程中,参考对照,自查代码习惯,从而提升输出的代码质量。《Effective Java》第4部分“接口”深入探讨了如何设计高质量的接口。在 Java 中,接口是构建程序的核心单元,良好的接口设计能够增强代码的可维护性、可扩展性可重用性。书中通过一系列的最佳实践设计建议,帮助我们构建清晰且高效的接口

weixin_37693760的博客 1677

关于接口设计的11个好习惯

当实现实现接口后,被认为是该接口型在早期会使用常量接口接口只定义常量,而没有抽象方法,这会违反接口的定义设计组件时尽量让访问权限最小化,封装组件能够隐藏内部实现,让外部直接使用(降低耦合、组件独立、提升可重用)设计时遵守:字段私有、常量最好是不可变对象、使用方法访问字段不可变对象简单易实现、线程安全、满足原子性、可充当哈希表key,但是性能不好,每个值都需要一个对象,可以使用享元,缓存常用对象设计不可变对象遵守:字段私有、final;不被继承 final class

关注菜菜的后端私房菜,获取更多技术干货~ 1106

C++接口设计原则探讨

http://www.warting.com/program/201208/53517.html 我这篇文章的主旨是介绍一部分接口的高质量设计的准则。这些准则不但应该保证设计并且实现的或者接口本身有高质量代码,而且更重要的是在工业领域应该尽可能的使代码的更新维护不影响客户的活动,主要也就是保持二进制代码兼容(binary compatibility)源代码兼容(source com

youtherzhang的博客 526

《Effective C++》条款18、19 接口设计

18、接口设计 接口设计不应该被误用 假如设计一个表示日期的,如果我们如下设计 那么当用户使用这种接口的时候很容易出现问题, 有可能会将年月的如期写反,也有可能会填写不符合常理的日期,因此这种接口设计并不是很好。 所以,为了防止用户把参数的顺序填写错误,我们可以将年月日封装成,这样当用户填写错误顺序时就将会收到编译器的抱怨。 但是其实上述的方法依然存在问题,那就是当我们不小心将月份输错,很有可能输出错误的月份,因此应该做进一步的封装,以防止用户输入错误的月份。 我们将所有的月份信息预先封装到

qq_44664843的博客 158

练习:鼠标设计之2_接口

续鼠标设计之1,前面解决了鼠标信号问题,这里解决显示问题。

jllws1的博客 724

从JavaScript到TypeScript,Pt。 IIB:使用接口混合器进行设计

Class-based design has become such an instinct that many developers can't imagine any alternative. 基于设计已经成为一种本能,许多开发人员无法想象任何替代方案。 Fortunately for that lot, ES6 adds a class keyword to simplify the...

culiu9261的博客 535

接口的高质量设计准则

本文地址:https://blog.csdn.net/seawt/article/details/3768866?utm_medium=distribute.pc_relevant_download.none-task-blog-2~default~BlogCommendFromBaidu~default-2.nonecase&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-

yuexiazhufeng的博客 207

C++接口设计原则

C++接口设计原则 这篇文章的主旨是介绍一部分接口的高质量设计的准则。这些准则不但应该保证设计并且实现的或者接口本身有高质量代码,而且更重要的是在工业领域应该尽可能的使代码的更新维护不影响客户的活动,主要也就是保持二进制代码兼容(binary compatibility)源代码兼容(source compatibility)。我希望这些准则能帮助刚从学校进入工业领域的朋友尽

我的梦想 582

Effective Java:接口设计

Efftive Java 接口 interface class 设计

qq_35066345的博客 268
上一篇: C++ 变量和函数的声明和定义
下一篇: C++中打印函数调用情况
sy8111
博客等级 码龄25年 3粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值