CSC/ECE 517 Fall 2009/wiki3 19 ee

From Expertiza_Wiki
Jump to navigation Jump to search

Uniform Access Principle

The Uniform Access Principle states that any attribute of a module (usually a class) should be accessed with the same syntax, regardless of the nature of the data. Simply put, when a consumer reads or writes a value, they should not know (or care) weather they accessing a variable or invoking a method. Thus, no matter how the module internally handles the data, externally the information is always accessed uniformly.

Origin of the Uniform Access Principle

The Uniform Access Principle was created by the French engineer Bertrand Meyer. Meyer is known to be a strong proponent of object oriented programming, and in fact was one of the earliest and most outspoken champions of OO. His book Object-Oriented Software Construction, first published in 1988, is considered to be a foundational publication of the object oriented programming movement.[1] It was in this book that he put forth the uniform access principal by stating "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."[2]

Advantages of the Uniform Access Principle

There are many advantages associated with using the Uniform Access Principal when writing software.

Reading Properties

Reading values (or getting) from an object is a syntactic cinch in code that uses the Uniform Access Principal. Just refer to the field that holds the relevant data and the information is accessible. This is a very simple way of interfacing with an external class.

Ease of Understanding

Accessing data from a class can be confusing if the user does not know how the data is stored. For example, in C# it is often hard to remember how to access the number of items in a collection - is it collection.Count or collection.Count()? It turns out that the syntax is collection.Count for a generic collection, but it is collection.Count() for an enumerable collection.[3]

This difference in syntax can be confusing to a user trying to access the data within an otherwise opaque object. If this data was uniformly accessible, it would be much less confusing.

Convenience of Interface

Similarly, it is convenient for a user to not have to remember details of an objects implementation to access data within the object. If the Uniform Access Principal is exercised, the programmer does not need to keep track of which values are computed in methods and which are just attributes. A user can simply access the data named what they are interested in.

Maintainability of Interface

Utilizing the Uniform Access Principal also aids in maintaining (reads 'not breaking') existing interfaces. For example, lets say there was a BankAccount class. That BankAccount class has an attribute called "Balance", which simply contains the balance value for that account. But later, the policy of the BankAccount changes, such that the balance is not a stored value, but must be computed by adding multiple values together.

In implementations that do not support the Uniform Access Principal, the accessing of that data could go from bankAcnt.Balance to bankAcnt.GetBalance(), thus 'breaking' any code that was depending on the balance property.

However, with an implementation that utilizes the Uniform Access Principal, this change will not be a problem. Be it a stored value, or a value computed in a method call, the accessing of this data could always be done with the same syntax.

Disadvantages of the Uniform Access Principle

Although it is a long-held tenet of good object oriented design, there can be some drawbacks to using the Uniform Access Principal.

Writing Properties

Setter methods are much easier to implement than setting a field that could be an attribute or could be associated with a method. Because of this inherent ambiguity, the language will probably not be able to determine how to handle a field being set on its own. The only good and consistent solution is to enforce the rule that the designer must specify how to handle an assignment for each method that is written. This can be burdensome on the original designer.[4]

Performance Issues

When retrieving data from a class that utilizes the Uniform Access Principal, it could potentially mask or obfuscate a performance hit the user was not anticipating. Consider a scenario where a piece of code needs to find the highest temperature value from a collection of many years worth of readings. A non-uniform access implementation might read something like weather.getHighestTempFromAllReadings(). An implementation using the Uniform Access Principal would likely look more like "weather.HighTemp".

The second option makes it appear that the highest temperature is just a value kept track of in the object, when in fact the data is off in a massive collection that must be searched. The user might be surprised that "weather.HighTemp" takes a long time to return a value. However, to a user invoking "weather.getHighestTempFromAllReadings()" it should be apparent that the method call is busy searching through millions of records and will return a value once it is found.

Class Maintenance

Maintaining code using the Uniform Access Principle might be difficult, especially if a different programmer originally designed the module. It would be unclear to the maintainer which values are computed and which are just stored. Whereas, in implementations where methods and attributes are clearly different, it would be easier to understand what was what.

Language Restrictions

It is actually impossible to utilize the Uniform Access Principle in some languages. For example, in Java fields and methods are kept in different namespaces. This means that fields and methods can have the same names. And because they are not restricted by these names, a field named myValue is completely independent from a method named myValue().

Code Examples

Ruby

class Foo
  attr_reader :x
  def initialize(x)
    @x = x
  end
  def squared_x
    return @x * @x
  end
end

y = Foo.new(2)
puts y.x
puts y.squared_x

This outputs:

2
4

References