第六章 可以工作的类
ADT:描述类的数据与操作。
优点:可以隐藏实现细节与内部数据;可以降低耦合,改变一部分时不用改变所有代码;有助于后期提高性能;可以避免直接操作数据可能带来的粗心错误;接口可以传递更多信息;程序更具自我说明性,更可读,且可以像操作现实中的数据一样操作。
良好的接口抽象:
应该展现一致的抽象层次,一个类及其接口对应一个ADT;
要清楚抽象的对象;
要检查是否需要提供成对的服务,如class Light可能要提供开灯与关灯的服务;
假如类中有两个甚至多个不相干的独立部分,将它们拆开;
尽可能使接口可编程,即通过参数、调用关系决定它们的关系,而减少“执行a接口之前必须执行b接口”这样的软关系;
不要在修改类的设计时破坏类的抽象;
同时考虑抽象性与内聚性。
良好的封装:
尽可能在不破坏抽象完整性的前提下限制内部成员与方法的可访问性;
尽量不要暴露内部数据,尤其是那些相关联的数据;
不要暴露类的内部实现。这与cpp要求矛盾,cpp要求头文件中放置类的定义,从而暴露了内部实现。一个解决方式是将内部用一个指针指向具体实现;
不要对使用者进行假设。假如某个方法有什么前提,应该自己设定或检查;
避免使用友元。这个概念我只在cpp中接触过……c++ primer plus认为友元类没有破坏oop的特性,但本书认为友元常常会破坏良好的封装;
不要因为一个方法只调用了公共方法就将其定义为公开接口,而应该从抽象角度考虑是否公开;
要增强程序可读性;
要避免从语义上破坏封装,如要求调用方法a之前必须要求方法b,或因为知道方法b中已经调用过方法a就不再调用方法a;私以为这与“不要对用户要求过多”是一个意思,但偏向于编码时避免该问题;
留意紧密的耦合;
设计与实现
“has a” relationship:尽量用包含实现,万不得已时用继承实现;警惕包含有7-2 ~ 7+2个成员的类
“is a” relationship:用继承实现;要考虑基类内部数据是否对子类可见;继承可能很危险,所以要对继承仔细说明;子类不要取和基类私有方法同名的共有方法;把公用的接口、数据及操作尽可能放到基类;警惕只有一个实例的类、只有一个派生类的基类、覆盖了某个基类方法却没有做任何操作的子类方法;避免过深的继承关系;尽量使用多态而非类型检查;尽量让所有数据都是private。
成员数据与函数:应该尽可能少;禁止隐式地调用不该调用的方法,如赋值、构造函数;减少类调用的不同子程序地数量;对其他类子程序尽量少调用;总的来说,尽量减少类和其他类地合作关系。
构造函数:尽可能给所有数据初始化;如果要实现单件属性(singleton property),即只有一个实例的类,应该将构造函数定义为私有;优先采用深层复制(问题在于某些语言,如python java,在一些情况下默认为浅层复制);
类的优势:
为现实对象或抽象对象建模;
隐藏细节,隔离复杂度,将可能的改变限制在小范围内;
集中对特定操作的控制;
让代码可重用;
实现重构。
应该避免:万能类;无关紧要的类;抽象某个动作而非某个对象、某族动作的类。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!