If you already understand OOP but are new to coding with Elan...

Be aware that Elan imposes a set of rules on the use of OOP, and which Elan enforces for all the supported languages - even though the individual languages, when used outside Elan, might not impose those constraints. These constraints are all good discipline: recognised by experts in OOP. So learning OOP within Elan will give you a better understanding and lead to fewer problems.

Before trying to writ

Exploring the 'Snake

In the Snake object-oriented demo you can find three examples of concrete classes: Only the 'signature' of each class is shown here - not the instructions within the classes:
+class SnakeName? inheritance?1 +constructor(parameter definitions?) 2 new code end constructor +function toStringname?(parameter definitions?) returns StringType? 3 return "undefined"value or expression?4 end function end class +class AppleName? inheritance?5 +constructor(parameter definitions?) 6 new code end constructor +function toStringname?(parameter definitions?) returns StringType? 7 return "undefined"value or expression?8 end function end class +class SquareName? inheritance?9 +constructor(parameter definitions?) 10 new code end constructor +function toStringname?(parameter definitions?) returns StringType? 11 return "undefined"value or expression?12 end function end class
+class SnakeName? inheritance? # concrete class1 +def __init__(self: Snake) -> None: 2 new code +def toStringname?(self: Snake) -> strType?: # function method3 return "undefined"value or expression?4 +class AppleName? inheritance? # concrete class5 +def __init__(self: Apple) -> None: 6 new code +def toStringname?(self: Apple) -> strType?: # function method7 return "undefined"value or expression?8 +class SquareName? inheritance? # concrete class9 +def __init__(self: Square) -> None: 10 new code +def toStringname?(self: Square) -> strType?: # function method11 return "undefined"value or expression?12
+class SnakeName? inheritance? {1 +public Snake(parameter definitions?) { 2 new code } // constructor +public stringType? toStringname?(parameter definitions?) { // function method3 return "undefined"value or expression?;4 } // function method } // class +class AppleName? inheritance? {5 +public Apple(parameter definitions?) { 6 new code } // constructor +public stringType? toStringname?(parameter definitions?) { // function method7 return "undefined"value or expression?;8 } // function method } // class +class SquareName? inheritance? {9 +public Square(parameter definitions?) { 10 new code } // constructor +public stringType? toStringname?(parameter definitions?) { // function method11 return "undefined"value or expression?;12 } // function method } // class
+Class SnakeName? inheritance? inheritance?1 +Sub New(parameter definitions?) 2 new code End Sub +Function toStringname?(parameter definitions?) As StringType? 3 Return "undefined"value or expression?4 End Function End Class +Class AppleName? inheritance? inheritance?5 +Sub New(parameter definitions?) 6 new code End Sub +Function toStringname?(parameter definitions?) As StringType? 7 Return "undefined"value or expression?8 End Function End Class +Class SquareName? inheritance? inheritance?9 +Sub New(parameter definitions?) 10 new code End Sub +Function toStringname?(parameter definitions?) As StringType? 11 Return "undefined"value or expression?12 End Function End Class
+class SnakeName? inheritance? inheritance? {1 +public Snake(parameter definitions?) { 2 new code } // constructor +public StringType? toStringname?(parameter definitions?) { // function method3 return "undefined"value or expression?;4 } // function method } // class +class AppleName? inheritance? inheritance? {5 +public Apple(parameter definitions?) { 6 new code } // constructor +public StringType? toStringname?(parameter definitions?) { // function method7 return "undefined"value or expression?;8 } // function method } // class +class SquareName? inheritance? inheritance? {9 +public Square(parameter definitions?) { 10 new code } // constructor +public StringType? toStringname?(parameter definitions?) { // function method11 return "undefined"value or expression?;12 } // function method } // class

This example illustrates an important principle of OOP: an object-oriented program should largely consist of the definition of types, the names and definitions of which correspond to entities in program's 'domain'. Here the domain is a game of 'snake' - which incorporate the concepts of a snake, an apple, and many squares. The program might included classes to represent, game, player, board, and others. The decision as to which entities from the domain should represented in code comes with experience - based on how much value would be added to the program by defining such a class - and some programmers will opt for more than others. The important principle is that each class should sound familiar to someone who is familiar with the problem domain (in this case: with the game of 'snake') but is not necessarily a programmer.

Instances

Each concrete class may be thought of as a template, from which one or more 'instances' are created. It is valid to define a concrete class that is instantiated only once within the program, but most classes that you define should be instantiated multiple times. When a class is instantiated, the instance is assigned to a variable, and we can see two (of more) separate examples of this from Snake here:

variable snakename? set to new Snake()value or expression?0
snakename? = Snake()value or expression? # variable definition0
var snakename? = new Snake()value or expression?;0
Dim snakename? = New Snake()value or expression? ' variable definition0
var snakename? = new Snake()value or expression?;0
variable tailname? set to new Square(20, 15)value or expression?0
tailname? = Square(20, 15)value or expression? # variable definition0
var tailname? = new Square(20, 15)value or expression?;0
Dim tailname? = New Square(20, 15)value or expression? ' variable definition0
var tailname? = new Square(20, 15)value or expression?;0

Members

Each concrete classes contains other instructions., collectively known as the 'members' of the class. The following different kinds of members can be seen:

Property

Most classes define several properties. A property has name (like a variable) and a type, which might be a standard simple type or data structure, but can also be a user-defined type - which indicates that the property contains a reference to an instance of that type. (This pattern is often referred to as an 'association' between instances. Like a variable, the value of a property may change during the program run. However, each instance holds its own value for each of the properties defined on its class. Here are some examples of properties being defined (the examples are taken from different classes), showing the name and type of each:

+constructor(parameter definitions?) 0 new code end constructor
property x as Int Code does not parse as Elan.
property x as Int Code does not parse as Elan.
+Sub New(parameter definitions?) 0 new code End Sub
property x as Int Code does not parse as Elan.
taken from SquareSquareSquareSquareSquare
+constructor(parameter definitions?) 0 new code end constructor
property location as Square Code does not parse as Elan.
property location as Square Code does not parse as Elan.
+Sub New(parameter definitions?) 0 new code End Sub
property location as Square Code does not parse as Elan.
taken from AppleAppleAppleAppleApple
+constructor(parameter definitions?) 0 new code end constructor
private property body as List Code does not parse as Elan.
private property body as List Code does not parse as Elan.
+Sub New(parameter definitions?) 0 new code End Sub
private property body as List Code does not parse as Elan.
taken from SnakeSnakeSnakeSnakeSnake

Within the same class the value of a property may be read, or written to using the following syntax (taken from SnakeSnakeSnakeSnakeSnake):

reassign this.priorTailvariableName? to this.body[0]value or expression?0
self.priorTailvariableName? = self.body[0]value or expression? # reassign variable0
this.priorTailvariableName? = this.body[0]value or expression?; // reassign variable0
Me.priorTailvariableName? = Me.body[0]value or expression? ' reassign variable0
this.priorTailvariableName? = this.body[0]value or expression?; // reassign variable0

where the value of the property named priorTailpriorTailpriorTailpriorTailpriorTail is being changed to hold the first element from the list of squares held in the property named bodybodybodybodybody. In both cases the keyword thisthisthisthisthis refers to the current instance (i.e. the instance to which the code is applied). It is followed by a dot and the name of the property - an example of 'dot syntax'.

Constructor

Each class must define one, and only one, constructor. The role of the constructor is to 'initialise' (set the initial values) of the properties. The constructor is called each time an instance of a class is created. In the constructor for SquareSquareSquareSquareSquare:

constructor(x as Int, y as Int) reassign this.x to x reassign this.y to y end constructor Code does not parse as Elan.
constructor(x as Int, y as Int) reassign this.x to x reassign this.y to y end constructor Code does not parse as Elan.
constructor(x as Int, y as Int) reassign this.x to x reassign this.y to y end constructor Code does not parse as Elan.
constructor(x as Int, y as Int) reassign this.x to x reassign this.y to y end constructor Code does not parse as Elan.
constructor(x as Int, y as Int) reassign this.x to x reassign this.y to y end constructor Code does not parse as Elan.

the value of the properties xxxxx and yyyyy are set to the values defined by the two parameters. Although the parameters have the same names as the properties they ambiguity is avoided because the property is always prefixed by thisthisthisthisthis.

The constructor for SnakeSnakeSnakeSnakeSnake does not define any parameters, because a snake is instantiated only once within each game, and always to the same size and initial position:

constructor()
constructor()
constructor()
constructor()
constructor()

Note, however, that within the constructor, it creates a new instance of SquareSquareSquareSquareSquare, passing in the arguments 2020202020 and 1515151515, which the square's constructor requires. It also makes used of getAdjacentSquare

which is an example of using a method - which we shall explore next.

Methods

The properties on a class define the state of an instance; the methods define its behaviour. Methods on a concrete class have two distinct forms: function method and procedure method. These mirror the distinction between a (global) function and procedure. A function is a 'query' that returns information based on the parameters that are supplied to it, but in the case of a function method (defined within a class), in addition to the parameter values, it has access to the property values of the instance - which is legitimate because it is only ever called via that instance. Thus, in the example above, getAdjacentSquare is invoked on the variable tailtailtailtailtail (an instance of SquareSquareSquareSquareSquare), and is passed the value of currentDircurrentDircurrentDircurrentDircurrentDir as an argument. Looking at the definition of getAdjacentSquare within the class SquareSquareSquareSquareSquare:

+function getAdjacentSquarename?(d as Directionparameter definitions?) returns SquareType? 1 variable newXname? set to this.xvalue or expression?2 variable newYname? set to this.yvalue or expression?3 +if d is Direction.leftcondition? then 4 reassign newXvariableName? to this.x - 1value or expression?5 elif d is Direction.rightcondition? then6 reassign newXvariableName? to this.x + 1value or expression?7 elif d is Direction.upcondition? then8 reassign newYvariableName? to this.y - 1value or expression?9 elif d is Direction.downcondition? then10 reassign newYvariableName? to this.y + 1value or expression?11 end if return new Square(newX, newY)value or expression?12 end function
+def getAdjacentSquarename?(d: Directionparameter definitions?) -> SquareType?: # function1 newXname? = self.xvalue or expression? # variable definition2 newYname? = self.yvalue or expression? # variable definition3 +if d == Direction.leftcondition?: 4 newXvariableName? = self.x - 1value or expression? # reassign variable5 elif d == Direction.rightcondition?: # else if6 newXvariableName? = self.x + 1value or expression? # reassign variable7 elif d == Direction.upcondition?: # else if8 newYvariableName? = self.y - 1value or expression? # reassign variable9 elif d == Direction.downcondition?: # else if10 newYvariableName? = self.y + 1value or expression? # reassign variable11 return Square(newX, newY)value or expression?12
+static SquareType? getAdjacentSquarename?(Direction dparameter definitions?) { // function1 var newXname? = this.xvalue or expression?;2 var newYname? = this.yvalue or expression?;3 +if (d == Direction.leftcondition?) { 4 newXvariableName? = this.x - 1value or expression?; // reassign variable5 } else if (d == Direction.rightcondition?) {6 newXvariableName? = this.x + 1value or expression?; // reassign variable7 } else if (d == Direction.upcondition?) {8 newYvariableName? = this.y - 1value or expression?; // reassign variable9 } else if (d == Direction.downcondition?) {10 newYvariableName? = this.y + 1value or expression?; // reassign variable11 } // if return new Square(newX, newY)value or expression?;12 } // function
+Function getAdjacentSquarename?(d As Directionparameter definitions?) As SquareType? 1 Dim newXname? = Me.xvalue or expression? ' variable definition2 Dim newYname? = Me.yvalue or expression? ' variable definition3 +If d = Direction.leftcondition? Then 4 newXvariableName? = Me.x - 1value or expression? ' reassign variable5 ElseIf d = Direction.rightcondition? Then6 newXvariableName? = Me.x + 1value or expression? ' reassign variable7 ElseIf d = Direction.upcondition? Then8 newYvariableName? = Me.y - 1value or expression? ' reassign variable9 ElseIf d = Direction.downcondition? Then10 newYvariableName? = Me.y + 1value or expression? ' reassign variable11 End If Return New Square(newX, newY)value or expression?12 End Function
+static SquareType? getAdjacentSquarename?(Direction dparameter definitions?) { // function1 var newXname? = this.xvalue or expression?;2 var newYname? = this.yvalue or expression?;3 +if (d == Direction.leftcondition?) { 4 newXvariableName? = this.x - 1value or expression?; // reassign variable5 } else if (d == Direction.rightcondition?) {6 newXvariableName? = this.x + 1value or expression?; // reassign variable7 } else if (d == Direction.upcondition?) {8 newYvariableName? = this.y - 1value or expression?; // reassign variable9 } else if (d == Direction.downcondition?) {10 newYvariableName? = this.y + 1value or expression?; // reassign variable11 } // if return new Square(newX, newY)value or expression?;12 } // function

we see that the direction (assigned to the parameter ddddd) is used together with the square's xxxxx and yyyyy properties to identify the coordinates newXnewXnewXnewXnewX and newYnewYnewYnewYnewY for the adjacent square in the specified direction, and use those to create and return a new instance of SquareSquareSquareSquareSquare with those two new coordinates.

Similarly a procedure method is like a global procedure except that it is defined within a class, is accessed only via an instance of that class, using dot syntax, and also has internal access to the properties of that instance. Both kinds of procedure may be thought of as 'commands': they make changes to the state of the system. A common use of procedure methods is to change the values of one or more properties and/or to make changes outside the object. The method newRandomPosition defined on the class AppleAppleAppleAppleApple:

+procedure newRandomPositionname?(snake as Snakeparameter definitions?) 1 variable changePositionname? set to truevalue or expression?2 +while changePositioncondition? 3 variable ranXname? set to randint(0, 39)value or expression?4 variable ranYname? set to randint(0, 29)value or expression?5 reassign this.locationvariableName? to new Square(ranX, ranY)value or expression?6 +if not snake.bodyCovers(this.location)condition? then 7 reassign changePositionvariableName? to falsevalue or expression?8 end if end while end procedure
+def newRandomPositionname?(snake: Snakeparameter definitions?) -> None: # procedure1 changePositionname? = Truevalue or expression? # variable definition2 +while changePositioncondition?: 3 ranXname? = randint(0, 39)value or expression? # variable definition4 ranYname? = randint(0, 29)value or expression? # variable definition5 self.locationvariableName? = Square(ranX, ranY)value or expression? # reassign variable6 +if not snake.bodyCovers(self.location)condition?: 7 changePositionvariableName? = Falsevalue or expression? # reassign variable8
+static void newRandomPositionname?(Snake snakeparameter definitions?) { // procedure1 var changePositionname? = truevalue or expression?;2 +while (changePositioncondition?) { 3 var ranXname? = randint(0, 39)value or expression?;4 var ranYname? = randint(0, 29)value or expression?;5 this.locationvariableName? = new Square(ranX, ranY)value or expression?; // reassign variable6 +if (!snake.bodyCovers(this.location)condition?) { 7 changePositionvariableName? = falsevalue or expression?; // reassign variable8 } // if } // while } // procedure
+Sub newRandomPositionname?(snake As Snakeparameter definitions?) ' procedure1 Dim changePositionname? = Truevalue or expression? ' variable definition2 +While changePositioncondition? 3 Dim ranXname? = randint(0, 39)value or expression? ' variable definition4 Dim ranYname? = randint(0, 29)value or expression? ' variable definition5 Me.locationvariableName? = New Square(ranX, ranY)value or expression? ' reassign variable6 +If Not snake.bodyCovers(Me.location)condition? Then 7 changePositionvariableName? = Falsevalue or expression? ' reassign variable8 End If End While End Sub
+static void newRandomPositionname?(Snake snakeparameter definitions?) { // procedure1 var changePositionname? = truevalue or expression?;2 +while (changePositioncondition?) { 3 var ranXname? = randint(0, 39)value or expression?;4 var ranYname? = randint(0, 29)value or expression?;5 this.locationvariableName? = new Square(ranX, ranY)value or expression?; // reassign variable6 +if (!snake.bodyCovers(this.location)condition?) { 7 changePositionvariableName? = falsevalue or expression?; // reassign variable8 } // if } // while } // procedure

changes the locationlocationlocationlocationlocation to a new SquareSquareSquareSquareSquare - which requires it to be a procedure method. (A second reason is that it uses the system method randint - which depends upon, and changes, data held in the system.) Note that this procedure method makes use of the function method bodyCoversbodyCoversbodyCoversbodyCoversbodyCovers, defined on SnakeSnakeSnakeSnakeSnake.

A function method may also use other function methods and global functions, but may not call any procedure, whether global or procedure method.

toString method

Every class must define a function method named toString, which defines no parameters and returns a StringstrstringStringString. The returned string is considered to be a representation of the instance: toString is called automatically when an instance printed, or is used within a field in an interpolated string. The string might be as generic as 'a Person', use one or more properties to identify the instance as e.g 'Charlie Smith', or even list, or summarise all the data held by the object.

Creating a new concrete class

A new concrete class is added into your code, at global level, with the concrete class instruction. The template includes an empty constructor, and a default implementation of toString:

+class NameName? inheritance?1 +constructor(parameter definitionsparameter definitions?) 2 new code end constructor +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 3 return "undefined"value or expression?4 end function end class
+class NameName? inheritance? # concrete class1 +def __init__(self: Name) -> None: 2 new code +def toStringname?(self: Name) -> strType?: # function method3 return "undefined"value or expression?4
+class NameName? inheritance? {1 +public Name(parameter definitionsparameter definitions?) { 2 new code } // constructor +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method3 return "undefined"value or expression?;4 } // function method } // class
+Class NameName? inheritance? inheritance?1 +Sub New(parameter definitionsparameter definitions?) 2 new code End Sub +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 3 Return "undefined"value or expression?4 End Function End Class
+class NameName? inheritance? inheritance? {1 +public Name(parameter definitionsparameter definitions?) { 2 new code } // constructor +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method3 return "undefined"value or expression?;4 } // function method } // class

You must complete the template, by giving the class a name, which must start with a capital letter (because a class defines a type), followed by any alphabetic or numeric characters, or underlines. In toString you should, at some point, replace "undefined""undefined""undefined""undefined""undefined" with something more specific.

Then via the new code prompt, which will offer you the appropriate options, add properties and methods. All properties must be initialised in the constructor, except those of types IntintintIntegerint, FloatfloatdoubleDoubledouble, which take on the default values of the type - although such properties may optionally be initialised to different values. Note in particular that properties of type StringstrstringStringString must be initialised, even if only to an empty string """""""""".

Inheritance

A concrete class may inherit from a single abstract class, and/or from one or more interfaces. It may not inherit from more than one abstract class. Nor may it inherit from another concrete class. Abstract class An abstract class may not be instantiate directly, and hence neither needs, nor allows, a constructor. It may define the following types of member - Property, Function method, Procedure method - Abstract Property, abstract function, abstract Intro What is OOP and its advantages Allows you to work with custom data structures that represent the domain of your application in a way that it easier to read, write, and modify. Example: Snake OOP vs. Snake Procedural Particularly well suited to simulations and games, graphics, or complex business applications – give examples of objects. You can define and use objects where needed within a procedural programming, but the OOP paradigm means using the ‘object model’ as the primary design pattern. Class and instance The example programs We will be referring to the following demo programs: … Class defines type and may be thought of as a template, used to create one or more instances. Class is defined in one place And instantiated, and subsequently used, in other places Example of Snake OOP Snake Defined here (the code within the definition is not shown here) Instantiated here and assigned to variable named snake Apple Defined here (the code within the definition is not shown here) Instantiated here and assigned to the variable named apple Square Instantiated many times to show each segment of the snake’s body and the apple. Whenever the snake moves a new instance of Square is added to the front (the ‘head’) and the one at the tail end is discarded. Class definition Adding a new class Select class from new code menu – at global (file) level show You must give it a name. Because a class defines a type the name must start with a capital letter. Examples of valid class names including use of camel case. Within the class, two members are defined in the template (because every class must have them both) : a constructor and a toString method. If you look at each of the classes in Snake_OOP you will see that each has these somewhere, but with some additions/modifications. Properties Almost all classes define one or more properties – which define the data items. Each has a name and type. See examples Properties are like variables, except that they are associated separately with each instance. Thus the class X might define a property y, which will contain z1 for one instance, and z2 for another. Properties are defined within a class using the the property instruction. Example A property of a numeric type (float or integer) will automatically be initialised to the value 0, and a Boolean type to false. All other properties must be explicitly initialised within the constructor. Whenever a value is specified for a property, it is done with a change variable instruction. In the following example the property x is initialised to …. Or you can allow value(s to be specified as parameters in the constructor, and then these values are used within the constructor to initialise one or more properties, for example: If the constructor defines parameters (as above) then appropriate values must be provided when you create a new instance. For example: Then, you can read the properties of an object instance that is held in Using property values within the toString method ToString is a function method that returns a string, but takes no parameters. However, like other methods it can make use of the instance’s properties To string is used: - When printing an object e.g. - Referencing an object within an interpolated string e.g. By default the toString returns ‘undefined’. You can edit this so that it returns `a student` but it is often more useful more useful to return a string that is derived from one or more of the instances properties, for example: - Returning a single identifier - Returning type plus identifier - Summarising all the properties Updating properties of an object Although you can read the value of an ordinary property from code that it outside the class (for example in main) you cannot directly update a property from outside the class. For example, if you try to write this code: You will get the error Xxx If you want to be able to update an property from code outside you need to write a method Private property What this looks like What it means Encapsulation and information hiding Associations Basic types e.g. numbers, strings, Boolean Single association Other UDT (class) or less-commonly another instance of the same type e.g. Best friend Multiple association List of value types, or of associated objects - referrerd Optional association Methods A method (more completely called an ‘instance method’) is a function or procedure that is defined within a class. These methods (unless defined as private) can be Function methods Think of them as queries on the object. They can return information from object’s Procedure methods Make changes to the object and/or depend upon the system. So they f