All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation. (BertrandMeyer in ObjectOrientedSoftwareConstruction)
The EiffelLanguage satisfies this principle: In the expression
my_account.balancethe notation doesn't reveal how the feature is implemented. It could be a read only attribute or a function. Thus, changing the implementation from computation to storage or vice versa won't impact the call from a client of ACCOUNTs.
The RubyLanguage also satisfies UAP, for both setting and getting values. You can only call methods on objects and thus any access to an object variable is actually a method call that is performing the setting/getting action whether it is simple storage or computation. Setters are like:
journey_plan.title = "South West Journey_plan"This is actually saying:
journey_plan.title=("South West Journey_plan") which is a call to the title=() method on the journey_plan objectGetters are like:
journey_plan.titlewhich is actually saying:
journey_plan.title(), a call to the method title() on the journey_plan objectThe SelfLanguage was at least partly constructed around this principle. In Self, objects have 'slots' which can hold values (be variables) or code (be functions).
The PythonLanguage allows attributes to hold property descriptors that make function calls look like simple setters and getters. If it were important to make an attribute behave like a function in terms of call syntax, this could be done by putting a callable object in the attribute.
In SmalltalkLanguage, UAP is not supported. But since variables are local to objects, you only have to worry about the difference within that object's methods, and nowhere else. By insisting that every variable access (even local ones) be through a getter/setter, UAP can be supported in Smalltalk. This is also extraordinarily helpful if the object in question is persistent, and therefore subject to various db synchronization/transaction issues.
In JavaLanguage, UAP is not natively supported (because instance variables and functions are syntactically distinct) but is naturally brought by every java programmer (because not having read only access to instance variable, you'll have to write getter functions anyway).
The Java convention (which I studiously avoid) breaks UAP by insisting that accessor methods include a get/set prefix. This means that a java slot named "balance" that computes it every time might be called "balance()" a cached version would be called "getBalance()". I think it's better to name the function "balance()" and let the compiler figure out (through method overloading) whether it's a getter or setter. This restores the UAP by convention.[Anon1]
Also found in SmalltalkLanguage, where one uses the at: message to retrieve elements from a container by key.
ForthLanguage values and constants obey this principle. You can even obtain execution tokens for them for deferred execution.
There are issues with this, as computation of a value and retrieval of storage can have different semantics beyond implementation:
It should also be noted that Meyer's advice only ought to apply to external clients - while a = y.get_foo() or a = y.get(foo) ought to be preferred over a = y.foo (using C/C++/Java syntax), inside the implementation of an object direct getting and setting need not be discouraged. (And at some point, you will need a language primitive for retrieving an element from a record.) Even inside an implementation, scattering direct get/set references makes subsequent variable refactoring much more tedious.
It should further be noted that CommonLisp gives you both UniformAccessPrinciple and specialized interfaces for different datastructures. All of the Lisp datatypes - lists, arrays, association lists, hash tables, vectors, et al - can be accessed with 'elt'. Additionally, they each have their own accessor functions which typically offer better performance than the generic 'elt'. Arrays have 'aref'. Simple vectors have 'svref'. Hash tables have 'gethash'. Strings have 'char'.
Thus, in cases where these subtle distinctions are significant to the client, they can use a type-specific access function. In other cases, they can use the uniform accessor.
Although the idea carries a nice idealism, one thing that bothers me about it is that it hides the cost of getting the result. An example from my website is the difference between what would otherwise be Florida.voteCount and Florida.countVotes(). One may take 3 days to calculate and the other is just referencing a pre-calculated attribute. If we have to reference that information multiple times in a given algorithm, the calculated version would bankrupt the machine. There are also advantages to isolating what is data and behavior that help with DivideAndConquer and multi-language info sharing, but I think we've had this debate before. I'll link it here when/if I encounter it again. -- top
Seems to be related to AccessOrientedProgramming
BertrandMeyer has a new article on this topic http://www.eiffel.com/general/monthly_column/2005/Sept_October.html. Seems to talk about the simplicity of allowing setter methods and value set to be truly the same, and a new compromise.
CategoryObjectOrientation (because that is the category of the quoted book at the top of the page)