In any relationship it’s important to have boundaries that are respected by all parties involved. When you create a library, you establish a relationship with the user of that library—the client programmer—who is another programmer, but one using your library to build an application or a bigger library.
Without rules, client programmers can do anything they want with all the members of a class, even if you might prefer they don’t directly manipulate some of the members. Everything’s naked to the world. This chapter looked at how classes are built to form libraries: first, the way a group of classes is packaged within a library, and second, the way the class controls access to its members. It is estimated that a C programming project begins to break down somewhere between 50K and 100K lines of code because C has a single namespace, and names begin to collide, causing extra management overhead. In Java, the package keyword, the package naming scheme, and the import keyword give you complete control over names, so the issue of name collision is easily avoided. There are two reasons for controlling access to members. The first is to keep users’ hands off portions that they shouldn’t touch. These pieces are necessary for the internal operations of the class, but not part of the interface that the client programmer needs. So making methods and fields private is a service to client programmers, because they can easily see what’s important to them and what they can ignore. It simplifies their understanding of the class. The second and most important reason for access control is to allow the library designer to change the internal workings of the class without worrying about how it will affect the client programmer. You might, for example, build a class one way at first, and then discover that restructuring your code will provide much greater speed. If the interface and implementation are clearly separated and protected, you can accomplish this without forcing client programmers to rewrite their code. Access control ensures that no client programmer becomes dependent on any part of the underlying implementation of a class.
When you have the ability to change the underlying implementation, you not only have the freedom to improve your design, you also have the freedom to make mistakes. No matter how carefully you plan and design, you’ll make mistakes. Knowing that it’s relatively safe to make these mistakes means you’ll be more experimental, you’ll learn more quickly, and you’ll finish your project sooner. The public interface to a class is what the user does see, so that is the most important part of the class to get “right” during analysis and design. Even that allows you some leeway for change. If you don’t get the interface right the first time, you can add more methods, as long as you don’t remove any that client programmers have already used in their code.
Notice that access control focuses on a relationship—and a kind of communication—between a library creator and the external clients of that library. There are many situations where this is not the case. For example, you are writing all the code yourself, or you are working in close quarters with a small team and everything goes into the same package. These situations have a different kind of communication, and rigid adherence to access rules may not be optimal. Default (package) access may be just fine.
对于任何关系,最重要的一点都是规定好所有方面都必须遵守的界限或规则。创建一个库时,相当于建立了同那个库的用户(即“客户程序员”)的一种关系——那些用户属于另外的程序员,可能用我们的库自行构建一个应用程序,或者用我们的库构建一个更大的库。如果不制订规则,客户程序员就可以随心所欲地操作一个类的所有成员,无论我们本来愿不愿意其中的一些 成员被直接操作。所有东西都在别人面前都暴露无遗。
本章讲述了如何构建类,从而制作出理想的库。首先,我们讲述如何将一组类封装到一个库里。其次,我们讲述类如何控制对自己成员的访问。一般情况下,一个 C 程序项目会在 50K 到100K 行代码之间的某个地方开始中断。这是由于 C 仅有一个“命名空间”,所以名字会开始互相抵触,从而造成额外的管理开销。而在 Java 中,package 关键字、包命名方案 以及import 关键字为我们提供对名字的完全控制,所以命名冲突的问题可以很轻易地得到避免。
有两方面的原因要求我们控制对成员的访问。第一个是防止用户接触那些他们不应碰的工具。对于数据类型的内部机制,那些工具是必需的。但它们并不属于用户接口的一部分,用户不必用它来解决自己的特定问题。所以将方法和字段变成“私有”(private)后,可极大方便用户。因为他们能轻易看出哪些对于自己来说是最重要的,以及哪些是自己需要忽略的。这样便简化了用户对一个类的理解。
进行访问控制的第二个、也是最重要的一个原因是:允许库设计者改变类的内部工作机制,同时不必担心它会对客户程序员产生什么影响。最开始的时候,可用一种方法构建一个类,后来发现需要重新构建代码,以便达到更快的速度。如接口和实施细节早已进行了明确的分隔与保护,就可以轻松地达到自己的目的,不要求用户改写他们的代码。 利用 Java 中的访问指示符,可有效控制类的创建者。那个类的用户可确切知道哪些是自己能够使用的,哪些则是可以忽略的。但更重要的一点是,它可确保没有任何用户能依赖一个类的基础实施机制的任何部分。作为一个类的创建者,我们可自由修改基础的实施细节,这一改变不会对客户程序员产生任何影响,因为他们不能访问类的那一部分。
有能力改变基础的实施细节后,除了能在以后改进自己的设置之外,也同时拥有了“犯错误”的自由。无论当初计划与设计时有多么仔细,仍然有可能出现一些失误。由于知道自己能相当安全地犯下这种错误,所以可以放心大胆地进行更多、更自由的试验。这对自己编程水平的提高是很有帮助的,使整个项目最终能更快、更好地完成。一个类的公共接口是所有用户都能看见的,所以在进行分析与设计的时候,这是应尽量保证其准确性的最重要的一个部分。但也不必过于紧张,少许的误差仍然是允许的。若最初设计的接口存在少许问题,可考虑添加更多的方法,只要保证不删除客户程序员已在他们的代码里使用的东西。
参考: Think In Java