Contents Previous Next Index
9 Object Oriented Programming
9.1 In This Chapter
A brief introduction to Object Oriented Programming will be presented.
A description of how Object Oriented Programming is implemented in Maple.
How to override operators and engine routines using Objects.
9.2 Introduction to Object Oriented Programming
Objects are a programming tool that allows data and procedures to be encapsulated together. For example, an object could be created to represent a car. A car object might have variables to track its position, velocity and steering position. The car object might also have procedures to accelerate the car and to adjust the steering. A further procedure could be implemented to update the car's position and velocity based on the current acceleration, velocity and steering. Multiple car objects could be used to represent multiple cars, each with their own positions and velocities, but sharing the same procedures for how the cars move.
Objects can also restrict access to certain variables and procedures. For example the car object would allow other code to call a routine to adjust the steering, but may not allow external code to set the value of the steering variable directly. Although this may seem restrictive, it allows the object to control its internal state. In the car example, it could limit the range of steering.
The variables in an object that store the data and procedures are referred to as the object's members. Procedures associated with an object are called methods. Object members have access controls which limit where the members can be accessed from, similar to modules. Members declared exported can be accessed from anywhere. Members declared local can only be accessed from within the object's methods. Objects are instances of a class. A class describes the exports and locals that each instance of the class (the objects) will have.
Benefits of Object Oriented Programming
Benefits of object oriented programming are:
The implementation of a class can be changed radically without changing the interface of exported methods. Thus code that uses the objects will not need to change when the internal implementation changes.
As objects are self contained, they can be reused.
Objects can define methods that allow them to integrate with existing Maple routines. Thus users can create objects that can be used like built-in types.
A set of classes can implement a common set of exports. Thus a procedure that uses only the common exports will work with any objects from any of the classes without needing to know which classes the objects belong to.
Good object oriented design can be difficult. In particular, identifying which concepts should be represented as objects can be tricky. A good rule of thumb is that objects should be your "nouns" and methods should be "verbs". Thus you would create an object to represent a car and you call a method to accelerate the car.
9.3 Objects in Maple
Creating a New Class of Objects
To create a new class of objects, use the named module declaration syntax with option object.
This will create a new object and assign the new object to the module name (NewObject in the example above). An object created this way will be referred to as a prototype object. In Maple, any object (prototype or other) can be used as a representative of the class.
When declaring an object the members must be declared as either local to the object, using a local declaration or exported, using an export declaration. A member that is declared local can only be accessed from the object's methods, or other object's methods of the same class. A member that is exported can be accessed anywhere.
By default, the values assigned to the object's members are unique to the object. That is, two objects of the same class can have different values assigned to their members. However some members, member procedures in particular, are shared among all objects of a class. Thus members can also be declared as static. A static member stores only one value that is common to all objects of a class.
Creating More Objects
Once a prototype object exists, it can be used to create new objects using the Object routine. The Object routine creates a new object of the same class as the object passed into Object.
newObj := Object( existingObject );
By default, the newly created object will have its members assigned the same values as the object passed to Object. However by implementing a ModuleCopy routine, the object can perform different actions when new instances are created. A ModuleCopy routine can accept additional arguments that are passed into the Object routine.
newObj := Object( existingObject, arg1, arg2, ... );
Objects and Types
All objects are of type object. In addition type and :: can be used to determine if an object is an instance of a particular class by passing an object of the expected class as the type. You can refine this type checking by defining the ModuleType method.
Methods are procedures assigned to the members of an object. Methods have a few differences from normal procedures.
Methods Can Access Object Locals
A method belonging to a particular class can access both the local and exported members of any object of that class. This allows methods to access and manipulate the internal states of their objects without requiring the objects to export accessor procedures.
Method Names Should Be Declared static
In Maple, most method names should be declared as static. In most cases, all objects of the same class use the same procedures for their methods. If the method name is not declared static, each object will have a separate copy of the procedure. This can be quite wasteful.
There are some instances where an object will have a non-static method. However unless you intend different objects to have different procedures assigned to the method, your method should be static.
Methods Are Passed the Objects They Manipulate
Some object oriented languages associate method calls with a particular object. That object is represented via a self variable or by allowing direct access to that object's members. Maple does not give a particular object special significance in that way. Instead, all objects that a method needs to manipulate must be passed as parameters.
To call an object's method, call the method as a standard function call and pass the object in as an argument.
method( ..., object, ... );
When a function call is evaluated and an object is passed in as an argument, the object is searched for an exported procedure with a matching name. If one is found, that member procedure is called with the given arguments.
This search proceeds from left to right, so the first object with a matching method is used as the class whose method is invoked.
Objects in Indexed Function Calls
When making an indexed function call (of the form func[index](args)) Maple will also check the indices (index) for a matching object as well as the arguments. If a matching object is found in the indices, that object will be used before one found in the arguments.
Searching an index sequence is also performed from left to right.
There are a set of special methods that a class can define that will automatically be used in various situations. Not all of these methods make sense for all objects. See the method specific help pages for complete details.
ModuleCopy: The ModuleCopy method is invoked when a object is copied via the Object routine.
ModuleType: The ModuleType method is invoked when an object is passed into the type routine. It allows a module to have a more precise type check of objects of a particular class.
ModulePrint: The ModulePrint method is invoked when an object is pretty-printed.
ModuleDeconstruct: The ModuleDeconstruct method is invoked when an object is converted to a one-dimensional form, usually Maple syntax.
ModuleApply: The ModuleApply method is invoked when an object is used as a function in a function call expression.
ModuleLoad: The ModuleLoad method is invoked when the object class is read in from a library.
ModuleUnload: The ModuleUnload method is invoked when an object is garbage-collected.
ModuleIterator: The ModuleIterator method creates an interface that can be used to iterate over the contents of the object.
9.5 Overloading Operators
Objects can define methods which allow them to control what happens when those objects are used with various operators. For example, if an object implements a + method, then that method will be invoked if the object appears in a sum expression.
1 + Obj1 + n;
By overloading operators, objects can be used in Maple expressions. This, combined with overloading built-in routines, allows objects to be used naturally in general Maple expressions.
The following operators can be overloaded by an object:
The following operators, in particular, cannot be overridden:
Note: These lists are not the same as the operators that can and cannot be overridden using a use statement.
In general implementing operators is similar to implementing normal methods. However particular operators have rules that must be followed if they are to be implemented correctly.
The rules for the various operators are documented on the Object,operators help page.
9.6 Overloading Built-in Routines
Objects can implement methods to override some built-in routines (like convert or abs). These methods will be invoked when objects are passed as arguments to the corresponding built-in routines. By overriding built-in routines, user-defined objects can be used in normal Maple expressions. This, combined with overloading operators, allows objects to be used naturally in general Maple expressions.
Any routine implemented in Maple code can be overloaded. However, not all built-in routines (routines implemented in the Maple kernel) can be overloaded.
Overridable Built-in Routines
The following built-in routines can be overloaded by object methods:
map, map2, map[n]
Some overloadable built-in routines have a specific interface that must be followed. The interfaces for the overloadable built-ins can be found on the object,builtins help page.
The following example shows a class for objects representing integers modulo a given base.
Warning, (in IntMod:-+) `i` is implicitly declared local
Warning, (in IntMod:-*) `i` is implicitly declared local
i0m5 := IntMod( 0, 5 );
i0m5:=0 mod 5
i1m5 := Object( i0m5, 1 );
i1m5:=1 mod 5
type( i1m5, 'IntMod' );
type( i1m5, 'IntMod'(3) );
type( i1m5, 'IntMod'(5) );
i2m5 := i1m5 + 1;
i2m5:=2 mod 5
i3m5 := i2m5 + 1;
i3m5:=3 mod 5
i4m5 := i3m5 + 1;
i4m5:=4 mod 5
i4m5 + 1;
0 mod 5
2 mod 5
i1m5 + 9 + i4m5;
4 mod 5
convert( i3m5, 'integer' );
convert( 3, IntMod );
Error, (in IntMod:-convert) cannot convert into IntMod from 3
i2m5 * i4m5 * y * f(x);
3 mod 5⁢y⁢f⁡x
3 mod 5
1 mod 5
evalb(i2m5 < i4m5);
evalb(i3m5 > i2m5);
evalb(i2m5 <= i4m5);
evalb(i3m5 >= i2m5);
evalb(i3m5 = i2m5);
9.8 Avoiding Common Mistakes
Be Aware of NULL
Be careful when assuming that operators and built-in routines will always be passed a certain number of arguments. Many will accept NULL as an argument, and this may lead to fewer arguments than expected.
local value := 10;
export `=`::static := proc( l, r, $ )
( l::Wrapper and r::Wrapper and l:-value = r:-value );
cp := Object( Wrapper ):
evalb( cp = Wrapper );
evalb( cp = 11 );
evalb( cp = NULL );
Error, invalid input: Wrapper:-= uses a 2nd argument, r, which is missing
Lexical Scoping Does Not Circumvent local
Members that are declared as local can only be accessed from within the class's methods. This means that methods cannot use lexical scoping to pass values to nested procedures.
export b :: static := proc(mm :: m, f, lst :: list, $)
return map(x -> f(mm:-a, x), lst);
b(m, `+`, [1,2,3]);
In this example, we can print the value of a in b because b is a method. However the map fails because the arrow procedure is not a member and thus does not have access to a.
Download Help Document