When to use them, when to avoid them
This article explores the reasons you would want to use accessors, as well as the disadvantages to using them. This article is modified from Chapter 8 of The Object Primer 2nd Edition.
Accessors (member functions that directly manipulate the value of fields) were introduced in "Accessors increase robustness of Java code." Accessors come in two flavors: setters, which modify the value of a field, and getters, which obtain its value. Accessors do add some overhead to your code, but it is trivial — especially when compared to other factors, such as questionable database designs. In this tip I address two fundamental issues: why should you use accessor methods and when shouldn’t you?
Why use accessors?
Accessors improve the maintainability of your classes and components in the following ways:
- Providing single points of update. You have single points of update for each attribute, making it easier to modify and to test. In other words, your attributes are encapsulated.
- Controlling access to attributes. You have complete control over how attributes are accessed and by whom.
- Enabling lazy initialization of "expensive" attributes. Lazy initialization means the value of an attribute is initialized (set) when it is first accessed. The advantage of this approach is that you incur the expense of obtaining the value only when and if you need it. The main disadvantage of lazy initialization is that your code becomes more complex because you need to check to see if the attribute has been defined yet and, if not, obtain its value. Lazy initialization is typically used when an attribute is expensive to calculate or obtain (perhaps it is very large and would take significant time to transmit across the network) and when it is not always required each time the object is brought into memory.
- Reducing coupling between a subclass and its superclasses. When subclasses access inherited attributes only through their corresponding accessor methods, it is possible to change the implementation of attributes in the superclass without affecting any of its subclasses. This effectively reduces coupling between them. Accessors reduce the risk of the fragile base class problem where changes in a superclass ripple throughout its subclasses.
- Encapsulating changes to attributes. If the business rules pertaining to one or more attributes change, you can potentially modify your accessors to provide the same capability as before the change, making it easier for you to respond to the new business rules.
- Mitigating the issue of name hiding. Although you should avoid name hiding (giving local variables the same names as attributes), the use of accessors to always access attributes means you can give local variables any name you want. You don’t need to worry about hiding attribute names because you never access them directly anyway.
- Encapsulating validation logic. If you need to implement critical validation logic, such as ensuring that a deposit into a bank account is always positive, then a valid place to consider putting that logic is in the setter.
- Encapsulating undo/redo logic. If your application needs to support undo/redo logic, having accessors in place greatly facilitates implementing this functionality.
An advanced use of getters is to obtain the values of constants. Unfortunately this technique is not very common in the Java programming world. What is common is the use of
use static finals to implement "constant" values, an approach that goes against the object-oriented concept of information hiding. Encapsulating the value of constants in static getter methods instead gives you a significantly more robust solution. When those values change, or when they evolve from being a constant into a value that is calculated according to a defined set of business rules, you only need to update the value in the getter and not every line of code where the constant is used. Static fields aren’t ideal but are reasonably painless when the value of the constant isn’t likely to change — such as the values for the days of the week. However, when the constant represents a user-defined value, such as the minimum value that a mutual fund must contain, then you are at risk that this value could change and are therefore better served by implementing it as a static getter method.
When shouldn’t you use accessors?
The only time you might not want to use accessors is when execution time is of the utmost importance, but it is a rare case, indeed, that the increased coupling within your application justifies this action. In Concurrent Programming in Java (see Resources), Lea makes this case for minimizing the use of accessors: often the values of attributes in combination must be consistent and it isn’t wise to provide access to attributes singly. He is right, so don’t make all your accessors public! In this situation, I would implement a bulk setter method, a method that would update several attributes at once, validating their business values appropriately, and invoke private setter methods that update a single value. I think Lea has missed the point that you don’t need to make all accessor methods public, as I pointed out in "Java accessor visibility." As an aside, bulk setter methods are quite common for EJB 1.x development efforts to counteract the standard, and performance degrading, approach to persistence. In short, when the values of some attributes depend on one another, then you should introduce methods that do the "right thing" and make the appropriate accessor methods protected or private as needed.
Special thanks to Chris Roffler, Senior EJB Architect at Ronin International, for his insights into this tip.