警惕从语义上破坏封装性

如果要看底层实现才能理解发生的事情那算不上抽象 -- P.J Plauger

避免语法错误的同时,还需要注意语义,因为大多数错误比语法错误更难于诊断和更正。

语义上的封装性和语法上的封装性二者的难度相差无几。从语法角度来说,要想避免窥探另一个类的内部实现细节,只要把它的内部的子程序和数据都声明为 private 就可以了,这是相对容易办到的。然而想要表达语义上的封装性就完全是另一码事了。下面是一些类的调用方式代码从语义上破坏其封装性的例子。

  • 不去调用 A 类的 InitalizeOperations() 子程序,因为你知道 A 类的 PerformFirstOperations() 子程序会自动调用它。
  • 不在调用 employee.Retrive(database) 之前调用 database.Connect() 子程序,因为你知道在未建立数据库连接的时候 employee.Retrieve() 会去连接数据库的。
  • 不去调用 A 类的 Terminate() 子程序,因为你知道 A 类的 PerformFinalOpetation() 子程序已经调用过它了。
  • 即使在ObjectA 离开作用域之后,你任去使用由 ObjectA 创建的、指向 ObjectB 的指针或引用, 因为你知道 ObjcetA 把 ObjectB 放置在静态存储空间中了,因此 ObjectB 肯定还可以用。
  • 使用 ClassB.MAXIMUM_ELEMENTS 而不用 ClassA.MAXIMUM_ELEMENTS 因为你知道它们两个的值是相等的。

上面这些例子的问题在于,它们让调用方代码不是依赖于类的公开接口,而是依赖于类的私有实现。每当你发现自己是通过查看类的内部实现来得知盖如何使用这个类的时候,你就不是在针对接口编程了,而是在透过接口针对内部实现编程类。如果你透过接口来编程的话,封装性就被破坏了,而一旦封装性开始遭到破坏,抽象能力也就快遭殃了。

如果仅仅根据类的接口文档还是无法得知如何使用一个类的话,正确的做法不是拉出这个类的源代码,从中查看其内部实现。这是个好的初衷,但却是个错误的决断。正确的做法应该是去联系类的作者,告诉他“我不知道该怎么用这个类。”而对于类的作者来说,正确的做法不是面对面地告诉你答案,而是从代码库中check out(签出)类的接口文件,修改类的接口文档,再把文件check in(签入)回去,然后告诉你“看看现在你知不知道该怎么用它了。”你希望让这一次对话出现在接口代码里,这样就能留下来让以后的程序员也能看到。你不希望让这一次对话只存在于自己的脑海里,这样会给使用该类的调用方代码烙下语义上的微妙依赖性。你也不想让这一次对话只在个人之间进行,这样只能让你的代码获益,而对其他人没有好处。




摘录:

《代码大全2》 P142

posted @   double64  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2021-09-08 塑料的定义与分类
点击右上角即可分享
微信分享提示