Object-oriented programming Reference

Getting started

Select the object-oriented programming paradigm

To undertake object-oriented programming (OOP) with Elan, you should first be reasonably familiar with procedural programming with Elan. Start by switching to the object-oriented paradigm from the menu at the top of the IDE. This will still allow you to do procedural programming, but changes two things:

In the following section we will describe 10 principles that define the essence of OOP, illustrated with example code - where possible from the demo programs.

Principles of object-oriented programming

Important. Every textbook on OOP differs slightly in the definition of core principles, and every language that supports OOP differs in the rules that it enforces. The following 10 principles are based on widely recognised best practices in OOP. When coding with Elan, in whichever of the supported languages - Python, VB, C#, or Java (or the Elan Reference Language) - these principles are, wherever possible, enforced by the Elan tooling. The resulting code is always valid syntax for the chosen language - but may depend upon common library methods defined in the Elan library. Also, if you go on to write code in any of those languages outside Elan, you will find that the same principles are not necessarily enforced. Though that might appear to offer you more freedom, sticking voluntarily to the principles that you learned in Elan will result in better code and fewer problems.

A class is a user-defined data structure type

In procedural programming with Elan, there is a fixed set of types available to you to represent data - the only exception being enums which offer a very limited form of 'user-defined' type (UDT). OOP makes it easy to define far richer forms of UDTs. The simplest and most common way to do this is to define a concrete class which is one of the new options made available at global (file) level when using Elan with the object-oriented paradigm selected.

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 contained 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 # end constructor +def toStringname?(self: Snake) -> strType?: # function method3 return "undefined"value or expression?4 # end function method # end class +class AppleName? inheritance?: # concrete class5 +def __init__(self: Apple) -> None: 6 new code # end constructor +def toStringname?(self: Apple) -> strType?: # function method7 return "undefined"value or expression?8 # end function method # end class +class SquareName? inheritance?: # concrete class9 +def __init__(self: Square) -> None: 10 new code # end constructor +def toStringname?(self: Square) -> strType?: # function method11 return "undefined"value or expression?12 # end function method # end class
+class SnakeName? inheritance? {1 +public Snake(parameter definitions?) { 2 new code } // end constructor +public stringType? toStringname?(parameter definitions?) { // function method3 return "undefined"value or expression?;4 } // end function method } // end class +class AppleName? inheritance? {5 +public Apple(parameter definitions?) { 6 new code } // end constructor +public stringType? toStringname?(parameter definitions?) { // function method7 return "undefined"value or expression?;8 } // end function method } // end class +class SquareName? inheritance? {9 +public Square(parameter definitions?) { 10 new code } // end constructor +public stringType? toStringname?(parameter definitions?) { // function method11 return "undefined"value or expression?;12 } // end function method } // end class
+Class SnakeName? 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?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?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
public class Global {

+class SnakeName? inheritance? {1 +public Snake(parameter definitions?) { 2 new code } // end constructor +public StringType? toStringname?(parameter definitions?) { // function method3 return "undefined"value or expression?;4 } // end function method } // end class +class AppleName? inheritance? {5 +public Apple(parameter definitions?) { 6 new code } // end constructor +public StringType? toStringname?(parameter definitions?) { // function method7 return "undefined"value or expression?;8 } // end function method } // end class +class SquareName? inheritance? {9 +public Square(parameter definitions?) { 10 new code } // end constructor +public StringType? toStringname?(parameter definitions?) { // function method11 return "undefined"value or expression?;12 } // end function method } // end class
} // end Global

Within the definition of each class you will find several property instructions. Each property has unique name (within the class). A class may defined multiple properties of the same type, for example, the SquareSquareSquareSquareSquare class in Snake defines:

Nametype
xxxxxIntintintIntegerint
yyyyyIntintintIntegerint

or properties may have different types, for example the TuringMachineTuringMachineTuringMachineTuringMachineTuringMachine class in the Roman Numerals Turing Machine demo has these:

nametype
initialStateinitialStateinitialStateinitialStateinitialStateStringstrstringStringString
currentStatecurrentStatecurrentStatecurrentStatecurrentStateStringstrstringStringString
headPositionheadPositionheadPositionheadPositionheadPositionIntintintIntegerint
haltStatehaltStatehaltStatehaltStatehaltStateStringstrstringStringString
rulesrulesrulesrulesrulesList<of Rule>list[Rule]List<Rule>List(Of Rule)List<Rule>
tapetapetapetapetapeStringstrstringStringString

Defining a class

Selecting concrete class from the new code menu inserts a template like this:

+class NameName? inheritance?13 +constructor(parameter definitionsparameter definitions?) 14 new code end constructor +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 15 return "undefined"value or expression?16 end function end class
+class NameName? inheritance?: # concrete class17 +def __init__(self: Name) -> None: 18 new code # end constructor +def toStringname?(self: Name) -> strType?: # function method19 return "undefined"value or expression?20 # end function method # end class
+class NameName? inheritance? {21 +public Name(parameter definitionsparameter definitions?) { 22 new code } // end constructor +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method23 return "undefined"value or expression?;24 } // end function method } // end class
+Class NameName? inheritance?25 +Sub New(parameter definitionsparameter definitions?) 26 new code End Sub +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 27 Return "undefined"value or expression?28 End Function End Class
+class NameName? inheritance? {29 +public Name(parameter definitionsparameter definitions?) { 30 new code } // end constructor +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method31 return "undefined"value or expression?;32 } // end function method } // end class
where the only thing you must provide is a unique name for the class, which must start with a capital letter, followed by other letters, digits or underscores. (We will look at the roles of the constructor, and the toString function later on.)

An object is an instance of a class

Every object in code is an instance of a specific class. The class may be thought of as a 'template' for defining instances. Each instance holds its own specific value for each of the properties defined in the class.

An new object may be created and assigned to a variable definition, for example:

variable applename? set to new Apple()value or expression?33
applename? = Apple()value or expression? # variable definition34
var applename? = new Apple()value or expression?;35
Dim applename? = New Apple()value or expression? ' variable definition36
var applename? = new Apple()value or expression?;37
variable snakename? set to new Snake()value or expression?38
snakename? = Snake()value or expression? # variable definition39
var snakename? = new Snake()value or expression?;40
Dim snakename? = New Snake()value or expression? ' variable definition41
var snakename? = new Snake()value or expression?;42

Note that the type of each variable is determined by the class of the instance so that the appleappleappleappleapple variable may subsequently be reassigned to a different instance of AppleAppleAppleAppleApple but may not be reassigned to an instance of a different class (nor any other type).

A new instance may also be created as part of an expression without having to be assigned to a variable, for example:

+function emptyPointname?(parameter definitionsparameter definitions?) returns PointType? 43 return new Point(-1, -1)value or expression?44 end function
+def emptyPointname?(parameter definitionsparameter definitions?) -> PointType?: # function45 return Point(-1, -1)value or expression?46 # end function
+static PointType? emptyPointname?(parameter definitionsparameter definitions?) { // function47 return new Point(-1, -1)value or expression?;48 } // end function
+Function emptyPointname?(parameter definitionsparameter definitions?) As PointType? 49 Return New Point(-1, -1)value or expression?50 End Function
+static PointType? emptyPointname?(parameter definitionsparameter definitions?) { // function51 return new Point(-1, -1)value or expression?;52 } // end function

Reading properties

Wherever you have a reference to an object, your code may read and make use of the publicly-visible properties of that object using 'dot syntax', for example (from the Pathfinder demo):

+if distViaCurrent < neighbour.distFromStartcondition? then 53 new code end if
+if distViaCurrent < neighbour.distFromStartcondition?: 54 new code # end if
+if (distViaCurrent < neighbour.distFromStartcondition?) { 55 new code } // end if
+If distViaCurrent < neighbour.distFromStartcondition? Then 56 new code End If
+if (distViaCurrent < neighbour.distFromStartcondition?) { 57 new code } // end if
is comparing the value of the variable distViaCurrentdistViaCurrentdistViaCurrentdistViaCurrentdistViaCurrent to the property named distFromStartdistFromStartdistFromStartdistFromStartdistFromStart of the object named neighbourneighbourneighbourneighbourneighbour.

The constructor initialises the properties

When coding with Elan every class must define a constructor, the template for which is created when you define a new class, and which may be edited, though not deleted. The role of the constructor is to initialise the properties whenever an instance of that class is created.

The only properties that don't need to be initialised are properties of type IntintintIntegerint, FloatfloatdoubleDoubledouble, or BooleanboolboolBooleanboolean - which are automatically initialised to the default value of each type - 00000, 0.00.00.00.00.0, or falseFalsefalseFalsefalse respectively. All other properties must be explicitly initialised within the constructor. Here is simple example of properties being initialised within the constructor for SquareSquareSquareSquareSquare in the Snake demo:

+class SquareName? inheritance?58 +constructor(x as Int, y as Intparameter definitions?) 59 assign this.xvariableName? to xvalue or expression?60 assign this.yvariableName? to yvalue or expression?61 end constructor property xname? as IntType?62 property yname? as IntType?63 # Methods not shown herecomment? +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 64 return "{this.x}, {this.y}"value or expression?65 end function end class
+class SquareName? inheritance?: # concrete class66 +def __init__(self: Square, x: int, y: intparameter definitions?) -> None: 67 self.xvariableName? = xvalue or expression? # assignment68 self.yvariableName? = yvalue or expression? # assignment69 # end constructor xname?: intType? # property70 yname?: intType? # property71 # Methods not shown herecomment? +def toStringname?(self: Square) -> strType?: # function method72 return "{this.x}, {this.y}"value or expression?73 # end function method # end class
+class SquareName? inheritance? {74 +public Square(int x, int yparameter definitions?) { 75 this.xvariableName? = xvalue or expression?; // assignment76 this.yvariableName? = yvalue or expression?; // assignment77 } // end constructor public intType? xname? {get; private set;} // property78 public intType? yname? {get; private set;} // property79 // Methods not shown herecomment? +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method80 return "{this.x}, {this.y}"value or expression?;81 } // end function method } // end class
+Class SquareName? inheritance?82 +Sub New(x As Integer, y As Integerparameter definitions?) 83 Me.xvariableName? = xvalue or expression? ' assignment84 Me.yvariableName? = yvalue or expression? ' assignment85 End Sub Property xname? As IntegerType?86 Property yname? As IntegerType?87 ' Methods not shown herecomment? +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 88 Return "{this.x}, {this.y}"value or expression?89 End Function End Class
+class SquareName? inheritance? {90 +public Square(int x, int yparameter definitions?) { 91 this.xvariableName? = xvalue or expression?; // assignment92 this.yvariableName? = yvalue or expression?; // assignment93 } // end constructor public intType? xname?; // property94 public intType? yname?; // property95 // Methods not shown herecomment? +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method96 return "{this.x}, {this.y}"value or expression?;97 } // end function method } // end class

Note:

Objects may have associations to other objects

So far we have looked at properties where the type is one of the standard library types. However, a class may define one or more properties where the type is a class - either of the same type as the class in which the property is defined, or - more commonly - of a different class. Each such property is referred to as an 'association' or 'associated object'. If the type of a property is a list, or other data structure, where the member type is a class, then it is often referred to as a 'multiple association' - with the ordinary kind of association referred to as 'single association'. The SnakeSnakeSnakeSnakeSnake class shows both kinds of association (only relevant parts of the class are shown only here):

+class SnakeName? inheritance?98 +constructor(parameter definitionsparameter definitions?) 99 variable tailname? set to new Square(20, 15)value or expression?100 assign this.currentDirvariableName? to Direction.rightvalue or expression?101 assign this.bodyvariableName? to [tail]value or expression?102 assign this.headvariableName? to tail.getAdjacentSquare(this.currentDir)value or expression?103 assign this.priorTailvariableName? to tailvalue or expression?104 end constructor private property currentDirname? as DirectionType?105 private property headname? as SquareType?106 private property bodyname? as List<of Square>Type?107 private property priorTailname? as SquareType?108 +function toStringname?(parameter definitions?) returns StringType? 109 return "undefined"value or expression?110 end function end class
+class SnakeName? inheritance?: # concrete class111 +def __init__(self: Snake) -> None: 112 tailname? = Square(20, 15)value or expression? # variable definition113 self.currentDirvariableName? = Direction.rightvalue or expression? # assignment114 self.bodyvariableName? = [tail]value or expression? # assignment115 self.headvariableName? = tail.getAdjacentSquare(self.currentDir)value or expression? # assignment116 self.priorTailvariableName? = tailvalue or expression? # assignment117 # end constructor currentDirname?: DirectionType? # private property118 headname?: SquareType? # private property119 bodyname?: list[Square]Type? # private property120 priorTailname?: SquareType? # private property121 +def toStringname?(self: Snake) -> strType?: # function method122 return "undefined"value or expression?123 # end function method # end class
+class SnakeName? inheritance? {124 +public Snake(parameter definitionsparameter definitions?) { 125 var tailname? = new Square(20, 15)value or expression?;126 this.currentDirvariableName? = Direction.rightvalue or expression?; // assignment127 this.bodyvariableName? = new [] {tail}value or expression?; // assignment128 this.headvariableName? = tail.getAdjacentSquare(this.currentDir)value or expression?; // assignment129 this.priorTailvariableName? = tailvalue or expression?; // assignment130 } // end constructor private DirectionType? currentDirname? {get; private set;} // private property131 private SquareType? headname? {get; private set;} // private property132 private List<Square>Type? bodyname? {get; private set;} // private property133 private SquareType? priorTailname? {get; private set;} // private property134 +public stringType? toStringname?(parameter definitions?) { // function method135 return "undefined"value or expression?;136 } // end function method } // end class
+Class SnakeName? inheritance?137 +Sub New(parameter definitionsparameter definitions?) 138 Dim tailname? = New Square(20, 15)value or expression? ' variable definition139 Me.currentDirvariableName? = Direction.rightvalue or expression? ' assignment140 Me.bodyvariableName? = {tail}value or expression? ' assignment141 Me.headvariableName? = tail.getAdjacentSquare(Me.currentDir)value or expression? ' assignment142 Me.priorTailvariableName? = tailvalue or expression? ' assignment143 End Sub Private Property currentDirname? As DirectionType?144 Private Property headname? As SquareType?145 Private Property bodyname? As List(Of Square)Type?146 Private Property priorTailname? As SquareType?147 +Function toStringname?(parameter definitions?) As StringType? 148 Return "undefined"value or expression?149 End Function End Class
+class SnakeName? inheritance? {150 +public Snake(parameter definitionsparameter definitions?) { 151 var tailname? = new Square(20, 15)value or expression?;152 this.currentDirvariableName? = Direction.rightvalue or expression?; // assignment153 this.bodyvariableName? = list(tail)value or expression?; // assignment154 this.headvariableName? = tail.getAdjacentSquare(this.currentDir)value or expression?; // assignment155 this.priorTailvariableName? = tailvalue or expression?; // assignment156 } // end constructor private DirectionType? currentDirname?; // private property157 private SquareType? headname?; // private property158 private List<Square>Type? bodyname?; // private property159 private SquareType? priorTailname?; // private property160 +public StringType? toStringname?(parameter definitions?) { // function method161 return "undefined"value or expression?;162 } // end function method } // end class

Note:

Classes provide behaviour through methods

In the last code example tail.getAdjacentSquare(this.currentDir)tail.getAdjacentSquare(self.currentDir)tail.getAdjacentSquare(this.currentDir)tail.getAdjacentSquare(Me.currentDir)tail.getAdjacentSquare(this.currentDir) calls a function method, using 'dot syntax' on the the variable tailtailtailtailtail, which is an instance of class SquareSquareSquareSquareSquare. Defining a function method, or a procedure method, on a class has these advantages:

Properties on a class define the data (or 'state') for each instance of the class; methods define the behaviour for the object.

A function method provides information based on the parameters specified in combination with the object's properties

Function methods defined on a class, are similar to global functions in that they act like 'queries', returning information that is determined by the values of the parameters. The difference is that a function method defined on a class, may additionally make use of information obtained from the properties of the object (directly or indirectly) - to determine the information to be returned. But like a global function, a function method defined on a class may not cause any side effects, which means that it may not update any property. The body of the getAdjacentSquare accesses the objects xxxxx and yyyyy properties and uses this, together with the DirectionDirectionDirectionDirectionDirection parameter to create and return a new instance of SquareSquareSquareSquareSquare with different xxxxx and yyyyy properties:

+function getAdjacentSquarename?(d as Directionparameter definitions?) returns SquareType? 163 variable newXname? set to this.xvalue or expression?164 variable newYname? set to this.yvalue or expression?165 +if d is Direction.leftcondition? then 166 assign newXvariableName? to this.x - 1value or expression?167 elif d is Direction.rightcondition? then168 assign newXvariableName? to this.x + 1value or expression?169 elif d is Direction.upcondition? then170 assign newYvariableName? to this.y - 1value or expression?171 elif d is Direction.downcondition? then172 assign newYvariableName? to this.y + 1value or expression?173 end if return new Square(newX, newY)value or expression?174 end function
+def getAdjacentSquarename?(d: Directionparameter definitions?) -> SquareType?: # function175 newXname? = self.xvalue or expression? # variable definition176 newYname? = self.yvalue or expression? # variable definition177 +if d == Direction.leftcondition?: 178 newXvariableName? = self.x - 1value or expression? # assignment179 elif d == Direction.rightcondition?: # else if180 newXvariableName? = self.x + 1value or expression? # assignment181 elif d == Direction.upcondition?: # else if182 newYvariableName? = self.y - 1value or expression? # assignment183 elif d == Direction.downcondition?: # else if184 newYvariableName? = self.y + 1value or expression? # assignment185 # end if return Square(newX, newY)value or expression?186 # end function
+static SquareType? getAdjacentSquarename?(Direction dparameter definitions?) { // function187 var newXname? = this.xvalue or expression?;188 var newYname? = this.yvalue or expression?;189 +if (d == Direction.leftcondition?) { 190 newXvariableName? = this.x - 1value or expression?; // assignment191 } else if (d == Direction.rightcondition?) {192 newXvariableName? = this.x + 1value or expression?; // assignment193 } else if (d == Direction.upcondition?) {194 newYvariableName? = this.y - 1value or expression?; // assignment195 } else if (d == Direction.downcondition?) {196 newYvariableName? = this.y + 1value or expression?; // assignment197 } // end if return new Square(newX, newY)value or expression?;198 } // end function
+Function getAdjacentSquarename?(d As Directionparameter definitions?) As SquareType? 199 Dim newXname? = Me.xvalue or expression? ' variable definition200 Dim newYname? = Me.yvalue or expression? ' variable definition201 +If d = Direction.leftcondition? Then 202 newXvariableName? = Me.x - 1value or expression? ' assignment203 ElseIf d = Direction.rightcondition? Then204 newXvariableName? = Me.x + 1value or expression? ' assignment205 ElseIf d = Direction.upcondition? Then206 newYvariableName? = Me.y - 1value or expression? ' assignment207 ElseIf d = Direction.downcondition? Then208 newYvariableName? = Me.y + 1value or expression? ' assignment209 End If Return New Square(newX, newY)value or expression?210 End Function
+static SquareType? getAdjacentSquarename?(Direction dparameter definitions?) { // function211 var newXname? = this.xvalue or expression?;212 var newYname? = this.yvalue or expression?;213 +if (d == Direction.leftcondition?) { 214 newXvariableName? = this.x - 1value or expression?; // assignment215 } else if (d == Direction.rightcondition?) {216 newXvariableName? = this.x + 1value or expression?; // assignment217 } else if (d == Direction.upcondition?) {218 newYvariableName? = this.y - 1value or expression?; // assignment219 } else if (d == Direction.downcondition?) {220 newYvariableName? = this.y + 1value or expression?; // assignment221 } // end if return new Square(newX, newY)value or expression?;222 } // end function

The toString function method returns a string that represents the object in some way. It is called automatically whenever you call print with a variable that holds an instance of the class. If you don't need to print an object in text form on the display, it is safe to leave the default implementation toString - that was created when you added the class, and which returns an empty string """""""""". If you do intend to print an object in text form then you can define the string to be returned, incorporating one or more of the object's properties to help identify the particular object. For example, you might edit the method defined on the SnakeSnakeSnakeSnakeSnake class to:

+function toStringname?(parameter definitionsparameter definitions?) returns StringType? 223 return $"A snake with {this.body.length() + 1} segments"value or expression?224 end function
+def toStringname?(parameter definitionsparameter definitions?) -> strType?: # function225 return f"A snake with {self.body.length() + 1} segments"value or expression?226 # end function
+static stringType? toStringname?(parameter definitionsparameter definitions?) { // function227 return $"A snake with {this.body.length() + 1} segments"value or expression?;228 } // end function
+Function toStringname?(parameter definitionsparameter definitions?) As StringType? 229 Return $"A snake with {Me.body.length() + 1} segments"value or expression?230 End Function
+static StringType? toStringname?(parameter definitionsparameter definitions?) { // function231 return String.format("A snake with % segments", this.body.length() + 1)value or expression?;232 } // end function
(You might like to try this out, temporarilyadding the instruction print(snake)print(snake)print(snake)print(snake)print(snake) into the main routine.)

A procedure method changes the state of the object, and/or interacts with the system

Procedure methods, like function methods, may define parameters and may also read the values of the object's properties. Like global procedures they may also generate side effects, and this includes changing the values of the object's properties, and may call other procedures, on the same object, or associated objects. For example, the clockTick procedure method defined in the SnakeSnakeSnakeSnakeSnake class, changes the position of various segments, and, if the headheadheadheadhead now covers the same space as the appleappleappleappleapple changes the position of the apple (passed into the method as a parameter) by calling the newRandomPosition procedure method on appleappleappleappleapple:

+procedure clockTickname?(key as String, apple as Appleparameter definitions?) 233 call this.setDirectionprocedureName?(keyarguments?)234 assign this.priorTailvariableName? to this.body[0]value or expression?235 variable bodyname? set to this.bodyvalue or expression?236 call body.appendprocedureName?(this.headarguments?)237 assign this.headvariableName? to this.head.getAdjacentSquare(this.currentDir)value or expression?238 +if this.head.equals(apple.location)condition? then 239 call apple.newRandomPositionprocedureName?(thisarguments?)240 else241 assign this.bodyvariableName? to this.body.subList(1, this.body.length())value or expression?242 end if end procedure
+def clockTickname?(key: str, apple: Appleparameter definitions?) -> None: # procedure243 self.setDirectionprocedureName?(keyarguments?) # procedure call244 self.priorTailvariableName? = self.body[0]value or expression? # assignment245 bodyname? = self.bodyvalue or expression? # variable definition246 body.appendprocedureName?(self.headarguments?) # procedure call247 self.headvariableName? = self.head.getAdjacentSquare(self.currentDir)value or expression? # assignment248 +if self.head.equals(apple.location)condition?: 249 apple.newRandomPositionprocedureName?(selfarguments?) # procedure call250 else:251 self.bodyvariableName? = self.body.subList(1, self.body.length())value or expression? # assignment252 # end if # end procedure
+static void clockTickname?(string key, Apple appleparameter definitions?) { // procedure253 this.setDirectionprocedureName?(keyarguments?); // procedure call254 this.priorTailvariableName? = this.body[0]value or expression?; // assignment255 var bodyname? = this.bodyvalue or expression?;256 body.appendprocedureName?(this.headarguments?); // procedure call257 this.headvariableName? = this.head.getAdjacentSquare(this.currentDir)value or expression?; // assignment258 +if (this.head.equals(apple.location)condition?) { 259 apple.newRandomPositionprocedureName?(thisarguments?); // procedure call260 } else {261 this.bodyvariableName? = this.body.subList(1, this.body.length())value or expression?; // assignment262 } // end if } // end procedure
+Sub clockTickname?(key As String, apple As Appleparameter definitions?) ' procedure263 Me.setDirectionprocedureName?(keyarguments?) ' procedure call264 Me.priorTailvariableName? = Me.body(0)value or expression? ' assignment265 Dim bodyname? = Me.bodyvalue or expression? ' variable definition266 body.appendprocedureName?(Me.headarguments?) ' procedure call267 Me.headvariableName? = Me.head.getAdjacentSquare(Me.currentDir)value or expression? ' assignment268 +If Me.head.equals(apple.location)condition? Then 269 apple.newRandomPositionprocedureName?(Mearguments?) ' procedure call270 Else271 Me.bodyvariableName? = Me.body.subList(1, Me.body.length())value or expression? ' assignment272 End If End Sub
+static void clockTickname?(String key, Apple appleparameter definitions?) { // procedure273 this.setDirectionprocedureName?(keyarguments?); // procedure call274 this.priorTailvariableName? = this.body[0]value or expression?; // assignment275 var bodyname? = this.bodyvalue or expression?;276 body.appendprocedureName?(this.headarguments?); // procedure call277 this.headvariableName? = this.head.getAdjacentSquare(this.currentDir)value or expression?; // assignment278 +if (this.head.equals(apple.location)condition?) { 279 apple.newRandomPositionprocedureName?(thisarguments?); // procedure call280 } else {281 this.bodyvariableName? = this.body.subList(1, this.body.length())value or expression?; // assignment282 } // end if } // end procedure

Objects should represent the principal nouns in the application domain

The word 'encapsulation' is often used in descriptions of object-oriented programming. But that word has two rather different meanings in plain English. Fortunately, both of the meanings of 'encapsulation' apply.

The first meaning of encapsulation is similar to 'represents' - as in 'Our school motto encapsulates the ethos and culture of the school'. (The second meaning will be addressed in the next principle.) Thus, objects should represent, or encapsulate, the principal concepts in the application domain. A rule of thumb is that the classes defined in a program should correspond to the main nouns used when describing the purpose and operation of the program in plain English. You can see this in the Snake demo, where the code defines three classes, named SnakeSnakeSnakeSnakeSnake, AppleAppleAppleAppleApple, and SquareSquareSquareSquareSquare - all nouns that would be recognised by anyone who understands how to play the game Snake, but who does not necessarily know anything about programming.

One of the claimed benefits of this is that it makes programs easier to read, and to update in response to new requirements, because the structure of the code corresponds more closely to the language of the application domain. If the user, or the business customer that owns the game, says that they want the snake to be a different colour, or for there to be two apples at once (in different positions) - it will be easier for the programmer to identity where within the program the code must be changed that in a procedural implementation. You can try this for yourself, by asking a user to specify similar small changes to the presentation and/or game rules, and testing how long it takes you to update the procedural, and object-oriented, versions of the Snake program as required.

Importantly, the class does not just represent all the information associated with the noun - it also encapsulates the behaviour needed from that thing, in the form of methods. This is sometimes stated as representing the know-whats for the thing (as properties), and the know how-to's as methods.

Objects should hide state behind methods, where possible

This principle corresponds to the second meaning of 'encapsulation': sealing. Think of the related word: 'capsule'. You can only access the capabilities of an object through its public members. All the 'members' defined on a class (that is: its properties and methods) are public - although properties are public only for reading, not for modifying. However, it is possible to make any property or method private - such that it can be accessed from code within the class, but not from outside. To do this, you bring up the context menu on the instruction (by right-mouse-click or Ctrl-m) and select make private (a change that can be reversed with make public).A method might be made private because it just provides help to one or more public methods, but is not expected to be useful in its own right. However, a property is often made private to implement the principle of 'information' hiding.

Some textbooks suggest that every property - for example dateOfBirthdateOfBirthdateOfBirthdateOfBirthdateOfBirth - should be private, and that access should be via a 'getter' - a function method typically named getDateOfBirth, and a 'setter' - a procedure method typically named setDateOfBirth. But that idea is not really hiding the information. Real information hiding is to prevent external code from accessing the data and instead provide them with public methods that provide more useful services. For example, in the Snake demo, all the headheadheadheadhead and bodybodybodybodybody properties are private, but the function method bodyCovers allows external code to test whether the snake (body or head) covers a given square, passed in as a parameter. This method is called from within the newRandomPosition defined by the AppleAppleAppleAppleApple class, to ensure that the next apple is not created directly underneath the snake.

A concrete class may optionally inherit from an abstract class

In many applications two or more of the classes will be seen as having quite a lot in common. When this situation identified, there might be a case for making them inherit from a common 'abstract' class. For example, in a school administration system, you might define classes named Pupil, Teacher, and Parent, but these might all inherit from a common abstract class named Individual. The Person class can then be described as the 'superclass' of the other three, and they as the 'subclasses' of Individual.

An abstract class is created, at global level, with an abstract class instruction, and the template requires a name (beginning with a capital letter) to be specified.

The main differences between an abstract and a concrete super-class are:

Earlier we learned about association between objects. Association may be characterised by the 'has a' relationship, as in a 'Student has a personal tutor' (meaning that the Student class defines a property named 'tutor' - which is of type Teacher. Bt contrast inheritance may be characterised as an 'is a' relationship, as in 'a Student is an Individual'.

There are two principal motivations from making two or more classes inherit from a common abstract class:

Some textbooks emphasise the first of those two motivations, but it is the second one that is far more important. Indeed, inheritance is the most over-used, and the most frequently abused, concepts in OOP. For that reason, irrespective of the programming language being used, Elan deliberately enforces principles of good OOP from the outset. For example:

The last point is controversial - since all the supported programming languages allow overriding. But overriding (of concrete methods) breaks the 'O' of the widely-quoted 'SOLID' principles of OOP: that classes should be 'Open for extension, but closed for modification'.

Application logic should be well-distributed across classes

In Elan, an object-oriented program still needs a procedural main method to execute.

The Java language is unusual in that even the main routine must defined within a class - Elan handles this by including a class named Global as part of the file boilerplate, but treats the instructions directly within this class as being 'global' in nature. Any user-defined classes are added within this, and Elan does not allow properties to be added directly within that Global class since these would be, effectively, just global variables in disguise.

In general good OOP design means aiming to keep the functionality well-distributed among the user-defined classes. Inevitably, some classes are going to define more methods, and/or more-complex methods, than others. But you should be wary of a situation where one class is doing most of the work, and the other classes act as little more than data structures without encapsulated behaviour. All too often that one big class is instantiated only once (this pattern is known as a 'singleton'), by the main routine. While singletons are not always a bad idea, a singleton that does most of the work of the program - sometimes irreverently referred to as a 'god class' - is really just a procedural program in disguise.

A useful check on your design is to look at the distribution of instructions in the program (not counting instructions within tests, which are not part of the executable program). For example, the Snake - object-oriented demo has a total of 96 instructions (excluding tests), of which just 12 are not in a user-defined class (they are all in the main routine). The allocation of the other instructions is as follows:

classpropertiesmethods (excl. constructor & toString)No. of instructionsNotes
SnakeSnakeSnakeSnakeSnake4657singleton - but an extended version of the game might have multiple instances.
AppleAppleAppleAppleApple1216ditto
SquareSquareSquareSquareSquare2222Many instances created in the game. Both Snake and Apple have associations to one or more Squares

Global Instructions

Coding with Elan using the object-oriented paradigm offers all the global instructions uses in the procedural paradigm plus two new ones: concrete class and abstract class

Concrete class

Examples of a concrete class

From the Snake - object-oriented demo:

+class AppleName? inheritance?283 +constructor(parameter definitionsparameter definitions?) 284 assign this.locationvariableName? to new Square(0, 0)value or expression?285 end constructor property locationname? as SquareType?286 +procedure newRandomPositionname?(snake as Snakeparameter definitions?) 287 variable changePositionname? set to truevalue or expression?288 +while changePositioncondition? 289 variable ranXname? set to randint(0, 39)value or expression?290 variable ranYname? set to randint(0, 29)value or expression?291 assign this.locationvariableName? to new Square(ranX, ranY)value or expression?292 +if not snake.bodyCovers(this.location)condition? then 293 assign changePositionvariableName? to falsevalue or expression?294 end if end while end procedure +procedure updateBlocksname?(blocks as List<of List<of Int>>parameter definitions?) 295 assign blocks[this.location.x][this.location.y]variableName? to redvalue or expression?296 end procedure +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 297 return $"an Apple at {this.location}"value or expression?298 end function end class
+class AppleName? inheritance?: # concrete class299 +def __init__(self: Apple) -> None: 300 self.locationvariableName? = Square(0, 0)value or expression? # assignment301 # end constructor locationname?: SquareType? # property302 +def newRandomPositionname?(self: Apple, snake: Snakeparameter definitions?) -> None: # procedure method303 changePositionname? = Truevalue or expression? # variable definition304 +while changePositioncondition?: 305 ranXname? = randint(0, 39)value or expression? # variable definition306 ranYname? = randint(0, 29)value or expression? # variable definition307 self.locationvariableName? = Square(ranX, ranY)value or expression? # assignment308 +if not snake.bodyCovers(self.location)condition?: 309 changePositionvariableName? = Falsevalue or expression? # assignment310 # end if # end while # end procedure method +def updateBlocksname?(self: Apple, blocks: list[list[int]]parameter definitions?) -> None: # procedure method311 blocks[self.location.x][self.location.y]variableName? = redvalue or expression? # assignment312 # end procedure method +def toStringname?(self: Apple) -> strType?: # function method313 return f"an Apple at {self.location}"value or expression?314 # end function method # end class
+class AppleName? inheritance? {315 +public Apple(parameter definitionsparameter definitions?) { 316 this.locationvariableName? = new Square(0, 0)value or expression?; // assignment317 } // end constructor public SquareType? locationname? {get; private set;} // property318 +public void newRandomPositionname?(Snake snakeparameter definitions?) { // procedure method319 var changePositionname? = truevalue or expression?;320 +while (changePositioncondition?) { 321 var ranXname? = randint(0, 39)value or expression?;322 var ranYname? = randint(0, 29)value or expression?;323 this.locationvariableName? = new Square(ranX, ranY)value or expression?; // assignment324 +if (!snake.bodyCovers(this.location)condition?) { 325 changePositionvariableName? = falsevalue or expression?; // assignment326 } // end if } // end while } // end procedure method +public void updateBlocksname?(List<List<int>> blocksparameter definitions?) { // procedure method327 blocks[this.location.x][this.location.y]variableName? = redvalue or expression?; // assignment328 } // end procedure method +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method329 return $"an Apple at {this.location}"value or expression?;330 } // end function method } // end class
+Class AppleName? inheritance?331 +Sub New(parameter definitionsparameter definitions?) 332 Me.locationvariableName? = New Square(0, 0)value or expression? ' assignment333 End Sub Property locationname? As SquareType?334 +Sub newRandomPositionname?(snake As Snakeparameter definitions?) ' procedure method335 Dim changePositionname? = Truevalue or expression? ' variable definition336 +While changePositioncondition? 337 Dim ranXname? = randint(0, 39)value or expression? ' variable definition338 Dim ranYname? = randint(0, 29)value or expression? ' variable definition339 Me.locationvariableName? = New Square(ranX, ranY)value or expression? ' assignment340 +If Not snake.bodyCovers(Me.location)condition? Then 341 changePositionvariableName? = Falsevalue or expression? ' assignment342 End If End While End Sub +Sub updateBlocksname?(blocks As List(Of List(Of Integer))parameter definitions?) ' procedure method343 blocks(Me.location.x)(Me.location.y)variableName? = redvalue or expression? ' assignment344 End Sub +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 345 Return $"an Apple at {Me.location}"value or expression?346 End Function End Class
+class AppleName? inheritance? {347 +public Apple(parameter definitionsparameter definitions?) { 348 this.locationvariableName? = new Square(0, 0)value or expression?; // assignment349 } // end constructor public SquareType? locationname?; // property350 +public void newRandomPositionname?(Snake snakeparameter definitions?) { // procedure method351 var changePositionname? = truevalue or expression?;352 +while (changePositioncondition?) { 353 var ranXname? = randint(0, 39)value or expression?;354 var ranYname? = randint(0, 29)value or expression?;355 this.locationvariableName? = new Square(ranX, ranY)value or expression?; // assignment356 +if (!snake.bodyCovers(this.location)condition?) { 357 changePositionvariableName? = falsevalue or expression?; // assignment358 } // end if } // end while } // end procedure method +public void updateBlocksname?(List<List<int>> blocksparameter definitions?) { // procedure method359 blocks[this.location.x][this.location.y]variableName? = redvalue or expression?; // assignment360 } // end procedure method +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method361 return String.format("an Apple at %", this.location)value or expression?;362 } // end function method } // end class

From the Blackjack demo:

+class CardName? inheritance?363 +constructor(rank as String, suit as Suit, facedown as Booleanparameter definitions?) 364 assign this.rankvariableName? to rankvalue or expression?365 assign this.suitvariableName? to suitvalue or expression?366 assign this.faceDownvariableName? to facedownvalue or expression?367 end constructor +procedure turnFaceUpname?(parameter definitionsparameter definitions?) 368 assign this.faceDownvariableName? to falsevalue or expression?369 end procedure +procedure turnFaceDownname?(parameter definitionsparameter definitions?) 370 assign this.faceDownvariableName? to truevalue or expression?371 end procedure property suitname? as SuitType?372 property rankname? as StringType?373 property faceDownname? as BooleanType?374 +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 375 return $"{this.rank}{symbolForSuit(this.suit)}"value or expression?376 end function end class
+class CardName? inheritance?: # concrete class377 +def __init__(self: Card, rank: str, suit: Suit, facedown: boolparameter definitions?) -> None: 378 self.rankvariableName? = rankvalue or expression? # assignment379 self.suitvariableName? = suitvalue or expression? # assignment380 self.faceDownvariableName? = facedownvalue or expression? # assignment381 # end constructor +def turnFaceUpname?(self: Card) -> None: # procedure method382 self.faceDownvariableName? = Falsevalue or expression? # assignment383 # end procedure method +def turnFaceDownname?(self: Card) -> None: # procedure method384 self.faceDownvariableName? = Truevalue or expression? # assignment385 # end procedure method suitname?: SuitType? # property386 rankname?: strType? # property387 faceDownname?: boolType? # property388 +def toStringname?(self: Card) -> strType?: # function method389 return f"{self.rank}{symbolForSuit(self.suit)}"value or expression?390 # end function method # end class
+class CardName? inheritance? {391 +public Card(string rank, Suit suit, bool facedownparameter definitions?) { 392 this.rankvariableName? = rankvalue or expression?; // assignment393 this.suitvariableName? = suitvalue or expression?; // assignment394 this.faceDownvariableName? = facedownvalue or expression?; // assignment395 } // end constructor +public void turnFaceUpname?(parameter definitionsparameter definitions?) { // procedure method396 this.faceDownvariableName? = falsevalue or expression?; // assignment397 } // end procedure method +public void turnFaceDownname?(parameter definitionsparameter definitions?) { // procedure method398 this.faceDownvariableName? = truevalue or expression?; // assignment399 } // end procedure method public SuitType? suitname? {get; private set;} // property400 public stringType? rankname? {get; private set;} // property401 public boolType? faceDownname? {get; private set;} // property402 +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method403 return $"{this.rank}{symbolForSuit(this.suit)}"value or expression?;404 } // end function method } // end class
+Class CardName? inheritance?405 +Sub New(rank As String, suit As Suit, facedown As Booleanparameter definitions?) 406 Me.rankvariableName? = rankvalue or expression? ' assignment407 Me.suitvariableName? = suitvalue or expression? ' assignment408 Me.faceDownvariableName? = facedownvalue or expression? ' assignment409 End Sub +Sub turnFaceUpname?(parameter definitionsparameter definitions?) ' procedure method410 Me.faceDownvariableName? = Falsevalue or expression? ' assignment411 End Sub +Sub turnFaceDownname?(parameter definitionsparameter definitions?) ' procedure method412 Me.faceDownvariableName? = Truevalue or expression? ' assignment413 End Sub Property suitname? As SuitType?414 Property rankname? As StringType?415 Property faceDownname? As BooleanType?416 +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 417 Return $"{Me.rank}{symbolForSuit(Me.suit)}"value or expression?418 End Function End Class
+class CardName? inheritance? {419 +public Card(String rank, Suit suit, boolean facedownparameter definitions?) { 420 this.rankvariableName? = rankvalue or expression?; // assignment421 this.suitvariableName? = suitvalue or expression?; // assignment422 this.faceDownvariableName? = facedownvalue or expression?; // assignment423 } // end constructor +public void turnFaceUpname?(parameter definitionsparameter definitions?) { // procedure method424 this.faceDownvariableName? = falsevalue or expression?; // assignment425 } // end procedure method +public void turnFaceDownname?(parameter definitionsparameter definitions?) { // procedure method426 this.faceDownvariableName? = truevalue or expression?; // assignment427 } // end procedure method public SuitType? suitname?; // property428 public StringType? rankname?; // property429 public booleanType? faceDownname?; // property430 +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method431 return String.format("%%", this.rank, symbolForSuit(this.suit))value or expression?;432 } // end function method } // end class

From the Pathfinder demo:

+class PointName? inheritance?433 +constructor(x as Int, y as Intparameter definitions?) 434 +if (x < 0) or (y < 0)condition? then 435 assign this.isEmptyvariableName? to truevalue or expression?436 else437 assign this.xvariableName? to xvalue or expression?438 assign this.yvariableName? to yvalue or expression?439 end if end constructor +function minDistToname?(p as Pointparameter definitions?) returns FloatType? 440 return sqrt(pow((p.x - this.x), 2) + pow((p.y - this.y), 2))value or expression?441 end function +function isAdjacentToname?(p as Pointparameter definitions?) returns BooleanType? 442 return (this.minDistTo(p) is 1) or (this.minDistTo(p).round(4) is sqrt(2).round(4))value or expression?443 end function # Returns the 8 theoretically-neighbouring points, whether or not within boundscomment? +function neighbouringPointsname?(parameter definitionsparameter definitions?) returns List<of Point>Type? 444 return [new Point(this.x - 1, this.y - 1), new Point(this.x, this.y - 1), new Point(this.x + 1, this.y - 1), new Point(this.x - 1, this.y), new Point(this.x + 1, this.y), new Point(this.x - 1, this.y + 1), new Point(this.x, this.y + 1), new Point(this.x + 1, this.y + 1)]value or expression?445 end function property xname? as IntType?446 property yname? as IntType?447 property isEmptyname? as BooleanType?448 +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 449 return $"{this.x},{this.y}"value or expression?450 end function end class
+class PointName? inheritance?: # concrete class451 +def __init__(self: Point, x: int, y: intparameter definitions?) -> None: 452 +if (x < 0) or (y < 0)condition?: 453 self.isEmptyvariableName? = Truevalue or expression? # assignment454 else:455 self.xvariableName? = xvalue or expression? # assignment456 self.yvariableName? = yvalue or expression? # assignment457 # end if # end constructor +def minDistToname?(self: Point, p: Pointparameter definitions?) -> floatType?: # function method458 return sqrt(pow((p.x - self.x), 2) + pow((p.y - self.y), 2))value or expression?459 # end function method +def isAdjacentToname?(self: Point, p: Pointparameter definitions?) -> boolType?: # function method460 return (self.minDistTo(p) == 1) or (self.minDistTo(p).round(4) == sqrt(2).round(4))value or expression?461 # end function method # Returns the 8 theoretically-neighbouring points, whether or not within boundscomment? +def neighbouringPointsname?(self: Point) -> list[Point]Type?: # function method462 return [Point(self.x - 1, self.y - 1), Point(self.x, self.y - 1), Point(self.x + 1, self.y - 1), Point(self.x - 1, self.y), Point(self.x + 1, self.y), Point(self.x - 1, self.y + 1), Point(self.x, self.y + 1), Point(self.x + 1, self.y + 1)]value or expression?463 # end function method xname?: intType? # property464 yname?: intType? # property465 isEmptyname?: boolType? # property466 +def toStringname?(self: Point) -> strType?: # function method467 return f"{self.x},{self.y}"value or expression?468 # end function method # end class
+class PointName? inheritance? {469 +public Point(int x, int yparameter definitions?) { 470 +if ((x < 0) || (y < 0)condition?) { 471 this.isEmptyvariableName? = truevalue or expression?; // assignment472 } else {473 this.xvariableName? = xvalue or expression?; // assignment474 this.yvariableName? = yvalue or expression?; // assignment475 } // end if } // end constructor +public doubleType? minDistToname?(Point pparameter definitions?) { // function method476 return sqrt(pow((p.x - this.x), 2) + pow((p.y - this.y), 2))value or expression?;477 } // end function method +public boolType? isAdjacentToname?(Point pparameter definitions?) { // function method478 return (this.minDistTo(p) == 1) || (this.minDistTo(p).round(4) == sqrt(2).round(4))value or expression?;479 } // end function method // Returns the 8 theoretically-neighbouring points, whether or not within boundscomment? +public List<Point>Type? neighbouringPointsname?(parameter definitionsparameter definitions?) { // function method480 return new [] {new Point(this.x - 1, this.y - 1), new Point(this.x, this.y - 1), new Point(this.x + 1, this.y - 1), new Point(this.x - 1, this.y), new Point(this.x + 1, this.y), new Point(this.x - 1, this.y + 1), new Point(this.x, this.y + 1), new Point(this.x + 1, this.y + 1)}value or expression?;481 } // end function method public intType? xname? {get; private set;} // property482 public intType? yname? {get; private set;} // property483 public boolType? isEmptyname? {get; private set;} // property484 +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method485 return $"{this.x},{this.y}"value or expression?;486 } // end function method } // end class
+Class PointName? inheritance?487 +Sub New(x As Integer, y As Integerparameter definitions?) 488 +If (x < 0) Or (y < 0)condition? Then 489 Me.isEmptyvariableName? = Truevalue or expression? ' assignment490 Else491 Me.xvariableName? = xvalue or expression? ' assignment492 Me.yvariableName? = yvalue or expression? ' assignment493 End If End Sub +Function minDistToname?(p As Pointparameter definitions?) As DoubleType? 494 Return sqrt(pow((p.x - Me.x), 2) + pow((p.y - Me.y), 2))value or expression?495 End Function +Function isAdjacentToname?(p As Pointparameter definitions?) As BooleanType? 496 Return (Me.minDistTo(p) = 1) Or (Me.minDistTo(p).round(4) = sqrt(2).round(4))value or expression?497 End Function ' Returns the 8 theoretically-neighbouring points, whether or not within boundscomment? +Function neighbouringPointsname?(parameter definitionsparameter definitions?) As List(Of Point)Type? 498 Return {New Point(Me.x - 1, Me.y - 1), New Point(Me.x, Me.y - 1), New Point(Me.x + 1, Me.y - 1), New Point(Me.x - 1, Me.y), New Point(Me.x + 1, Me.y), New Point(Me.x - 1, Me.y + 1), New Point(Me.x, Me.y + 1), New Point(Me.x + 1, Me.y + 1)}value or expression?499 End Function Property xname? As IntegerType?500 Property yname? As IntegerType?501 Property isEmptyname? As BooleanType?502 +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 503 Return $"{Me.x},{Me.y}"value or expression?504 End Function End Class
+class PointName? inheritance? {505 +public Point(int x, int yparameter definitions?) { 506 +if ((x < 0) || (y < 0)condition?) { 507 this.isEmptyvariableName? = truevalue or expression?; // assignment508 } else {509 this.xvariableName? = xvalue or expression?; // assignment510 this.yvariableName? = yvalue or expression?; // assignment511 } // end if } // end constructor +public doubleType? minDistToname?(Point pparameter definitions?) { // function method512 return sqrt(pow((p.x - this.x), 2) + pow((p.y - this.y), 2))value or expression?;513 } // end function method +public booleanType? isAdjacentToname?(Point pparameter definitions?) { // function method514 return (this.minDistTo(p) == 1) || (this.minDistTo(p).round(4) == sqrt(2).round(4))value or expression?;515 } // end function method // Returns the 8 theoretically-neighbouring points, whether or not within boundscomment? +public List<Point>Type? neighbouringPointsname?(parameter definitionsparameter definitions?) { // function method516 return list(new Point(this.x - 1, this.y - 1), new Point(this.x, this.y - 1), new Point(this.x + 1, this.y - 1), new Point(this.x - 1, this.y), new Point(this.x + 1, this.y), new Point(this.x - 1, this.y + 1), new Point(this.x, this.y + 1), new Point(this.x + 1, this.y + 1))value or expression?;517 } // end function method public intType? xname?; // property518 public intType? yname?; // property519 public booleanType? isEmptyname?; // property520 +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method521 return String.format("%,%", this.x, this.y)value or expression?;522 } // end function method } // end class

What you need to know about a concrete class

  • A concrete class is a type of user-defined data-structure that holds data in properties, as well as methods to provide behaviour.
  • A new class is defined by adding a concrete class instruction at global level, which requires a name (starting with an initial capital) to be specified
  • All concrete classes must define a constructor, and a toString method, but the template for the class provides minimal valid implementations of these which you may expand or modify.
  • You create an instance of the class using the class name and providing any require arguments (as specified by the class' constructor) for example: variable applename? set to new Apple()value or expression?5applename? = Apple()value or expression? # variable definition6var applename? = new Apple()value or expression?;7Dim applename? = New Apple()value or expression? ' variable definition8var applename? = new Apple()value or expression?;9 or variable startname? set to new Point(5, 5)value or expression?10startname? = Point(5, 5)value or expression? # variable definition11var startname? = new Point(5, 5)value or expression?;12Dim startname? = New Point(5, 5)value or expression? ' variable definition13var startname? = new Point(5, 5)value or expression?;14.
  • All properties must be initialised within the constructor (except for numeric or Boolean properties which have the default value for their type until changed)
  • Function methods provide information derived from the properties of the instance, and or from parameters provided to it.
  • toString is an ordinary function method but with a specific name; it is called if an instance of the class is printed, or inserted into an interpolated string; typically toString will include one or more of the object's properties.
  • Procedure methods make changes to the properties of the instance and/or depend on the system in some way.
  • Any property or method may be made private (using an action on the context menu for the instruction), such that it may only be accessed by code within the same class.
  • Once you have an instance in a variable you can access its public methods using dot-syntax; however, properties may be only be read by external code - to modify properties from outside you need to go through a procedure method defined on that class.
  • A class may optionally inherit from a single abstract class.

Abstract class

Example of an abstract class

From the Blackjack demo, the abstract class PlayerPlayerPlayerPlayerPlayer - which is inherited by the concrete classes HumanPlayerHumanPlayerHumanPlayerHumanPlayerHumanPlayer and DealerDealerDealerDealerDealer - defines public concrete properties, public concrete methods, plus three abstract methods, two of which are accompanied by private 'helper' methods to which subclasses may delegate part or all of their implementations.

+abstract class PlayerName? inheritance?523 property namename? as StringType?524 property pointsname? as IntType?525 property cardsname? as List<of Card>Type?526 property handTotalname? as IntType?527 property softAcename? as BooleanType?528 property statusname? as StatusType?529 property hasTurnname? as BooleanType?530 +procedure startTurnname?(parameter definitionsparameter definitions?) 531 +if this.status is Status.activecondition? then 532 assign this.hasTurnvariableName? to truevalue or expression?533 end if end procedure +procedure determineOutcomeAndUpdatePointsname?(dealer as Dealerparameter definitions?) 534 variable playerOutcomename? set to determinePlayerOutcome(dealer, this)value or expression?535 +if playerOutcome is Outcome.winDoublecondition? then 536 call this.changePointsByprocedureName?(2arguments?)537 call dealer.changePointsByprocedureName?(-2arguments?)538 elif playerOutcome is Outcome.wincondition? then539 call this.changePointsByprocedureName?(1arguments?)540 call dealer.changePointsByprocedureName?(-1arguments?)541 elif playerOutcome is Outcome.losecondition? then542 call this.changePointsByprocedureName?(-1arguments?)543 call dealer.changePointsByprocedureName?(1arguments?)544 end if end procedure +procedure evaluateStatusname?(newCard as Cardparameter definitions?) 545 +if (this.cardCount() is 2) and (this.handTotal is 21)condition? then 546 assign this.statusvariableName? to Status.blackjackvalue or expression?547 elif (this.handTotal > 21) and (this.softAce)condition? then548 assign this.handTotalvariableName? to this.handTotal - 10value or expression?549 assign this.softAcevariableName? to falsevalue or expression?550 elif this.handTotal > 21condition? then551 assign this.statusvariableName? to Status.bustvalue or expression?552 elif this.handTotal is 21condition? then553 assign this.statusvariableName? to Status.standingvalue or expression?554 end if +if this.status isnt Status.activecondition? then 555 assign this.hasTurnvariableName? to falsevalue or expression?556 end if end procedure +procedure standname?(parameter definitionsparameter definitions?) 557 assign this.statusvariableName? to Status.standingvalue or expression?558 assign this.hasTurnvariableName? to falsevalue or expression?559 end procedure +procedure drawname?(parameter definitionsparameter definitions?) 560 variable newCardname? set to dealCard(random())value or expression?561 variable cardsname? set to this.cardsvalue or expression?562 call cards.appendprocedureName?(newCardarguments?)563 +if newCard.rank.equals("A")condition? then 564 call this.addAceprocedureName?(arguments?)565 else566 assign this.handTotalvariableName? to this.handTotal + rankValue()[newCard.rank]value or expression?567 end if call this.evaluateStatusprocedureName?(newCardarguments?)568 end procedure +procedure addAcename?(parameter definitionsparameter definitions?) 569 +if this.softAcecondition? then 570 assign this.handTotalvariableName? to this.handTotal + 1value or expression?571 else572 assign this.handTotalvariableName? to this.handTotal + 11value or expression?573 assign this.softAcevariableName? to truevalue or expression?574 end if end procedure +function cardCountname?(parameter definitionsparameter definitions?) returns IntType? 575 return this.cards.length()value or expression?576 end function +procedure changePointsByname?(amount as Intparameter definitions?) 577 assign this.pointsvariableName? to this.points + amountvalue or expression?578 end procedure abstract procedure newHandname?(parameter definitionsparameter definitions?)579 +private procedure newHandHelpername?(parameter definitionsparameter definitions?) 580 assign this.hasTurnvariableName? to falsevalue or expression?581 assign this.softAcevariableName? to falsevalue or expression?582 assign this.cardsvariableName? to new List<of Card>()value or expression?583 assign this.handTotalvariableName? to 0value or expression?584 assign this.statusvariableName? to Status.activevalue or expression?585 call this.drawprocedureName?(arguments?)586 call this.drawprocedureName?(arguments?)587 end procedure abstract function getMessagename?(parameter definitionsparameter definitions?) returns StringType?588 +private function getMessageHelpername?(parameter definitionsparameter definitions?) returns StringType? 589 variable msgname? set to ""value or expression?590 variable statusname? set to this.statusvalue or expression?591 +if this.hasTurncondition? then 592 assign msgvariableName? to msg + " - PLAYING"value or expression?593 elif status is Status.standingcondition? then594 assign msgvariableName? to msg + " - STANDING"value or expression?595 elif status is Status.blackjackcondition? then596 assign msgvariableName? to msg + " - BLACKJACK"value or expression?597 elif status is Status.bustcondition? then598 assign msgvariableName? to msg + " - BUST"value or expression?599 end if return msgvalue or expression?600 end function abstract procedure nextActionname?(dealerFaceCard as Cardparameter definitions?)601 end abstract class
+class PlayerName?(ABC) inheritance?: # abstract class602 namename?: strType? # property603 pointsname?: intType? # property604 cardsname?: list[Card]Type? # property605 handTotalname?: intType? # property606 softAcename?: boolType? # property607 statusname?: StatusType? # property608 hasTurnname?: boolType? # property609 +def startTurnname?(self: Player) -> None: # procedure method610 +if self.status == Status.activecondition?: 611 self.hasTurnvariableName? = Truevalue or expression? # assignment612 # end if # end procedure method +def determineOutcomeAndUpdatePointsname?(self: Player, dealer: Dealerparameter definitions?) -> None: # procedure method613 playerOutcomename? = determinePlayerOutcome(dealer, self)value or expression? # variable definition614 +if playerOutcome == Outcome.winDoublecondition?: 615 self.changePointsByprocedureName?(2arguments?) # procedure call616 dealer.changePointsByprocedureName?(-2arguments?) # procedure call617 elif playerOutcome == Outcome.wincondition?: # else if618 self.changePointsByprocedureName?(1arguments?) # procedure call619 dealer.changePointsByprocedureName?(-1arguments?) # procedure call620 elif playerOutcome == Outcome.losecondition?: # else if621 self.changePointsByprocedureName?(-1arguments?) # procedure call622 dealer.changePointsByprocedureName?(1arguments?) # procedure call623 # end if # end procedure method +def evaluateStatusname?(self: Player, newCard: Cardparameter definitions?) -> None: # procedure method624 +if (self.cardCount() == 2) and (self.handTotal == 21)condition?: 625 self.statusvariableName? = Status.blackjackvalue or expression? # assignment626 elif (self.handTotal > 21) and (self.softAce)condition?: # else if627 self.handTotalvariableName? = self.handTotal - 10value or expression? # assignment628 self.softAcevariableName? = Falsevalue or expression? # assignment629 elif self.handTotal > 21condition?: # else if630 self.statusvariableName? = Status.bustvalue or expression? # assignment631 elif self.handTotal == 21condition?: # else if632 self.statusvariableName? = Status.standingvalue or expression? # assignment633 # end if +if self.status != Status.activecondition?: 634 self.hasTurnvariableName? = Falsevalue or expression? # assignment635 # end if # end procedure method +def standname?(self: Player) -> None: # procedure method636 self.statusvariableName? = Status.standingvalue or expression? # assignment637 self.hasTurnvariableName? = Falsevalue or expression? # assignment638 # end procedure method +def drawname?(self: Player) -> None: # procedure method639 newCardname? = dealCard(random())value or expression? # variable definition640 cardsname? = self.cardsvalue or expression? # variable definition641 cards.appendprocedureName?(newCardarguments?) # procedure call642 +if newCard.rank.equals("A")condition?: 643 self.addAceprocedureName?(arguments?) # procedure call644 else:645 self.handTotalvariableName? = self.handTotal + rankValue()[newCard.rank]value or expression? # assignment646 # end if self.evaluateStatusprocedureName?(newCardarguments?) # procedure call647 # end procedure method +def addAcename?(self: Player) -> None: # procedure method648 +if self.softAcecondition?: 649 self.handTotalvariableName? = self.handTotal + 1value or expression? # assignment650 else:651 self.handTotalvariableName? = self.handTotal + 11value or expression? # assignment652 self.softAcevariableName? = Truevalue or expression? # assignment653 # end if # end procedure method +def cardCountname?(self: Player) -> intType?: # function method654 return self.cards.length()value or expression?655 # end function method +def changePointsByname?(self: Player, amount: intparameter definitions?) -> None: # procedure method656 self.pointsvariableName? = self.points + amountvalue or expression? # assignment657 # end procedure method @abstractmethod
def newHandname?(parameter definitionsparameter definitions?) -> None
  pass
# abstract procedure658
+def newHandHelpername?(self: Player) -> None: # private procedure method659 self.hasTurnvariableName? = Falsevalue or expression? # assignment660 self.softAcevariableName? = Falsevalue or expression? # assignment661 self.cardsvariableName? = list[Card]()value or expression? # assignment662 self.handTotalvariableName? = 0value or expression? # assignment663 self.statusvariableName? = Status.activevalue or expression? # assignment664 self.drawprocedureName?(arguments?) # procedure call665 self.drawprocedureName?(arguments?) # procedure call666 # end procedure method @abstractmethod
def getMessagename?(parameter definitionsparameter definitions?) -> strType?:
  pass
# abstract function667
+def getMessageHelpername?(self: Player) -> strType?: # private function method668 msgname? = ""value or expression? # variable definition669 statusname? = self.statusvalue or expression? # variable definition670 +if self.hasTurncondition?: 671 msgvariableName? = msg + " - PLAYING"value or expression? # assignment672 elif status == Status.standingcondition?: # else if673 msgvariableName? = msg + " - STANDING"value or expression? # assignment674 elif status == Status.blackjackcondition?: # else if675 msgvariableName? = msg + " - BLACKJACK"value or expression? # assignment676 elif status == Status.bustcondition?: # else if677 msgvariableName? = msg + " - BUST"value or expression? # assignment678 # end if return msgvalue or expression?679 # end function method @abstractmethod
def nextActionname?(dealerFaceCard: Cardparameter definitions?) -> None
  pass
# abstract procedure680
# end class
+abstract class PlayerName? inheritance? {681 public stringType? namename? {get; private set;} // property682 public intType? pointsname? {get; private set;} // property683 public List<Card>Type? cardsname? {get; private set;} // property684 public intType? handTotalname? {get; private set;} // property685 public boolType? softAcename? {get; private set;} // property686 public StatusType? statusname? {get; private set;} // property687 public boolType? hasTurnname? {get; private set;} // property688 +public void startTurnname?(parameter definitionsparameter definitions?) { // procedure method689 +if (this.status == Status.activecondition?) { 690 this.hasTurnvariableName? = truevalue or expression?; // assignment691 } // end if } // end procedure method +public void determineOutcomeAndUpdatePointsname?(Dealer dealerparameter definitions?) { // procedure method692 var playerOutcomename? = determinePlayerOutcome(dealer, this)value or expression?;693 +if (playerOutcome == Outcome.winDoublecondition?) { 694 this.changePointsByprocedureName?(2arguments?); // procedure call695 dealer.changePointsByprocedureName?(-2arguments?); // procedure call696 } else if (playerOutcome == Outcome.wincondition?) {697 this.changePointsByprocedureName?(1arguments?); // procedure call698 dealer.changePointsByprocedureName?(-1arguments?); // procedure call699 } else if (playerOutcome == Outcome.losecondition?) {700 this.changePointsByprocedureName?(-1arguments?); // procedure call701 dealer.changePointsByprocedureName?(1arguments?); // procedure call702 } // end if } // end procedure method +public void evaluateStatusname?(Card newCardparameter definitions?) { // procedure method703 +if ((this.cardCount() == 2) && (this.handTotal == 21)condition?) { 704 this.statusvariableName? = Status.blackjackvalue or expression?; // assignment705 } else if ((this.handTotal > 21) && (this.softAce)condition?) {706 this.handTotalvariableName? = this.handTotal - 10value or expression?; // assignment707 this.softAcevariableName? = falsevalue or expression?; // assignment708 } else if (this.handTotal > 21condition?) {709 this.statusvariableName? = Status.bustvalue or expression?; // assignment710 } else if (this.handTotal == 21condition?) {711 this.statusvariableName? = Status.standingvalue or expression?; // assignment712 } // end if +if (this.status != Status.activecondition?) { 713 this.hasTurnvariableName? = falsevalue or expression?; // assignment714 } // end if } // end procedure method +public void standname?(parameter definitionsparameter definitions?) { // procedure method715 this.statusvariableName? = Status.standingvalue or expression?; // assignment716 this.hasTurnvariableName? = falsevalue or expression?; // assignment717 } // end procedure method +public void drawname?(parameter definitionsparameter definitions?) { // procedure method718 var newCardname? = dealCard(random())value or expression?;719 var cardsname? = this.cardsvalue or expression?;720 cards.appendprocedureName?(newCardarguments?); // procedure call721 +if (newCard.rank.equals("A")condition?) { 722 this.addAceprocedureName?(arguments?); // procedure call723 } else {724 this.handTotalvariableName? = this.handTotal + rankValue()[newCard.rank]value or expression?; // assignment725 } // end if this.evaluateStatusprocedureName?(newCardarguments?); // procedure call726 } // end procedure method +public void addAcename?(parameter definitionsparameter definitions?) { // procedure method727 +if (this.softAcecondition?) { 728 this.handTotalvariableName? = this.handTotal + 1value or expression?; // assignment729 } else {730 this.handTotalvariableName? = this.handTotal + 11value or expression?; // assignment731 this.softAcevariableName? = truevalue or expression?; // assignment732 } // end if } // end procedure method +public intType? cardCountname?(parameter definitionsparameter definitions?) { // function method733 return this.cards.length()value or expression?;734 } // end function method +public void changePointsByname?(int amountparameter definitions?) { // procedure method735 this.pointsvariableName? = this.points + amountvalue or expression?; // assignment736 } // end procedure method abstract void newHandname?(parameter definitionsparameter definitions?); // abstract procedure737 +protected void newHandHelpername?(parameter definitionsparameter definitions?) { // private procedure method738 this.hasTurnvariableName? = falsevalue or expression?; // assignment739 this.softAcevariableName? = falsevalue or expression?; // assignment740 this.cardsvariableName? = new List<Card>()value or expression?; // assignment741 this.handTotalvariableName? = 0value or expression?; // assignment742 this.statusvariableName? = Status.activevalue or expression?; // assignment743 this.drawprocedureName?(arguments?); // procedure call744 this.drawprocedureName?(arguments?); // procedure call745 } // end procedure method abstract stringType? getMessagename?(parameter definitionsparameter definitions?); // abstract function746 +protected stringType? getMessageHelpername?(parameter definitionsparameter definitions?) { // private function method747 var msgname? = ""value or expression?;748 var statusname? = this.statusvalue or expression?;749 +if (this.hasTurncondition?) { 750 msgvariableName? = msg + " - PLAYING"value or expression?; // assignment751 } else if (status == Status.standingcondition?) {752 msgvariableName? = msg + " - STANDING"value or expression?; // assignment753 } else if (status == Status.blackjackcondition?) {754 msgvariableName? = msg + " - BLACKJACK"value or expression?; // assignment755 } else if (status == Status.bustcondition?) {756 msgvariableName? = msg + " - BUST"value or expression?; // assignment757 } // end if return msgvalue or expression?;758 } // end function method abstract void nextActionname?(Card dealerFaceCardparameter definitions?); // abstract procedure759 } // end class
+MustInherit Class PlayerName? inheritance?760 Property namename? As StringType?761 Property pointsname? As IntegerType?762 Property cardsname? As List(Of Card)Type?763 Property handTotalname? As IntegerType?764 Property softAcename? As BooleanType?765 Property statusname? As StatusType?766 Property hasTurnname? As BooleanType?767 +Sub startTurnname?(parameter definitionsparameter definitions?) ' procedure method768 +If Me.status = Status.activecondition? Then 769 Me.hasTurnvariableName? = Truevalue or expression? ' assignment770 End If End Sub +Sub determineOutcomeAndUpdatePointsname?(dealer As Dealerparameter definitions?) ' procedure method771 Dim playerOutcomename? = determinePlayerOutcome(dealer, Me)value or expression? ' variable definition772 +If playerOutcome = Outcome.winDoublecondition? Then 773 Me.changePointsByprocedureName?(2arguments?) ' procedure call774 dealer.changePointsByprocedureName?(-2arguments?) ' procedure call775 ElseIf playerOutcome = Outcome.wincondition? Then776 Me.changePointsByprocedureName?(1arguments?) ' procedure call777 dealer.changePointsByprocedureName?(-1arguments?) ' procedure call778 ElseIf playerOutcome = Outcome.losecondition? Then779 Me.changePointsByprocedureName?(-1arguments?) ' procedure call780 dealer.changePointsByprocedureName?(1arguments?) ' procedure call781 End If End Sub +Sub evaluateStatusname?(newCard As Cardparameter definitions?) ' procedure method782 +If (Me.cardCount() = 2) And (Me.handTotal = 21)condition? Then 783 Me.statusvariableName? = Status.blackjackvalue or expression? ' assignment784 ElseIf (Me.handTotal > 21) And (Me.softAce)condition? Then785 Me.handTotalvariableName? = Me.handTotal - 10value or expression? ' assignment786 Me.softAcevariableName? = Falsevalue or expression? ' assignment787 ElseIf Me.handTotal > 21condition? Then788 Me.statusvariableName? = Status.bustvalue or expression? ' assignment789 ElseIf Me.handTotal = 21condition? Then790 Me.statusvariableName? = Status.standingvalue or expression? ' assignment791 End If +If Me.status <> Status.activecondition? Then 792 Me.hasTurnvariableName? = Falsevalue or expression? ' assignment793 End If End Sub +Sub standname?(parameter definitionsparameter definitions?) ' procedure method794 Me.statusvariableName? = Status.standingvalue or expression? ' assignment795 Me.hasTurnvariableName? = Falsevalue or expression? ' assignment796 End Sub +Sub drawname?(parameter definitionsparameter definitions?) ' procedure method797 Dim newCardname? = dealCard(random())value or expression? ' variable definition798 Dim cardsname? = Me.cardsvalue or expression? ' variable definition799 cards.appendprocedureName?(newCardarguments?) ' procedure call800 +If newCard.rank.equals("A")condition? Then 801 Me.addAceprocedureName?(arguments?) ' procedure call802 Else803 Me.handTotalvariableName? = Me.handTotal + rankValue()(newCard.rank)value or expression? ' assignment804 End If Me.evaluateStatusprocedureName?(newCardarguments?) ' procedure call805 End Sub +Sub addAcename?(parameter definitionsparameter definitions?) ' procedure method806 +If Me.softAcecondition? Then 807 Me.handTotalvariableName? = Me.handTotal + 1value or expression? ' assignment808 Else809 Me.handTotalvariableName? = Me.handTotal + 11value or expression? ' assignment810 Me.softAcevariableName? = Truevalue or expression? ' assignment811 End If End Sub +Function cardCountname?(parameter definitionsparameter definitions?) As IntegerType? 812 Return Me.cards.length()value or expression?813 End Function +Sub changePointsByname?(amount As Integerparameter definitions?) ' procedure method814 Me.pointsvariableName? = Me.points + amountvalue or expression? ' assignment815 End Sub MustOverride Sub newHandname?(parameter definitionsparameter definitions?)816 +Protected Sub newHandHelpername?(parameter definitionsparameter definitions?) ' private procedure method817 Me.hasTurnvariableName? = Falsevalue or expression? ' assignment818 Me.softAcevariableName? = Falsevalue or expression? ' assignment819 Me.cardsvariableName? = New List(Of Card)()value or expression? ' assignment820 Me.handTotalvariableName? = 0value or expression? ' assignment821 Me.statusvariableName? = Status.activevalue or expression? ' assignment822 Me.drawprocedureName?(arguments?) ' procedure call823 Me.drawprocedureName?(arguments?) ' procedure call824 End Sub MustOverride Function getMessagename?(parameter definitionsparameter definitions?) As StringType?825 +Protected Function getMessageHelpername?(parameter definitionsparameter definitions?) As StringType? 826 Dim msgname? = ""value or expression? ' variable definition827 Dim statusname? = Me.statusvalue or expression? ' variable definition828 +If Me.hasTurncondition? Then 829 msgvariableName? = msg + " - PLAYING"value or expression? ' assignment830 ElseIf status = Status.standingcondition? Then831 msgvariableName? = msg + " - STANDING"value or expression? ' assignment832 ElseIf status = Status.blackjackcondition? Then833 msgvariableName? = msg + " - BLACKJACK"value or expression? ' assignment834 ElseIf status = Status.bustcondition? Then835 msgvariableName? = msg + " - BUST"value or expression? ' assignment836 End If Return msgvalue or expression?837 End Function MustOverride Sub nextActionname?(dealerFaceCard As Cardparameter definitions?)838 End Class
+abstract class PlayerName? inheritance? {839 public StringType? namename?; // property840 public intType? pointsname?; // property841 public List<Card>Type? cardsname?; // property842 public intType? handTotalname?; // property843 public booleanType? softAcename?; // property844 public StatusType? statusname?; // property845 public booleanType? hasTurnname?; // property846 +public void startTurnname?(parameter definitionsparameter definitions?) { // procedure method847 +if (this.status == Status.activecondition?) { 848 this.hasTurnvariableName? = truevalue or expression?; // assignment849 } // end if } // end procedure method +public void determineOutcomeAndUpdatePointsname?(Dealer dealerparameter definitions?) { // procedure method850 var playerOutcomename? = determinePlayerOutcome(dealer, this)value or expression?;851 +if (playerOutcome == Outcome.winDoublecondition?) { 852 this.changePointsByprocedureName?(2arguments?); // procedure call853 dealer.changePointsByprocedureName?(-2arguments?); // procedure call854 } else if (playerOutcome == Outcome.wincondition?) {855 this.changePointsByprocedureName?(1arguments?); // procedure call856 dealer.changePointsByprocedureName?(-1arguments?); // procedure call857 } else if (playerOutcome == Outcome.losecondition?) {858 this.changePointsByprocedureName?(-1arguments?); // procedure call859 dealer.changePointsByprocedureName?(1arguments?); // procedure call860 } // end if } // end procedure method +public void evaluateStatusname?(Card newCardparameter definitions?) { // procedure method861 +if ((this.cardCount() == 2) && (this.handTotal == 21)condition?) { 862 this.statusvariableName? = Status.blackjackvalue or expression?; // assignment863 } else if ((this.handTotal > 21) && (this.softAce)condition?) {864 this.handTotalvariableName? = this.handTotal - 10value or expression?; // assignment865 this.softAcevariableName? = falsevalue or expression?; // assignment866 } else if (this.handTotal > 21condition?) {867 this.statusvariableName? = Status.bustvalue or expression?; // assignment868 } else if (this.handTotal == 21condition?) {869 this.statusvariableName? = Status.standingvalue or expression?; // assignment870 } // end if +if (this.status != Status.activecondition?) { 871 this.hasTurnvariableName? = falsevalue or expression?; // assignment872 } // end if } // end procedure method +public void standname?(parameter definitionsparameter definitions?) { // procedure method873 this.statusvariableName? = Status.standingvalue or expression?; // assignment874 this.hasTurnvariableName? = falsevalue or expression?; // assignment875 } // end procedure method +public void drawname?(parameter definitionsparameter definitions?) { // procedure method876 var newCardname? = dealCard(random())value or expression?;877 var cardsname? = this.cardsvalue or expression?;878 cards.appendprocedureName?(newCardarguments?); // procedure call879 +if (newCard.rank.equals("A")condition?) { 880 this.addAceprocedureName?(arguments?); // procedure call881 } else {882 this.handTotalvariableName? = this.handTotal + rankValue()[newCard.rank]value or expression?; // assignment883 } // end if this.evaluateStatusprocedureName?(newCardarguments?); // procedure call884 } // end procedure method +public void addAcename?(parameter definitionsparameter definitions?) { // procedure method885 +if (this.softAcecondition?) { 886 this.handTotalvariableName? = this.handTotal + 1value or expression?; // assignment887 } else {888 this.handTotalvariableName? = this.handTotal + 11value or expression?; // assignment889 this.softAcevariableName? = truevalue or expression?; // assignment890 } // end if } // end procedure method +public intType? cardCountname?(parameter definitionsparameter definitions?) { // function method891 return this.cards.length()value or expression?;892 } // end function method +public void changePointsByname?(int amountparameter definitions?) { // procedure method893 this.pointsvariableName? = this.points + amountvalue or expression?; // assignment894 } // end procedure method abstract void newHandname?(parameter definitionsparameter definitions?); // abstract procedure895 +protected void newHandHelpername?(parameter definitionsparameter definitions?) { // private procedure method896 this.hasTurnvariableName? = falsevalue or expression?; // assignment897 this.softAcevariableName? = falsevalue or expression?; // assignment898 this.cardsvariableName? = new List<Card>()value or expression?; // assignment899 this.handTotalvariableName? = 0value or expression?; // assignment900 this.statusvariableName? = Status.activevalue or expression?; // assignment901 this.drawprocedureName?(arguments?); // procedure call902 this.drawprocedureName?(arguments?); // procedure call903 } // end procedure method abstract StringType? getMessagename?(parameter definitionsparameter definitions?); // abstract function904 +protected StringType? getMessageHelpername?(parameter definitionsparameter definitions?) { // private function method905 var msgname? = ""value or expression?;906 var statusname? = this.statusvalue or expression?;907 +if (this.hasTurncondition?) { 908 msgvariableName? = msg + " - PLAYING"value or expression?; // assignment909 } else if (status == Status.standingcondition?) {910 msgvariableName? = msg + " - STANDING"value or expression?; // assignment911 } else if (status == Status.blackjackcondition?) {912 msgvariableName? = msg + " - BLACKJACK"value or expression?; // assignment913 } else if (status == Status.bustcondition?) {914 msgvariableName? = msg + " - BUST"value or expression?; // assignment915 } // end if return msgvalue or expression?;916 } // end function method abstract void nextActionname?(Card dealerFaceCardparameter definitions?); // abstract procedure917 } // end class

What you need to know about an abstract class

  • An abstract class is like a concrete class except that:
    • It may not be instantiated directly, and hence has no constructor.
    • A concrete class may inherit from it; this is specified in the concrete class, by entering the name of the desired abstract 'superclass' in the the field labelled inheritance. Note that when this has been entered the editor will add the syntax for inheritance appropriate to the language being used.
    • It may define concrete members (as found on concrete classes) and/or abstract members.
    • Any concrete class that inherits from an abstract class, must provide concrete implementations of all abstract members defined on its immediate superclass, and on any abstract classes defined in its hierarchy.
  • An abstract class must be declared in the code above any class that inherits from it.
  • Inheritance hierarchies must form a tree, that is you must avoid creating a circular dependency where, for example, type A inherits from type B, which inherits from type CCCCC, which inherits from type AAAAA.

More advanced techniques

Delegation

If you want sub-classes to be able to define their own implementation of a common method, then that method should be made abstract. If it turns out that several of the sub-classes need the same, or very similar, implementations (and duplication of code is always to be avoided, according to the DRY principle - Don't Repeat Yourself) then you use the pattern of 'delegation'. This means defining the method as abstract, but also providing a private implementation (or part-implementation) with a slightly different name, which sub-classes may call from their own implementations. You can see an example of this in the Blackjack demo:

  • The abstract class named PlayerPlayerPlayerPlayerPlayer defines abstract procedure newHandname?(parameter definitionsparameter definitions?)15@abstractmethod
    def newHandname?(parameter definitionsparameter definitions?) -> None
      pass
    # abstract procedure16
    abstract void newHandname?(parameter definitionsparameter definitions?); // abstract procedure17MustOverride Sub newHandname?(parameter definitionsparameter definitions?)18abstract void newHandname?(parameter definitionsparameter definitions?); // abstract procedure19
    .
  • It also defines a concrete procedure method named newHandHelpernewHandHelpernewHandHelpernewHandHelpernewHandHelper.
  • The subclass HumanPlayerHumanPlayerHumanPlayerHumanPlayerHumanPlayer defines its implementation of newHand as just call this.newHandHelperprocedureName?(arguments?)20self.newHandHelperprocedureName?(arguments?) # procedure call21this.newHandHelperprocedureName?(arguments?); // procedure call22Me.newHandHelperprocedureName?(arguments?) ' procedure call23this.newHandHelperprocedureName?(arguments?); // procedure call24.
  • The subclass DealerDealerDealerDealerDealer defines a more complex implementation of newHand with five instructions, but these include call this.newHandHelperprocedureName?(arguments?)25self.newHandHelperprocedureName?(arguments?) # procedure call26this.newHandHelperprocedureName?(arguments?); // procedure call27Me.newHandHelperprocedureName?(arguments?) ' procedure call28this.newHandHelperprocedureName?(arguments?); // procedure call29 to do the common part of the task.

Statement Instructions

Coding with Elan using the object-oriented paradigm offers the same statement instructions used in the procedural paradigm.

Member instructions

Member instructions (also referred to simply as 'members') may be added only with a concrete class or abstract class. Hence, these method instructions may be created only when the paradigm is set to object-oriented (or functional - which includes all OOP capabilities).

Constructor

Examples of a constructor

From the Snake - object-oriented demo, where the SquareSquareSquareSquareSquare class defines this constructor:

+class SquareName? inheritance?918 +constructor(x as Int, y as Intparameter definitions?) 919 assign this.xvariableName? to xvalue or expression?920 assign this.yvariableName? to yvalue or expression?921 end constructor # other code not showncomment? +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 922 return "{this.x}, {this.y}"value or expression?923 end function end class
+class SquareName? inheritance?: # concrete class924 +def __init__(self: Square, x: int, y: intparameter definitions?) -> None: 925 self.xvariableName? = xvalue or expression? # assignment926 self.yvariableName? = yvalue or expression? # assignment927 # end constructor # other code not showncomment? +def toStringname?(self: Square) -> strType?: # function method928 return "{this.x}, {this.y}"value or expression?929 # end function method # end class
+class SquareName? inheritance? {930 +public Square(int x, int yparameter definitions?) { 931 this.xvariableName? = xvalue or expression?; // assignment932 this.yvariableName? = yvalue or expression?; // assignment933 } // end constructor // other code not showncomment? +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method934 return "{this.x}, {this.y}"value or expression?;935 } // end function method } // end class
+Class SquareName? inheritance?936 +Sub New(x As Integer, y As Integerparameter definitions?) 937 Me.xvariableName? = xvalue or expression? ' assignment938 Me.yvariableName? = yvalue or expression? ' assignment939 End Sub ' other code not showncomment? +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 940 Return "{this.x}, {this.y}"value or expression?941 End Function End Class
+class SquareName? inheritance? {942 +public Square(int x, int yparameter definitions?) { 943 this.xvariableName? = xvalue or expression?; // assignment944 this.yvariableName? = yvalue or expression?; // assignment945 } // end constructor // other code not showncomment? +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method946 return "{this.x}, {this.y}"value or expression?;947 } // end function method } // end class

which requires the programmer to provide values for parameters xxxxx and yyyyy when instantiating a SquareSquareSquareSquareSquare, and assigns these parameters to the corresponding properties of the object.

From the Blackjack demo, where the GameGameGameGameGame class defines this constructor:

+class GameName? inheritance?948 +constructor(dealerStartPoints as Intparameter definitions?) 949 assign this.dealervariableName? to new Dealer(dealerStartPoints)value or expression?950 assign this.playersvariableName? to new List<of Player>()value or expression?951 assign this.messagevariableName? to ""value or expression?952 end constructor #  other code not showncomment? +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 953 return "a Game"value or expression?954 end function end class
+class GameName? inheritance?: # concrete class955 +def __init__(self: Game, dealerStartPoints: intparameter definitions?) -> None: 956 self.dealervariableName? = Dealer(dealerStartPoints)value or expression? # assignment957 self.playersvariableName? = list[Player]()value or expression? # assignment958 self.messagevariableName? = ""value or expression? # assignment959 # end constructor #  other code not showncomment? +def toStringname?(self: Game) -> strType?: # function method960 return "a Game"value or expression?961 # end function method # end class
+class GameName? inheritance? {962 +public Game(int dealerStartPointsparameter definitions?) { 963 this.dealervariableName? = new Dealer(dealerStartPoints)value or expression?; // assignment964 this.playersvariableName? = new List<Player>()value or expression?; // assignment965 this.messagevariableName? = ""value or expression?; // assignment966 } // end constructor //  other code not showncomment? +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method967 return "a Game"value or expression?;968 } // end function method } // end class
+Class GameName? inheritance?969 +Sub New(dealerStartPoints As Integerparameter definitions?) 970 Me.dealervariableName? = New Dealer(dealerStartPoints)value or expression? ' assignment971 Me.playersvariableName? = New List(Of Player)()value or expression? ' assignment972 Me.messagevariableName? = ""value or expression? ' assignment973 End Sub '  other code not showncomment? +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 974 Return "a Game"value or expression?975 End Function End Class
+class GameName? inheritance? {976 +public Game(int dealerStartPointsparameter definitions?) { 977 this.dealervariableName? = new Dealer(dealerStartPoints)value or expression?; // assignment978 this.playersvariableName? = new List<Player>()value or expression?; // assignment979 this.messagevariableName? = ""value or expression?; // assignment980 } // end constructor //  other code not showncomment? +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method981 return "a Game"value or expression?;982 } // end function method } // end class

Note that the single parameter defined in the instructor does not correspond directly to the any property defined on GameGameGameGameGame, but is used to create a new instance of DealerDealerDealerDealerDealer for its own dealerdealerdealerdealerdealer (association) property. Its playerplayerplayerplayerplayer property is a 'multiple association' and is initialised to a new (empty) list of type PlayerPlayerPlayerPlayerPlayer.

What you need to know about a constructor

  • A concrete class must have a constructor - so when a new concrete class is created, it includes a minimal default implementation.
  • The purpose of the constructor is to initialise all properties (except, optionally, for properties of numeric or Boolean types - if you are happy for them to take the default values for those types).
  • A property is initialised using an assignment instruction, prefixing the name of the property as in this example – assign this.xvariableName? to xvalue or expression?30self.xvariableName? = xvalue or expression? # assignment31this.xvariableName? = xvalue or expression?; // assignment32Me.xvariableName? = xvalue or expression? ' assignment33this.xvariableName? = xvalue or expression?; // assignment34 – the prefix means that you can have a parameter with the same name, with no risk of ambiguity.
  • The constructor may make use of the object's function methods and/or global methods to calculate values to be assigned to its properties.
  • However, a constructor may not call any procedure - internal or external - because constructing a new instance should have no side-effects.

Property

Examples of a property

From the Snake - object-oriented demo, where the GameGameGameGameGame class defines these properties:

property dealername? as DealerType?983
dealername?: DealerType? # property984
public DealerType? dealername? {get; private set;} // property985
Property dealername? As DealerType?986
public DealerType? dealername?; // property987
property playersname? as List<of Player>Type?988
playersname?: list[Player]Type? # property989
public List<Player>Type? playersname? {get; private set;} // property990
Property playersname? As List(Of Player)Type?991
public List<Player>Type? playersname?; // property992
property messagename? as StringType?993
messagename?: strType? # property994
public stringType? messagename? {get; private set;} // property995
Property messagename? As StringType?996
public StringType? messagename?; // property997

What you need to know about a property

  • A property is a named value defined on a concrete or abstract class with a name conforming to the standard rules for an identifier.
  • The type of a property, unlike a variable, is explicitly defined, as shown in the examples above. It may be a standard library type (including data structures), or the name of another used-defined class, or a data structure of another class type (see examples above).
  • Every property must be initialised within a constructor unless it is of a numeric or Boolean type - which have default values.
  • A property is public by default, but may be specified as private (visible only to code inside the class) though the make private action on the context menu (or reverted to public with make public.
  • The public properties of an instance may be read by code outside the class using dot-syntax, but may only be modified (from outside) via an appropriate procedure method.

More advanced techniques

Optional ('Maybe') associations

When a class property is optional, it is defined with the type MaybeMaybeMaybeMaybeMaybe.

Before reading the property's value, you must test if it has a value with function hasValue which returns a BooleanboolboolBooleanboolean. If trueTruetrueTruetrue is returned, then you retrieve the value with function getValue; if falseFalsefalseFalsefalse is returned, then it has no current value, and using getValue will give a runtime error.

To update the property's value, use function put (supersedes deprecated put). To restore it to having no value, use function clearValue.

Here a pupil (of class Pupil) that optionally has a tutor (of class Teacher):

+main 1 variable tTomname? set to new Teacher()value or expression?2 variable pPetename? set to new Pupil()value or expression?3 +if pPete.tutor.hasValue()condition? then 4 print(pPete.tutorvalue or expression?)5 end if print(pvalue or expression?)6 end main +class TeacherName? inheritance?7 +constructor(parameter definitionsparameter definitions?) 8 new code end constructor +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 9 return "a Teacher"value or expression?10 end function end class +class PupilName? inheritance?11 +constructor(parameter definitionsparameter definitions?) 12 assign this.tutorvariableName? to new Maybe<of Teacher>()value or expression?13 end constructor property tutorname? as Maybe<of Teacher>Type?14 +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 15 return "a Pupil"value or expression?16 end function end class
+def main() -> None: 1 tTomname? = Teacher()value or expression? # variable definition2 pPetename? = Pupil()value or expression? # variable definition3 +if pPete.tutor.hasValue()condition?: 4 print(pPete.tutorvalue or expression?)5 # end if print(pvalue or expression?)6 # end main +class TeacherName? inheritance?: # concrete class7 +def __init__(self: Teacher) -> None: 8 new code # end constructor +def toStringname?(self: Teacher) -> strType?: # function method9 return "a Teacher"value or expression?10 # end function method # end class +class PupilName? inheritance?: # concrete class11 +def __init__(self: Pupil) -> None: 12 self.tutorvariableName? = Maybe[Teacher]()value or expression? # assignment13 # end constructor tutorname?: Maybe[Teacher]Type? # property14 +def toStringname?(self: Pupil) -> strType?: # function method15 return "a Pupil"value or expression?16 # end function method # end class main()
+static void main() { 1 var tTomname? = new Teacher()value or expression?;2 var pPetename? = new Pupil()value or expression?;3 +if (pPete.tutor.hasValue()condition?) { 4 Console.WriteLine(pPete.tutorvalue or expression?); // print statement5 } // end if Console.WriteLine(pvalue or expression?); // print statement6 } // end main +class TeacherName? inheritance? {7 +public Teacher(parameter definitionsparameter definitions?) { 8 new code } // end constructor +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method9 return "a Teacher"value or expression?;10 } // end function method } // end class +class PupilName? inheritance? {11 +public Pupil(parameter definitionsparameter definitions?) { 12 this.tutorvariableName? = new Maybe<Teacher>()value or expression?; // assignment13 } // end constructor public Maybe<Teacher>Type? tutorname? {get; private set;} // property14 +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method15 return "a Pupil"value or expression?;16 } // end function method } // end class
+Sub main() 1 Dim tTomname? = New Teacher()value or expression? ' variable definition2 Dim pPetename? = New Pupil()value or expression? ' variable definition3 +If pPete.tutor.hasValue()condition? Then 4 Console.WriteLine(pPete.tutorvalue or expression?) ' print statement5 End If Console.WriteLine(pvalue or expression?) ' print statement6 End Sub +Class TeacherName? inheritance?7 +Sub New(parameter definitionsparameter definitions?) 8 new code End Sub +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 9 Return "a Teacher"value or expression?10 End Function End Class +Class PupilName? inheritance?11 +Sub New(parameter definitionsparameter definitions?) 12 Me.tutorvariableName? = New Maybe(Of Teacher)()value or expression? ' assignment13 End Sub Property tutorname? As Maybe(Of Teacher)Type?14 +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 15 Return "a Pupil"value or expression?16 End Function End Class
public class Global {

+static void main() { 1 var tTomname? = new Teacher()value or expression?;2 var pPetename? = new Pupil()value or expression?;3 +if (pPete.tutor.hasValue()condition?) { 4 System.out.println(pPete.tutorvalue or expression?); // print statement5 } // end if System.out.println(pvalue or expression?); // print statement6 } // end main +class TeacherName? inheritance? {7 +public Teacher(parameter definitionsparameter definitions?) { 8 new code } // end constructor +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method9 return "a Teacher"value or expression?;10 } // end function method } // end class +class PupilName? inheritance? {11 +public Pupil(parameter definitionsparameter definitions?) { 12 this.tutorvariableName? = new Maybe<Teacher>()value or expression?; // assignment13 } // end constructor public Maybe<Teacher>Type? tutorname?; // property14 +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method15 return "a Pupil"value or expression?;16 } // end function method } // end class
} // end Global

Function method

Examples of a function method

From the Roman Numerals Turing Machine demo, where the TuringMachineTuringMachineTuringMachineTuringMachineTuringMachine class defines these two function methods:

+function isHaltedname?(parameter definitionsparameter definitions?) returns BooleanType? 1 return this.currentState.equals(this.haltState)value or expression?2 end function +function findMatchingRulename?(parameter definitionsparameter definitions?) returns RuleType? 3 variable matchesname? set to this.rules.filter(lambda r as Rule => (r.currentState.equals(this.currentState)) and (r.currentSymbol.equals(this.tape[this.headPosition])))value or expression?4 +if matches.length() is 0condition? then 5 throw ElanRuntimeErrorexception type? $"No rule matching state {this.currentState} and symbol {this.tape[this.headPosition]}"message?6 end if return matches.head()value or expression?7 end function
+def isHaltedname?(parameter definitionsparameter definitions?) -> boolType?: # function1 return self.currentState.equals(self.haltState)value or expression?2 # end function +def findMatchingRulename?(parameter definitionsparameter definitions?) -> RuleType?: # function3 matchesname? = self.rules.filter(lambda r: Rule: (r.currentState.equals(self.currentState)) and (r.currentSymbol.equals(self.tape[self.headPosition])))value or expression? # variable definition4 +if matches.length() == 0condition?: 5 raise ElanRuntimeErrorexception type?(f"No rule matching state {self.currentState} and symbol {self.tape[self.headPosition]}"message?)6 # end if return matches.head()value or expression?7 # end function
+static boolType? isHaltedname?(parameter definitionsparameter definitions?) { // function1 return this.currentState.equals(this.haltState)value or expression?;2 } // end function +static RuleType? findMatchingRulename?(parameter definitionsparameter definitions?) { // function3 var matchesname? = this.rules.filter(Rule r => (r.currentState.equals(this.currentState)) && (r.currentSymbol.equals(this.tape[this.headPosition])))value or expression?;4 +if (matches.length() == 0condition?) { 5 throw new ElanRuntimeErrorexception type?($"No rule matching state {this.currentState} and symbol {this.tape[this.headPosition]}"message?);6 } // end if return matches.head()value or expression?;7 } // end function
+Function isHaltedname?(parameter definitionsparameter definitions?) As BooleanType? 1 Return Me.currentState.equals(Me.haltState)value or expression?2 End Function +Function findMatchingRulename?(parameter definitionsparameter definitions?) As RuleType? 3 Dim matchesname? = Me.rules.filter(Function (r As Rule) (r.currentState.equals(Me.currentState)) And (r.currentSymbol.equals(Me.tape(Me.headPosition))))value or expression? ' variable definition4 +If matches.length() = 0condition? Then 5 Throw New ElanRuntimeErrorexception type?($"No rule matching state {Me.currentState} and symbol {Me.tape(Me.headPosition)}"message?)6 End If Return matches.head()value or expression?7 End Function
public class Global {

+static booleanType? isHaltedname?(parameter definitionsparameter definitions?) { // function1 return this.currentState.equals(this.haltState)value or expression?;2 } // end function +static RuleType? findMatchingRulename?(parameter definitionsparameter definitions?) { // function3 var matchesname? = this.rules.filter((Rule r) -> (r.currentState.equals(this.currentState)) && (r.currentSymbol.equals(this.tape[this.headPosition])))value or expression?;4 +if (matches.length() == 0condition?) { 5 throw new ElanRuntimeErrorexception type?(String.format("No rule matching state % and symbol %", this.currentState, this.tape[this.headPosition])message?);6 } // end if return matches.head()value or expression?;7 } // end function
} // end Global

What you need to know about a function method

  • A function method provides information based on the parameters specified in combination with the object's properties.
  • A function method broadly follows the same rules as an ordinary (global) function - so it may not create any side effects nor.
  • However, a function method may read, and hence depend upon, the properties of the object on which it is defined.
  • A function method is public by default, but may be specified as private (visible only to code inside the class) though the make private action on the context menu (or reverted to public with make public).
  • A function method may be called (within an expression) on an object in code outside the class using dot-syntax on the variable holding the instance.
  • A function method may be invoked from within another method inside the class using, for example this.methodName()self.methodName()this.methodName()Me.methodName()this.methodName().

Procedure method

Examples of a procedure method

From the Roman Numerals Turing Machine demo, where the TuringMachineTuringMachineTuringMachineTuringMachineTuringMachine defines these procedure methods (among others):

+procedure setTapename?(tape as Stringparameter definitions?) 8 assign this.tapevariableName? to tapevalue or expression?9 end procedure
+def setTapename?(tape: strparameter definitions?) -> None: # procedure10 self.tapevariableName? = tapevalue or expression? # assignment11 # end procedure
+static void setTapename?(string tapeparameter definitions?) { // procedure12 this.tapevariableName? = tapevalue or expression?; // assignment13 } // end procedure
+Sub setTapename?(tape As Stringparameter definitions?) ' procedure14 Me.tapevariableName? = tapevalue or expression? ' assignment15 End Sub
+static void setTapename?(String tapeparameter definitions?) { // procedure16 this.tapevariableName? = tapevalue or expression?; // assignment17 } // end procedure

This is the simplest form of a procedure method, which allows the tapetapetapetapetape property (a string) to be set to a new value by code outside the class.

+procedure singleStepname?(parameter definitionsparameter definitions?) 18 variable rulename? set to this.findMatchingRule()value or expression?19 call this.executeprocedureName?(rulearguments?)20 end procedure
+def singleStepname?(parameter definitionsparameter definitions?) -> None: # procedure21 rulename? = self.findMatchingRule()value or expression? # variable definition22 self.executeprocedureName?(rulearguments?) # procedure call23 # end procedure
+static void singleStepname?(parameter definitionsparameter definitions?) { // procedure24 var rulename? = this.findMatchingRule()value or expression?;25 this.executeprocedureName?(rulearguments?); // procedure call26 } // end procedure
+Sub singleStepname?(parameter definitionsparameter definitions?) ' procedure27 Dim rulename? = Me.findMatchingRule()value or expression? ' variable definition28 Me.executeprocedureName?(rulearguments?) ' procedure call29 End Sub
+static void singleStepname?(parameter definitionsparameter definitions?) { // procedure30 var rulename? = this.findMatchingRule()value or expression?;31 this.executeprocedureName?(rulearguments?); // procedure call32 } // end procedure

This method made use of a function method on the same class - findMatchingRule() - and then calls another procedure on the same instance - execute - passing it the found rulerulerulerulerule.

What you need to know about a procedure method

  • A procedure method changes the state of the object, and/or interacts with the system
  • A procedure method broadly follows the same rules as an ordinary (global) function - so it may not create any side effects nor.
  • However, a procedure method may read, and hence depend upon, the properties of the object on which it is defined.
  • A procedure method is public by default, but may be specified as private (visible only to code inside the class) though the make private action on the context menu (or reverted to public with make public).
  • A procedure method may be called (using a procedure call/b>) on an object in code outside the class using dot-syntax on the variable holding the instance.
  • A procedure method may be called from within another method inside the class using, for example call this.methodNameprocedureName?(arguments?)35self.methodNameprocedureName?(arguments?) # procedure call36this.methodNameprocedureName?(arguments?); // procedure call37Me.methodNameprocedureName?(arguments?) ' procedure call38this.methodNameprocedureName?(arguments?); // procedure call39.

Abstract function

Examples of an abstract function

From the Blackjack demo, where the abstract class PlayerPlayerPlayerPlayerPlayer defines this abstract function:

abstract function getMessagename?(parameter definitionsparameter definitions?) returns StringType?33
@abstractmethod
def getMessagename?(parameter definitionsparameter definitions?) -> strType?:
  pass
# abstract function34
abstract stringType? getMessagename?(parameter definitionsparameter definitions?); // abstract function35
MustOverride Function getMessagename?(parameter definitionsparameter definitions?) As StringType?36
abstract StringType? getMessagename?(parameter definitionsparameter definitions?); // abstract function37

which is implemented (as a concrete function method) by the subclasses, including on DealerDealerDealerDealerDealer:

+function getMessagename?(parameter definitionsparameter definitions?) returns StringType? 38 variable msgname? set to ""value or expression?39 +if this.hasPlayedcondition? then 40 assign msgvariableName? to this.getMessageHelper() + $" - hand total: {this.handTotal}"value or expression?41 end if return msgvalue or expression?42 end function
+def getMessagename?(parameter definitionsparameter definitions?) -> strType?: # function43 msgname? = ""value or expression? # variable definition44 +if self.hasPlayedcondition?: 45 msgvariableName? = self.getMessageHelper() + f" - hand total: {self.handTotal}"value or expression? # assignment46 # end if return msgvalue or expression?47 # end function
+static stringType? getMessagename?(parameter definitionsparameter definitions?) { // function48 var msgname? = ""value or expression?;49 +if (this.hasPlayedcondition?) { 50 msgvariableName? = this.getMessageHelper() + $" - hand total: {this.handTotal}"value or expression?; // assignment51 } // end if return msgvalue or expression?;52 } // end function
+Function getMessagename?(parameter definitionsparameter definitions?) As StringType? 53 Dim msgname? = ""value or expression? ' variable definition54 +If Me.hasPlayedcondition? Then 55 msgvariableName? = Me.getMessageHelper() + $" - hand total: {Me.handTotal}"value or expression? ' assignment56 End If Return msgvalue or expression?57 End Function
+static StringType? getMessagename?(parameter definitionsparameter definitions?) { // function58 var msgname? = ""value or expression?;59 +if (this.hasPlayedcondition?) { 60 msgvariableName? = this.getMessageHelper() + String.format(" - hand total: %", this.handTotal)value or expression?; // assignment61 } // end if return msgvalue or expression?;62 } // end function

which makes use of a method getMessageHelper() that is defined as (private) concrete function method on the superclass (PlayerPlayerPlayerPlayerPlayer).

What you need to know about an abstract function

  • An abstract function method may be defined only on an abstract class .
  • Any concrete subclass must then implement a concrete (regular) function to match.

Abstract procedure

Examples of an abstract procedure

From the Blackjack demo, where the abstract class PlayerPlayerPlayerPlayerPlayer defines this abstract function:

abstract procedure nextActionname?(dealerFaceCard as Cardparameter definitions?)63
@abstractmethod
def nextActionname?(dealerFaceCard: Cardparameter definitions?) -> None
  pass
# abstract procedure64
abstract void nextActionname?(Card dealerFaceCardparameter definitions?); // abstract procedure65
MustOverride Sub nextActionname?(dealerFaceCard As Cardparameter definitions?)66
abstract void nextActionname?(Card dealerFaceCardparameter definitions?); // abstract procedure67

which is implemented (as a concrete function method) by the subclass DealerDealerDealerDealerDealer using simple logic to implement the standard rules that a Blackjack dealer must always follow:

+procedure nextActionname?(faceCard as Cardparameter definitions?) 68 +if this.handTotal < 17condition? then 69 call this.drawprocedureName?(arguments?)70 else71 call this.standprocedureName?(arguments?)72 end if end procedure
+def nextActionname?(faceCard: Cardparameter definitions?) -> None: # procedure73 +if self.handTotal < 17condition?: 74 self.drawprocedureName?(arguments?) # procedure call75 else:76 self.standprocedureName?(arguments?) # procedure call77 # end if # end procedure
+static void nextActionname?(Card faceCardparameter definitions?) { // procedure78 +if (this.handTotal < 17condition?) { 79 this.drawprocedureName?(arguments?); // procedure call80 } else {81 this.standprocedureName?(arguments?); // procedure call82 } // end if } // end procedure
+Sub nextActionname?(faceCard As Cardparameter definitions?) ' procedure83 +If Me.handTotal < 17condition? Then 84 Me.drawprocedureName?(arguments?) ' procedure call85 Else86 Me.standprocedureName?(arguments?) ' procedure call87 End If End Sub
+static void nextActionname?(Card faceCardparameter definitions?) { // procedure88 +if (this.handTotal < 17condition?) { 89 this.drawprocedureName?(arguments?); // procedure call90 } else {91 this.standprocedureName?(arguments?); // procedure call92 } // end if } // end procedure

and on the subclass HumanPlayerHumanPlayerHumanPlayerHumanPlayerHumanPlayer by code that uses input/output methods to obtain the next action from the user:

+procedure nextActionname?(dealerFaceCard as Cardparameter definitions?) 93 variable keyname? set to ""value or expression?94 call clearKeyBufferprocedureName?(arguments?)95 +while key.equals("")condition? 96 assign keyvariableName? to waitForKey()value or expression?97 +if key.equals("d")condition? then 98 call this.drawprocedureName?(arguments?)99 elif key.equals("s")condition? then100 call this.standprocedureName?(arguments?)101 else102 assign keyvariableName? to ""value or expression?103 end if end while end procedure
+def nextActionname?(dealerFaceCard: Cardparameter definitions?) -> None: # procedure104 keyname? = ""value or expression? # variable definition105 clearKeyBufferprocedureName?(arguments?) # procedure call106 +while key.equals("")condition?: 107 keyvariableName? = waitForKey()value or expression? # assignment108 +if key.equals("d")condition?: 109 self.drawprocedureName?(arguments?) # procedure call110 elif key.equals("s")condition?: # else if111 self.standprocedureName?(arguments?) # procedure call112 else:113 keyvariableName? = ""value or expression? # assignment114 # end if # end while # end procedure
+static void nextActionname?(Card dealerFaceCardparameter definitions?) { // procedure115 var keyname? = ""value or expression?;116 clearKeyBufferprocedureName?(arguments?); // procedure call117 +while (key.equals("")condition?) { 118 keyvariableName? = waitForKey()value or expression?; // assignment119 +if (key.equals("d")condition?) { 120 this.drawprocedureName?(arguments?); // procedure call121 } else if (key.equals("s")condition?) {122 this.standprocedureName?(arguments?); // procedure call123 } else {124 keyvariableName? = ""value or expression?; // assignment125 } // end if } // end while } // end procedure
+Sub nextActionname?(dealerFaceCard As Cardparameter definitions?) ' procedure126 Dim keyname? = ""value or expression? ' variable definition127 clearKeyBufferprocedureName?(arguments?) ' procedure call128 +While key.equals("")condition? 129 keyvariableName? = waitForKey()value or expression? ' assignment130 +If key.equals("d")condition? Then 131 Me.drawprocedureName?(arguments?) ' procedure call132 ElseIf key.equals("s")condition? Then133 Me.standprocedureName?(arguments?) ' procedure call134 Else135 keyvariableName? = ""value or expression? ' assignment136 End If End While End Sub
+static void nextActionname?(Card dealerFaceCardparameter definitions?) { // procedure137 var keyname? = ""value or expression?;138 clearKeyBufferprocedureName?(arguments?); // procedure call139 +while (key.equals("")condition?) { 140 keyvariableName? = waitForKey()value or expression?; // assignment141 +if (key.equals("d")condition?) { 142 this.drawprocedureName?(arguments?); // procedure call143 } else if (key.equals("s")condition?) {144 this.standprocedureName?(arguments?); // procedure call145 } else {146 keyvariableName? = ""value or expression?; // assignment147 } // end if } // end while } // end procedure

What you need to know about an abstract procedure

  • An abstract abstract procedure method may be defined only on an abstract class .
  • Any concrete subclass must then implement a concrete (regular) function to match.

Comment

See Comments

Elan Language Reference go to the top