Uniform access principle

Last updated

The uniform access principle of computer programming was put forth by Bertrand Meyer (originally in his book Object-Oriented Software Construction ). It states "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." [1] [2] This principle applies generally to the syntax of object-oriented programming languages. In simpler form, it states that there should be no syntactical difference between working with an attribute, pre-computed property, or method/query of an object.

Contents

While most examples focus on the "read" aspect of the principle (i.e., retrieving a value), Meyer shows that the "write" implications (i.e., modifying a value) of the principle are harder to deal with in his monthly column on the Eiffel programming language official website. [3]

Explanation

The problem being addressed by Meyer involves the maintenance of large software projects or software libraries. Sometimes when developing or maintaining software it is necessary, after much code is in place, to change a class or object in a way that transforms what was simply an attribute access into a method call. Programming languages often use different syntax for attribute access and invoking a method, (e.g., object.something versus object.something()). The syntax change would require, in popular programming languages of the day, changing the source code in all the places where the attribute was used. This might require changing source code in many different locations throughout a very large volume of source code. Or worse, if the change is in an object library used by hundreds of customers, each of those customers would have to find and change all the places the attribute was used in their own code and recompile their programs.

Going the reverse way (from method to simple attribute) really was not a problem, as one can always just keep the function and have it simply return the attribute value.

Meyer recognized the need for software developers to write code in such a way as to minimize or eliminate cascading changes in code that result from changes which convert an object attribute to a method call or vice versa. For this he developed the Uniform Access Principle.

Many programming languages do not strictly support the UAP but do support forms of it. Properties, which are provided in a number of programming languages, address the problem Meyer was addressing with his UAP in a different way. Instead of providing a single uniform notation, properties provide a way to invoke a method of an object while using the same notation as is used for attribute access. The separate method invocation syntax is still available.

UAP example

If the language uses the method invocation syntax it may look something like this.

// Assume print displays the variable passed to it, with or without parens // Set Foo's attribute 'bar' to  value 5. Foo.bar(5) print Foo.bar() 

When executed, should display :

5 

Whether or not Foo.bar(5) invokes a function or simply sets an attribute is hidden from the caller. Likewise whether Foo.bar() simply retrieves the value of the attribute, or invokes a function to compute the value returned, is an implementation detail hidden from the caller.

If the language uses the attribute syntax the syntax may look like this.

Foo.bar = 5 print Foo.bar 

Again, whether or not a method is invoked, or the value is simply assigned to an attribute is hidden from the calling method.

Problems

However, UAP itself can lead to problems, if used in places where the differences between access methods are not negligible, such as when the returned value is expensive to compute or will trigger cache operations. [2]

Language examples

Python

Python properties may be used to allow a method to be invoked with the same syntax as accessing an attribute. Whereas Meyer's UAP would have a single notation for both attribute access and method invocation (method invocation syntax), a language with support for properties still supports separate notations for attribute and method access. Properties allow the attribute notation to be used, but to hide the fact that a method is being invoked instead of simply retrieving or setting a value.

As such, Python leaves the option of adherence to UAP up to the individual programmer. The built-in @property function provides a simple way to decorate any given method in attribute access syntax, thus abstracting away the syntactical difference between method invocations and attribute accesses. [4]

In Python, we may have code that access an Egg object that could be defined such that weight and color are simple attributes as in the following

""">>> egg = Egg(4.0, "white")>>> egg.color = "green">>> print(egg)Egg(4.0, green)"""classEgg:def__init__(self,weight,color)->None:self.weight=weightself.color=colordef__str__(self)->str:returnf"{__class__.__name__}({self.weight}, {self.color})"

Or the Egg object could use properties, and invoke getter and setter methods instead

# ...(snip)...classEgg:def__init__(self,weight_oz:float,color_name:float)->None:self.weight=weight_ozself.color=color_name@propertydefcolor(self)->str:'''Color of the Egg'''returnto_color_str(self._color_rgb)@color.setterdefcolor(self,color_name:str)->None:self._color_rgb=to_rgb(color_name)@propertydefweight(self)->float:'''Weight in Ounces'''returnself._weight_gram/29.3@weight.setterdefweight(self,weight_oz:float)->None:self._weight_gram=29.3*weight_oz# ...(snip)...
Snipped codes are as following:
importwebcolors# class Egg:defto_color_str(rgb:webcolors.IntegerRGB)->str:try:returnwebcolors.rgb_to_name(rgb)exceptValueError:returnwebcolors.rgb_to_hex(rgb)defto_rgb(color_name:str)->webcolors.IntegerRGB:try:returnwebcolors.name_to_rgb(color_name)exceptValueError:returnwebcolors.hex_to_rgb(color_name)if__name__=="__main__":importdoctestdoctest.testmod()

