Public, Protected and Private Keywords
The private keyword denotes the least stable kind of method and provides the most restricted visibility. Private methods must be called with an implicit receiver, or, inversely, may never be called with an explicit receiver.
If class Trip contains private method fun_factor, you may not send self.fun_factor from within Trip or a_trip.fun_factor from another object. However, you may send fun_factor, defaulting to self (the implicit receiver) from within instances of Trip and its subclasses.
The protected keyword also indicates an unstable method, but one with slightly different visibility restrictions. Protected methods allow explicit receivers as long as the receiver is self or an instance of the same class or subclass of self.
Thus, if Trip's fun_factor method is protected, you may always send self.fun_factor. Additionally, you may send a_trip.fun_factor, but only from within a class where self is the same kind of thing (class or subclass) as a_trip.
The public keyword indicates that a method is stable; public methods are visible everywhere.
To further complicate matters, Ruby not only provides these keywords but also supplies various mechanisms for circumventing the visibility restrictions that private and protected impose. Users of a class can redefine any method to public regardless of its initial declaration. The private and protected keywords are more like flexible barriers than concrete restrictions. Anyone can get by them, it's just a matter of expending the effort.
Therefore, any notion that you can prevent method access by using these keywords is an illusion. The keywords don't deny access, they just make it a bit harder. Using them sends two messages:
- You believe that you have better information today than programmers will have in the future.
- You believe that those future programmers need to be prevented from accidentally using a method that you currently consider unstable.
These beliefs may be correct but the future is a long way off and one never knows. The most apparently stable methods may change regularly and the most initially unstable may survive the test of time. If the illusion of control is a comfort, feel free to use the keywords. However, many perfectly competent Ruby programmers omit them and instead use comments to indicate the 'public' and 'private' parts of interfaces.
This strategy is perfectly acceptable and sometimes even preferable. Comments supply information about method stability without imposing visibility restrictions. Use of them trusts future programmers to make good choices about which methods to depend upon based on the increased information they have at that time.
Regardless of how you choose, as long as you find some way to convey this information you have fulfilled your obligations to the future.
Honor the Public Interfaces of Others
Do your best to interact with other classes using only their public interfaces. Assume that the authors of those classes were just as intentional as you are now being and they are trying desperately, across time and space, to communicate which methods are dependable. The public/private distinctions they made are intended to help you and it's best to heed them.
If your design forces the use of a private method in another class, first rethink your design. It's possible that a committed effort will unearth an alternative; you should try very hard to find one.
When you depend on a private interface you increase the risk of being forced to change. When that private interface is part of an external framework that undergoes periodic releases, this dependency is like a time bomb that will go off at the worst possible moment. Inevitably, the person who created the dependency leaves for greener pastures, the external framework gets updated, the private method being depended upon changes and the application breaks in a way that baffles current maintainers.
A dependency on a private method of an external framework is a form of technical debt. Avoid these dependencies.
Exercise Caution When Depending on Private Interfaces
Despite your best efforts you may find that you must depend on a private interface. This is a dangerous dependency that should be isolated using the techniques described in Chapter 3. Even if you cannot avoid using a private method, you can prevent the method from being referenced in many places in your application. Depending on a private interface increases risk, keep this risk to a minimum by isolating the dependency.
Construct public interfaces with an eye toward minimizing the context they require from others. Keep the what verses how distinction in mind; create public methods that allow senders to get what they want without knowing how your class implements its behavior.
Conversely, do not succumb to a class that has an ill-defined or absent public interface. When faced with a situation like that of the Mechanic class in figure 4.5, do not give up and tell it how to behave by invoking all of it's methods. Even if the original author did not define a public interface it is not too late, create one for yourself.
Depending on how often you plan to use this new public interface, it can be a new method that you define and place in the Mechanic class, a new wrapper class that you create and use instead of Mechanic or a single wrapping method that you place in your own class. Do what best suits your needs, but create some kind of defined public interface and use it. This reduces your class's context, making it easier to reuse and simpler to test.