• Aucun résultat trouvé

Accessing Instance Variables

Dans le document The Art and Science of Smalltalk (Page 156-159)

A matter of style which is sometimes hotly debated among Smalltalkers concerns the accessing of instance variables. If a class defines or inherits instance variables, those variables can be accessed in the methods defined on the class simply by naming the variable. This applies both for assigning values to the variable, and using the variable's value in expressions. For example, the following expression is legal within a method of a class which has instance variables called area, width and height:

area := width * height.

Typically, a programmer will also define accessing methods, which permit other objects to access the variables by sending a message. In this case the instance variables above could be accessed from outside their own object using expressions like:

MyObject area.

HyObject width: 25.

It is often suggested however, that an object should also access its own instance variables by sending messages to itself, and not by directly naming them. In this case, the earlier method fragment would perhaps become something like this (with parentheses added for clarity):

self area: (self width) * (self height).

Provided the methods area: , width and height are defined correctly (to do nothing more than access the variables), this expression has exactly the same effect as the first one. So why would anyone want to do this? The answer is that by avoiding direct access to instance variables, you can potentially increase flexibility and as a result enhance reusability. But how?

Instance variables really represent properties of an object.

Specifically, they are properties whose actual values are held (or cached) in the object, rather than being computed as required.

However, what starts life as an instance variable, may later need to become a computed value. Now if that instance variable is only accessed via a method, this becomes a simple task of replacing the accessing method with one of the same name which computes the value in some arbitrarily complex way instead of simply returning it. If the variable has been accessed by naming it directly, the situation is more complicated. A new method must be created, and every single reference to the variable throughout the class and all its subclasses must be changed to messages invoking the new method.

This process gets especially complicated and messy if the variable is defined in a superclass, and you want to make it a computed value in a subclass. Unless you go and modify the superclass (which might not be allowed), you have to over-ride in your subclass all the methods of the superclass which access the variable, replacing all references to the variable with a message expression. If the original writer of the superclass had used accessing messages in the first place, you'd only have to over-ride the accessing methods in your subclass.

Looking back at the above example, we can imagine that there is an instance variable called area. We might define an accessing method which simply returns the value of the area variable:

area

^rea.

Now, provided we use it consistently in all the rest of our code, then when we later want (either in a subclass, or a later version of the same class) to replace the notion of *area' with a computed value instead of a cached one, we simply redefine the method area, perhaps as follows:

Coding in Smalltalk

area.

A(width * height).

Now it doesn't matter how many references there are to area. Because they are message expressions and not variable references they will automatically access the new computed value. What's more, if the change from a variable to a computed value did happen in a subclass, then because of the way method lookup works, references to area in instances of the superclass would continue to invoke the old method (returning the value of the instance variable), whilst instances of the subclass would invoke the new method (returning the computed value).

So much for the claimed benefits of accessing instance variables only through messages. What are the disadvantages? The most obvious disadvantage is performance. Accessing your own instance variables via a message is necessarily slower than directly referencing them. The accessing message does exactly the same variable reference as you would have done, to which the time for a message pass must be added.

It is up to you to decide whether this has any impact at all on the performance of your code, and if so whether that impact is a price worth paying for the advantages we've just discussed. You could use the profiler provided as part of the VisualWorks Advanced Programming ObjectKit to decide which variables to refer to directly and which to use accessing methods for. You might also be able to improve your use of accessing messages (for example by caching a value in a temporary variable inside a loop instead of accessing it repeatedly).

Variable accesses via message expressions are also less readable than direct references. But in a sense, that's part of the point. The idea is to isolate the user of a value from the implementation of that value as an instance variable or a computed value. Again, it's a matter of judgement and personal opinion as to whether clarity of code, or

encapsulation is most important.

Finally, having to define accessing methods even for supposedly 'private' variables (variables which you don't intend to be accessed from outside instances of the class they're defined or inherited in) may make you uncomfortable. Remember though that nothing is really private in Smalltalk. All you can do is flag your intentions by naming a protocol correctly (for example private-accessing). Ultimately, someone else could always define their own accessing method on your class, or use the instVarAt; method defined on object. (Have a browse to see what this method does, but never use it unless you want to be branded as a 'hacker'!)

Chapter 13

Dans le document The Art and Science of Smalltalk (Page 156-159)