Regardless of which way Egg is defined, the calling code can remain the same. The implementation of Egg can switch from one form to the other without affecting code that uses the Egg class. Languages which implement the UAP have this property as well.

Ruby

Consider the following

y=Egg.new("Green")y.color="White"putsy.color

Now the Egg class could be defined as follows

classEggattr_accessor:colordefinitialize(color)@color=colorendend

The above initial code segment would work fine with the Egg being defined as such. The Egg class could also be defined as below, where color is instead a method. The calling code would still work, unchanged if Egg were to be defined as follows.

classEggdefinitialize(color)@rgb_color=to_rgb(color)enddefcolorto_color_name(@rgb_color)enddefcolor=(color)@rgb_color=to_rgb(color)endprivatedefto_rgb(color_name).....enddefto_color_name(color)....endend

Note how even though color looks like an attribute in one case and a pair of methods in the next, the interface to the class remains the same. The person maintaining the Egg class can switch from one form to the other without fear of breaking any caller's code. Ruby follows the revised UAP, the attr_accessor :color only acts as syntactic sugar for generating accessor/setter methods for color. There is no way in Ruby to retrieve an instance variable from an object without calling a method on it.

Strictly speaking, Ruby does not follow Meyer's original UAP in that the syntax for accessing an attribute is different from the syntax for invoking a method. But here, the access for an attribute will always actually be through a function which is often automatically generated. So in essence, either type of access invokes a function and the language does follow Meyer's revised Uniform Access Principle.

C#

The C# language supports class properties, which provide a means to define get and set operations (getters and setters) for a member variable. The syntax to access or modify the property is the same as accessing any other class member variable, but the actual implementation for doing so can be defined as either a simple read/write access or as functional code.

