APLX Help : Help on APL language : APL Fundamentals : Classes and Objects
|
|
![]() |
Classes and Objects |
Overview of Classes and ObjectsAs well as traditional APL functions and operators, APLX adds object-oriented programming facilities to the core APL language. These facilities are broadly similar to those implemented in other object-oriented programming languages (such as C++, C#, Java, or Ruby), but with the difference that APL's array-programming approach applies to classes and objects in the same way as it applies to ordinary data. The fundamental building block for object-oriented programming in APLX Version 4 is the class. For example, in a commercial invoicing application, a given class might represent the attributes and behavior of an Invoice, and another class might represent a CreditNote. In an application concerned with geometry, a class might represent a Sphere, or a Rectangle, or a Polygon. A class contains definitions both for program logic (functions and operators, known collectively as the methods of the class), and for data (named variables associated with the class, known as properties). The term members is used to describe both the properties and methods of a class. In most cases, when you come to use a class, you need to create an instance of that class, also known as an object. Whereas the class represents an abstraction of (say) an Invoice, or a Sphere, or a Rectangle, an object represents a particular invoice, sphere or rectangle. Typically, you may have many instances of a given class, each containing independent copies of data (properties), but all supporting the same program logic (methods). InheritanceWhen you define a class, you can specify that it inherits from another class. The new class is said to be the child, and the class it inherits from is the parent or base class. Inheritance means that (unless you explicitly change their definition), all of the properties and methods defined in the parent class are also available in the child class. This works for further levels of inheritance as well, so that methods and properties can be inherited from the immediate parent, or from the parent's parent, and so on. The terms derived classes or descendants are sometimes used to denote the children of a class, and the children's children, and so on. Similarly, the term ancestors of a class is used to denote the parent, parent's parent, and so on. For example, you might have a class Shape, representing an abstract geometric shape. This might have properties called 'X' and 'Y' giving the center point of the shape, and methods called 'Move' and 'Area'. A Circle class might inherit from Shape, introducing further properties such as 'radius'. Equally, a class Polygon might also inherit from Shape, and further classes Triangle and Square inherit from Polygon. All of the classes Circle, Polygon, Triangle and Square are derived from Shape. Because of the way inheritance works, they would all include the properties X and Y, and the methods Move and Area. When a class inherits from another, you can specify that the definition of a given method of the parent (or the initial value of a property) is different in the child class. In our example, you would need to supply a different definition of the Area method for a Circle and a Square. This is known as overriding the method. For classes defined in APLX, all methods can be overridden, and all methods are virtual, that is to say if method A in a base class calls another method B, and the second method B is overridden in a child class, then running method A in the child class will cause the overridden version of B to be called, not the version of B defined in the parent. APLX uses an inheritance model known as single inheritance. This means that a child class can be derived from only one parent (which may itself derive from another class, and so on). However, APLX also allows you to 'mix-in' one or more other classes (including external classes, such as those written in .Net or Java) into your objects at runtime. This is a very flexible feature which can be used in much the same way as multiple inheritance is used in some other languages. See the section on Mixins for more details. User-defined, System and External classesAPLX supports the following types of class:
Object References and Class ReferencesWhen you create an object, i.e. an instance of a class (using the system function Of course, because APLX is an array language, you can have arrays of object references, and you can embed object references in nested arrays along with other data. For example, you might have an array containing references to hundreds of Rectangle objects. You can also have a reference to a Class. This makes it possible for general functions to act on classes without knowing in advance which class applies. Creating objects (instances of classes)The system function The class is specified as the right argument (or first element of the right argument). It can be specified either as a class reference, or as a class name (i.e. a character vector). Any parameters to be passed to the constructor of the class (the method which is run automatically when a class is created) follow the class name or reference. If you specify the class by name, you also need to identify in the left argument the environment where the class exists, unless it is internal. Creating instances of internal (user-defined) classesNormally, you create an instance of a user-defined class by passing the class reference directly as the right argument (or first element of the right argument). For example, if you have a class called Invoice, you can create an instance of it by entering: I←⎕NEW Invoice What is really happening here is that the symbol Invoice refers to the class definition, and when it is used in this way, it returns a reference to the class. Note that you can also pass the class name rather than a class reference. The following are alternative ways of creating an instance of a user-defined class: I←⎕NEW 'Invoice' I←'apl' ⎕NEW 'Invoice' Passing arguments to the constructorA constructor is a special method of a class, which is run automatically when the class is created using For example, suppose the class Invoice looks like this: Invoice { TimeStamp Account InvNumber {Serial←0} ∇Invoice B ⍝ Constructor for class Invoice. B is the account number Account←B TimeStamp←⎕TS Serial←Serial+1 InvNumber←Serial ∇ } This is a class which has a constructor and four properties. One of the properties (Serial) is a class-wide property, which means it has only a single value shared between all instances of the class. When a new instance of this class is created, the constructor will be run. It will store the account number (passed as an argument to S←⎕NEW Invoice 23533 S.⎕DS Account=23533, TimeStamp=2007 10 11 15 47 34 848, InvNumber=1 T←⎕NEW Invoice 67544 T.⎕DS Account=67544, TimeStamp=2007 10 11 15 48 11 773, InvNumber=2 Default display of a class or object referenceWhen you call the By default, APLX displays an object reference as the unqualified class name contained in square brackets. Class references are displayed as the class name in curly braces: )CLASSES Queue ⍝ User-defined APL class Queue {Queue} ⍝ Default display of class reference QUEUE23←⎕NEW Queue QUEUE23 ⍝ Default display of APL object reference [Queue] However, if the APL programmer wishes to override the default display form of an object, this can easily be done by using the QUEUE23.⎕DF 'Checkout Queue23' QUEUE23 Checkout Queue23 Object references and object lifetimesWhen you use Consider this sequence, where we create an instance of a class called Philosopher which has a property Name: A←⎕NEW Philosopher A.Name←'Aristotle' At this point, we have created a new instance of the class, and we have a single reference to it, in the variable A. We now copy the reference (not the object itself) to a variable B: B←A B.Name Aristotle We now have two references to the same object. So if we change a property of the object, the change is visible through either reference - they refer to the same thing: B.Name←'Socrates' A.Name Socrates Now we erase one of the references: )ERASE A We still have a second reference to the object. The object will persist until we delete the last reference to it: B.Name Socrates )ERASE B At this point, there are no more references to the object left in the workspace, and the object itself is deleted. It follows from this that, if you use ⎕NEW Philosopher [Philosopher] The Null objectAs its name implies, the Null object is a special case of an object, which has no properties and no methods of its own (although System methods may apply to it). A reference to the Null object displays in the special form: [NULL OBJECT] A reference to the Null object can arise for a number of different reasons:
Types of PropertyWhen you define a class, you specify the names of the properties of that class, which can be used to hold data associated with the class. You can optionally specify a default value for the property, that is the value which the property will have in a newly-created instance of the class. You can also specify that the property is read-only, which means it is not possible to assign a new value to it. Most properties are instance properties, which means that each instance of the class has a separate copy of the property (for example, the X and Y position of a Shape). Occasionally, however, it is useful to define a class-wide property (known in some other languages as a static or shared property). This is a property where there is a single copy of the data, shared between all instances. This is useful for cases such as keeping a unique incrementing serial number (the next invoice number, for example). Combining these concepts, you have the following main types of property:
You can also in principle have a read-only property with no initial value, but this is not very useful! You can also have a read-only instance property, but this is indistinguishable from a read-only class-wide property because you can't assign a different value to it in different instances. Implementation note: APLX uses a 'create-on-write' approach when you assign to an instance property. This means that, if you have never changed the value of a property for a particular instance since the instance was first created, the value which is returned when you read the property is the default value stored in the class definition. It follows that, if you change the class definition so that the property has a different default value, the change will immediately be reflected in all instances of the class, unless the property has been modified for that instance. Name scope, and Public versus Private membersThe members of a class (i.e its properties and methods) can be either public or private. Public members can be accessed from outside the class, whereas private members can only be accessed from within methods defined in the class (or from desk calculator mode, if a method has been interrupted because of an error or interrupt and the method is on the )SI stack). Private members can also be accessed by methods defined in a child (derived) class. If you are familiar with other object-oriented languages such as C++ or Visual Basic, this means that private methods in APLX correspond to 'protected' methods in those languages. If you want to access a public member of an object from outside the class (i.e. not within a method of the class), then you use dot notation to refer to it. This takes the form ObjectReference.MemberName. For example, suppose you have a variable myrect which is a reference to an object of class Rectangle. You could call the Move method and access the X and Y properties for that object as follows: myrect.X←45 myrect.Y←78 myrect.Move 17 6 myrect.X 62 myrect.Y 84 Within the methods of the class itself, you do not normally need to use dot notation. This is because the search order for symbols encountered when executing a method is as follows:
Thus, a simple implementation of the Move method above (defined in the Shape class from which Rectangle derives) might be something like this: ∇ Move B [1] ⍝ Move shape by amount B specified as change to X, Y [2] (X Y)←(X,Y)+B ∇ ConstructorsAs we saw earlier, a constructor is a special type of method, which is run automatically when an instance of a class is created using For a user-defined class, a constructor is defined as a method which takes a right argument, and which has the same name as the class itself. In some other object-oriented programming languages, constructors are a very important part of the language because they are the only way of initializing property values. For user-defined classes in APLX, default values can be set up in the class definition, so constructors are not always needed. Where a class inherits from another class, the constructor which gets run automatically is that of the class itself (if it has a constructor), or of the first ancestor class which has a constructor. Normally, in a constructor, you will want to do some initialization specific to the class itself, and also call the constructor of the parent class (using In APLX, a constructor is also a perfectly ordinary method; it can be called in the normal way by one of the other methods in the class, or from outside (if it declared as Public). This can be useful for re-initializing an object. Some object-oriented languages also include a special method called a destructor, which is called just before the object is deleted. APLX user-defined classes do not have destructors. This means that, if you need to release system resources (for example, close a file or a database connection), you need to call a method to do that explicitly before erasing the last reference to the internal object. However, APLX will automatically take care of deleting all the properties of the object, and releasing the memory back to the workspace. Using Classes without InstancesSo far, we have concentrated on using objects as instances of classes. However, classes can also be very useful in their own right, without the need to make instances of them. There are two major reasons why you might want to define a class which can be used directly: Defining a set of constantsIf you define a class with a set of read-only properties, those properties can be used as a set of constant values or 'enumerations'. For example, you might have a class called Messages, which holds all the messages which your application displays to the user: Messages { OutOfMemory←←'There is not enough memory to continue' AskModelName←←'Enter the name of the model' OpComplete←←'Operation Complete' AskReset←←'Do you want to reset the model?' ...etc } You can then use this class in your application (without having to make an instance of it) to encapsulate all the messages and refer to them by name: ∇R←CheckWS [1] :If R←⎕WA<MIN_FREE_WS [2] ShowError Messages.OutOfMemory [3] :EndIf ∇ This keeps all the messages together in one place, allows you to refer to them by a name which is easy to remember and is self-documenting, but does not pollute the global symbol space with hundreds of APL variables. Keeping namespaces tidyIn traditional APL systems, it often used to be the case the number of global functions was very large. By placing related functions in a class, the workspace can be kept tidy. For example, in a statistical application, you might have a class Average which contained methods for calculating many different types of average (Mean, Median, Mode etc). As long as these methods do not write to any property of the class, there is no need to make an instance of the class to run them; you can just run them using dot notation as Average.Mean, Average.Median etc. Note that, in APLX classes, there is no pre-determined difference between a method which can only be run inside an instance (sometimes known as an instance method), and a method which can be run as a class member without an instance being created (sometimes known as a static method). The only difference is that, at run time, if a method writes to a property, an error will be generated if there is no instance to write to. However, you do need to be aware of the difference between static and instance methods when using classes written in other languages such as Java or C#. See the system function Editing User-Defined ClassesYou can create and edit user-defined classes in a number of ways:
|
|
APLX Help : Help on APL language : APL Fundamentals : Classes and Objects
|
Copyright © 1996-2010 MicroAPL Ltd