publicclassFoo{privatestring_name;// PropertypublicintSize{get;// Getterset;// Setter}// PropertypublicstringName{get{return_name;}// Getterset{_name=value;}// Setter}}

In the example above, class Foo contains two properties, Size and Name. The Size property is an integer that can be read (get) and written (set). Similarly, the Name property is a string that can also be read and modified, but its value is stored in a separate (private) class variable _name.

Omitting the set operation in a property definition makes the property read-only, while omitting the get operation makes it write-only.

Use of the properties employs the UAP, as shown in the code below.

publicFooCreateFoo(intsize,stringname){varfoo=newFoo();foo.Size=size;// Property setterfoo.Name=name;// Property setterreturnfoo;}

C++

C++ has neither the UAP nor properties, when an object is changed such that an attribute (color) becomes a pair of functions (getA, setA). Any place in that uses an instance of the object and either sets or gets the attribute value (x = obj.color or obj.color = x) must be changed to invoke one of the functions. (x = obj.getColor() or obj.setColor(x)). Using templates and operator overloading, it is possible to fake properties, but this is more complex than in languages which directly support properties. This complicates maintenance of C++ programs. Distributed libraries of C++ objects must be careful about how they provide access to member data.

JavaScript

JavaScript has had support for computed properties since 2009. [5]

Related Research Articles

Eiffel is an object-oriented programming language designed by Bertrand Meyer and Eiffel Software. Meyer conceived the language in 1985 with the goal of increasing the reliability of commercial software development. The first version was released in 1986. In 2005, the International Organization for Standardization (ISO) released a technical standard for Eiffel.

In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

In computer science, reflective programming or reflection is the ability of a process to examine, introspect, and modify its own structure and behavior.

<span class="mw-page-title-main">Apache Groovy</span> Programming language

Apache Groovy is a Java-syntax-compatible object-oriented programming language for the Java platform. It is both a static and dynamic language with features similar to those of Python, Ruby, and Smalltalk. It can be used as both a programming language and a scripting language for the Java Platform, is compiled to Java virtual machine (JVM) bytecode, and interoperates seamlessly with other Java code and libraries. Groovy uses a curly-bracket syntax similar to Java's. Groovy supports closures, multiline strings, and expressions embedded in strings. Much of Groovy's power lies in its AST transformations, triggered through annotations.

In computer programming, a function object is a construct allowing an object to be invoked or called as if it were an ordinary function, usually with the same syntax. In some languages, particularly C++, function objects are often called functors.

In object-oriented programming, a metaclass is a class whose instances are classes themselves. Unlike ordinary classes, which define the behaviors of objects, metaclasses specify the behaviors of classes and their instances. Not all object-oriented programming languages support the concept of metaclasses. For those that do, the extent of control metaclasses have over class behaviors varies. Metaclasses are often implemented by treating classes as first-class citizens, making a metaclass an object that creates and manages these classes. Each programming language adheres to its own metaobject protocol, which are the rules that determine interactions among objects, classes, and metaclasses. Metaclasses are utilized to automate code generation and to enhance framework development.

In some programming languages, eval, short for evaluate, is a function which evaluates a string as though it were an expression in the language, and returns a result; in others, it executes multiple lines of code as though they had been included instead of the line including the eval. The input to eval is not necessarily a string; it may be structured representation of code, such as an abstract syntax tree, or of special type such as code. The analog for a statement is exec, which executes a string as if it were a statement; in some languages, such as Python, both are present, while in other languages only one of either eval or exec is.

In class-based, object-oriented programming, a constructor is a special type of function called to create an object. It prepares the new object for use, often accepting arguments that the constructor uses to set required member variables.

In computer science, a mutator method is a method used to control changes to a variable. They are also widely known as setter methods. Often a setter is accompanied by a getter, which returns the value of the private member variable. They are also known collectively as accessors.

this, self, and Me are keywords used in some computer programming languages to refer to the object, class, or other entity which the currently running code is a part of. The entity referred to thus depends on the execution context. Different programming languages use these keywords in slightly different ways. In languages where a keyword like "this" is mandatory, the keyword is the only way to access data and methods stored in the current object. Where optional, these keywords can disambiguate variables and functions with the same name.

<span class="mw-page-title-main">Oxygene (programming language)</span> Object Pascal-based programming language

Oxygene is a programming language developed by RemObjects Software for Microsoft's Common Language Infrastructure, the Java Platform and Cocoa. Oxygene is based on Delphi's Object Pascal, but also has influences from C#, Eiffel, Java, F# and other languages.

<span class="mw-page-title-main">Python syntax and semantics</span> Set of rules defining correctly structured programs

The syntax of the Python programming language is the set of rules that defines how a Python program will be written and interpreted. The Python language has many similarities to Perl, C, and Java. However, there are some definite differences between the languages. It supports multiple programming paradigms, including structured, object-oriented programming, and functional programming, and boasts a dynamic type system and automatic memory management.

A property, in some object-oriented programming languages, is a special sort of class member, intermediate in functionality between a field and a method. The syntax for reading and writing of properties is like for fields, but property reads and writes are (usually) translated to 'getter' and 'setter' method calls. The field-like syntax is easier to read and write than many method calls, yet the interposition of method calls "under the hood" allows for data validation, active updating, or implementation of what may be called "read-only fields".

In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). The term was coined in 2005 by Eric Evans and Martin Fowler.

This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.

Nemerle is a general-purpose, high-level, statically typed programming language designed for platforms using the Common Language Infrastructure (.NET/Mono). It offers functional, object-oriented, aspect-oriented, reflective and imperative features. It has a simple C#-like syntax and a powerful metaprogramming system.

Objective-C is a high-level general-purpose, object-oriented programming language that adds Smalltalk-style message passing (messaging) to the C programming language. Originally developed by Brad Cox and Tom Love in the early 1980s, it was selected by NeXT for its NeXTSTEP operating system. Due to Apple macOS’s direct lineage from NeXTSTEP, Objective-C was the standard language used, supported, and promoted by Apple for developing macOS and iOS applications from 1997, when Apple purchased NeXT until the introduction of the Swift language in 2014.

In object-oriented programming, method cascading is syntax which allows multiple methods to be called on the same object. This is particularly applied in fluent interfaces.

In object-oriented programming, the safe navigation operator is a binary operator that returns null if its first argument is null; otherwise it performs a dereferencing operation as specified by the second argument.

The syntax of the Ruby programming language is broadly similar to that of Perl and Python. Class and method definitions are signaled by keywords, whereas code blocks can be defined by either keywords or braces. In contrast to Perl, variables are not obligatorily prefixed with a sigil. When used, the sigil changes the semantics of scope of the variable. For practical purposes there is no distinction between expressions and statements. Line breaks are significant and taken as the end of a statement; a semicolon may be equivalently used. Unlike Python, indentation is not significant.

References

  1. Meyer, Bertrand (1997). Object-Oriented Software Construction (second ed.). Prentice Hall. p. 57. ISBN   978-0-13-629155-8.
  2. 1 2 "The UniformAccessPrinciple". c2 wiki. Retrieved 6 August 2013.
  3. Meyer, Bertrand. "EiffelWorld Column: Business plus pleasure" . Retrieved 6 August 2013.
  4. Official Python Docs, built-in functions
  5. w3schools.com, Javascript Accessors