10 steps to Object-Oriented Programming with Elan
Getting started
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 profile from the menu at the top of the IDE.
This will still allow you to do procedural programming, but changes two things:
- The list of demo programs will now show only programs that use OOP - except that Snake is offered in both procedural
and object-oriented forms for direct comparison.
We will be referring to code within the OOP demo programs within this text.
- It will change some of the options available on the new code menus in the editor:
- At the global level, it will offer: concrete class, abstract class, and interface
- Within a concrete class, abstract class, or interface, the new code menu will show an entirely new list of instructions,
collectively known as ‘members’ (plus comment) that may be defined within those three new global instructions.
In the following sections we will describe 10 principles that define the essence of OOP, illustrated with example code - where possible from the demo programs.
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.
Principle 1: 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
profile 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
+def toStringname?(self: Snake) -> strType?:
# function method3
return "undefined"value or expression?4
+class AppleName? inheritance? # concrete class5
+def __init__(self: Apple) -> None:
6
new code
+def toStringname?(self: Apple) -> strType?:
# function method7
return "undefined"value or expression?8
+class SquareName? inheritance? # concrete class9
+def __init__(self: Square) -> None:
10
new code
+def toStringname?(self: Square) -> strType?:
# function method11
return "undefined"value or expression?12
+class SnakeName? inheritance? {1
+public Snake(parameter definitions?) {
2
new code
} // constructor
+public stringType? toStringname?(parameter definitions?) {
// function method3
return "undefined"value or expression?;4
} // function method
} // class
+class AppleName? inheritance? {5
+public Apple(parameter definitions?) {
6
new code
} // constructor
+public stringType? toStringname?(parameter definitions?) {
// function method7
return "undefined"value or expression?;8
} // function method
} // class
+class SquareName? inheritance? {9
+public Square(parameter definitions?) {
10
new code
} // constructor
+public stringType? toStringname?(parameter definitions?) {
// function method11
return "undefined"value or expression?;12
} // function method
} // class
+Class SnakeName? inheritance? inheritance?1
+Sub New(parameter definitions?)
2
new code
End Sub
+Function toStringname?(parameter definitions?) As StringType?
3
Return "undefined"value or expression?4
End Function
End Class
+Class AppleName? inheritance? inheritance?5
+Sub New(parameter definitions?)
6
new code
End Sub
+Function toStringname?(parameter definitions?) As StringType?
7
Return "undefined"value or expression?8
End Function
End Class
+Class SquareName? inheritance? inheritance?9
+Sub New(parameter definitions?)
10
new code
End Sub
+Function toStringname?(parameter definitions?) As StringType?
11
Return "undefined"value or expression?12
End Function
End Class
+class SnakeName? inheritance? inheritance? {1
+public Snake(parameter definitions?) {
2
new code
} // constructor
+public StringType? toStringname?(parameter definitions?) {
// function method3
return "undefined"value or expression?;4
} // function method
} // class
+class AppleName? inheritance? inheritance? {5
+public Apple(parameter definitions?) {
6
new code
} // constructor
+public StringType? toStringname?(parameter definitions?) {
// function method7
return "undefined"value or expression?;8
} // function method
} // class
+class SquareName? inheritance? inheritance? {9
+public Square(parameter definitions?) {
10
new code
} // constructor
+public StringType? toStringname?(parameter definitions?) {
// function method11
return "undefined"value or expression?;12
} // function method
} // class
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:
+constructor(parameter definitions?)
0
new code
end constructor
property x as Int
Code does not parse as Elan.
property x as Int
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property x as Int
Code does not parse as Elan.
+constructor(parameter definitions?)
0
new code
end constructor
property y as Int
Code does not parse as Elan.
property y as Int
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property y as Int
Code does not parse as Elan.
or properties may have different types, for example the TuringMachineTuringMachineTuringMachineTuringMachineTuringMachine class in the Roman Numerals Turing Machine demo has these:[
- the class may have multiple properties of the same type but with different names, or define
+constructor(parameter definitions?)
0
new code
end constructor
property initialState as String
Code does not parse as Elan.
property initialState as String
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property initialState as String
Code does not parse as Elan.
+constructor(parameter definitions?)
0
new code
end constructor
property currentState as String
Code does not parse as Elan.
property currentState as String
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property currentState as String
Code does not parse as Elan.
+constructor(parameter definitions?)
0
new code
end constructor
property headPosition as Int
Code does not parse as Elan.
property headPosition as Int
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property headPosition as Int
Code does not parse as Elan.
+constructor(parameter definitions?)
0
new code
end constructor
property haltState as String
Code does not parse as Elan.
property haltState as String
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property haltState as String
Code does not parse as Elan.
+constructor(parameter definitions?)
0
new code
end constructor
property rules as List
Code does not parse as Elan.
property rules as List
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property rules as List
Code does not parse as Elan.
+constructor(parameter definitions?)
0
new code
end constructor
property tape as String
Code does not parse as Elan.
property tape as String
Code does not parse as Elan.
+Sub New(parameter definitions?)
0
new code
End Sub
property tape as String
Code does not parse as Elan.
Defining a class
Selecting concrete class from the new code menu inserts a template like this:
+class NameName? inheritance?1
+constructor(parameter definitionsparameter definitions?)
2
new code
end constructor
+function toStringname?(parameter definitionsparameter definitions?) returns StringType?
3
return "undefined"value or expression?4
end function
end class
+class NameName? inheritance? # concrete class1
+def __init__(self: Name) -> None:
2
new code
+def toStringname?(self: Name) -> strType?:
# function method3
return "undefined"value or expression?4
+class NameName? inheritance? {1
+public Name(parameter definitionsparameter definitions?) {
2
new code
} // constructor
+public stringType? toStringname?(parameter definitionsparameter definitions?) {
// function method3
return "undefined"value or expression?;4
} // function method
} // class
+Class NameName? inheritance? inheritance?1
+Sub New(parameter definitionsparameter definitions?)
2
new code
End Sub
+Function toStringname?(parameter definitionsparameter definitions?) As StringType?
3
Return "undefined"value or expression?4
End Function
End Class
+class NameName? inheritance? inheritance? {1
+public Name(parameter definitionsparameter definitions?) {
2
new code
} // constructor
+public StringType? toStringname?(parameter definitionsparameter definitions?) {
// function method3
return "undefined"value or expression?;4
} // function method
} // class
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.)
Principle 2: 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?0
applename? = Apple()value or expression? # variable definition0
var applename? = new Apple()value or expression?;0
Dim applename? = New Apple()value or expression? ' variable definition0
var applename? = new Apple()value or expression?;0
variable snakename? set to new Snake()value or expression?0
snakename? = Snake()value or expression? # variable definition0
var snakename? = new Snake()value or expression?;0
Dim snakename? = New Snake()value or expression? ' variable definition0
var snakename? = new Snake()value or expression?;0
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:
return new Square(newX, newY)
Code does not parse as Elan.
return new Square(newX, newY)
Code does not parse as Elan.
return new Square(newX, newY)
Code does not parse as Elan.
return new Square(newX, newY)
Code does not parse as Elan.
return new Square(newX, newY)
Code does not parse as Elan.
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
0
new code
end if
+if distViaCurrent < neighbour.distFromStartcondition?:
0
new code
+if (distViaCurrent < neighbour.distFromStartcondition?) {
0
new code
} // if
+If distViaCurrent < neighbour.distFromStartcondition? Then
0
new code
End If
+if (distViaCurrent < neighbour.distFromStartcondition?) {
0
new code
} // if
is comparing the value of the variable distViaCurrentdistViaCurrentdistViaCurrentdistViaCurrentdistViaCurrent to the property named distFromStartdistFromStartdistFromStartdistFromStartdistFromStart of the
object named neighbourneighbourneighbourneighbourneighbour.
Principle 3: 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 BooleanboolboolBooleanbool -
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:
constructor(x as Int, y as Int)
reassign this.x to x
reassign this.y to y
end constructor
Code does not parse as Elan.
constructor(x as Int, y as Int)
reassign this.x to x
reassign this.y to y
end constructor
Code does not parse as Elan.
constructor(x as Int, y as Int)
reassign this.x to x
reassign this.y to y
end constructor
Code does not parse as Elan.
constructor(x as Int, y as Int)
reassign this.x to x
reassign this.y to y
end constructor
Code does not parse as Elan.
constructor(x as Int, y as Int)
reassign this.x to x
reassign this.y to y
end constructor
Code does not parse as Elan.
Note:
- Even properties that are automatically initialised may be explicitly initialised in the constructor.
- It is quite common to initialise strings to an empty string -
"""""""""" and a list to any empty list of the appropriate member-type,
for example new List
Code does not parse as Elan.new List
Code does not parse as Elan.new List
Code does not parse as Elan.new List
Code does not parse as Elan.new List
Code does not parse as Elan. - though you may choose any value.
- If you don't you want every instance of a class to be initialised with the same value, the specific value for
an instance is specified via paramaters added into the constructor definition.
- When a new instance is created, arguments for each parameter must be specified (as they would be for a procedure or function),
so creating a new
SquareSquareSquareSquareSquare for example takes the form variable tailname? set to new Square(20, 15)value or expression?0tailname? = Square(20, 15)value or expression? # variable definition0var tailname? = new Square(20, 15)value or expression?;0Dim tailname? = New Square(20, 15)value or expression? ' variable definition0var tailname? = new Square(20, 15)value or expression?;0
- A property is initialised within the construction using a reassign variable instruction - since a property may be thought of as
a named variable that holds a value specific to each instance of the class.
- the name of the property being reassigned must be prefixed by
thisselfMethisMe
followed by a dot.
Principle 4: 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?1
+constructor(parameter definitionsparameter definitions?)
2
variable tailname? set to new Square(20, 15)value or expression?3
reassign this.currentDirvariableName? to Direction.rightvalue or expression?4
reassign this.bodyvariableName? to [tail]value or expression?5
reassign this.headvariableName? to tail.getAdjacentSquare(this.currentDir)value or expression?6
reassign this.priorTailvariableName? to tailvalue or expression?7
end constructor
private property currentDirname? as DirectionType?8
private property headname? as SquareType?9
private property bodyname? as List<of Square>Type?10
private property priorTailname? as SquareType?11
+function toStringname?(parameter definitions?) returns StringType?
12
return "undefined"value or expression?13
end function
end class
+class SnakeName? inheritance? # concrete class1
+def __init__(self: Snake) -> None:
2
tailname? = Square(20, 15)value or expression? # variable definition3
self.currentDirvariableName? = Direction.rightvalue or expression? # reassign variable4
self.bodyvariableName? = [tail]value or expression? # reassign variable5
self.headvariableName? = tail.getAdjacentSquare(self.currentDir)value or expression? # reassign variable6
self.priorTailvariableName? = tailvalue or expression? # reassign variable7
currentDirname?: DirectionType? # private property8
headname?: SquareType? # private property9
bodyname?: list[Square]Type? # private property10
priorTailname?: SquareType? # private property11
+def toStringname?(self: Snake) -> strType?:
# function method12
return "undefined"value or expression?13
+class SnakeName? inheritance? {1
+public Snake(parameter definitionsparameter definitions?) {
2
var tailname? = new Square(20, 15)value or expression?;3
this.currentDirvariableName? = Direction.rightvalue or expression?; // reassign variable4
this.bodyvariableName? = [tail]value or expression?; // reassign variable5
this.headvariableName? = tail.getAdjacentSquare(this.currentDir)value or expression?; // reassign variable6
this.priorTailvariableName? = tailvalue or expression?; // reassign variable7
} // constructor
private DirectionType? currentDirname? {get; private set;} // private property8
private SquareType? headname? {get; private set;} // private property9
private List<Square>Type? bodyname? {get; private set;} // private property10
private SquareType? priorTailname? {get; private set;} // private property11
+public stringType? toStringname?(parameter definitions?) {
// function method12
return "undefined"value or expression?;13
} // function method
} // class
+Class SnakeName? inheritance? inheritance?1
+Sub New(parameter definitionsparameter definitions?)
2
Dim tailname? = New Square(20, 15)value or expression? ' variable definition3
Me.currentDirvariableName? = Direction.rightvalue or expression? ' reassign variable4
Me.bodyvariableName? = {tail}value or expression? ' reassign variable5
Me.headvariableName? = tail.getAdjacentSquare(Me.currentDir)value or expression? ' reassign variable6
Me.priorTailvariableName? = tailvalue or expression? ' reassign variable7
End Sub
Private Property currentDirname? As DirectionType?8
Private Property headname? As SquareType?9
Private Property bodyname? As List(Of Square)Type?10
Private Property priorTailname? As SquareType?11
+Function toStringname?(parameter definitions?) As StringType?
12
Return "undefined"value or expression?13
End Function
End Class
+class SnakeName? inheritance? inheritance? {1
+public Snake(parameter definitionsparameter definitions?) {
2
var tailname? = new Square(20, 15)value or expression?;3
this.currentDirvariableName? = Direction.rightvalue or expression?; // reassign variable4
this.bodyvariableName? = [tail]value or expression?; // reassign variable5
this.headvariableName? = tail.getAdjacentSquare(this.currentDir)value or expression?; // reassign variable6
this.priorTailvariableName? = tailvalue or expression?; // reassign variable7
} // constructor
private DirectionType? currentDirname?; // private property8
private SquareType? headname?; // private property9
private List<Square>Type? bodyname?; // private property10
private SquareType? priorTailname?; // private property11
+public StringType? toStringname?(parameter definitions?) {
// function method12
return "undefined"value or expression?;13
} // function method
} // class
Note:
- The
SnakeSnakeSnakeSnakeSnake is always initialised to the same values, so the constructor does not need to define any parameters.
- The
currentDircurrentDircurrentDircurrentDircurrentDir property is of type DirectionDirectionDirectionDirectionDirection (which is an enum) and is initialised to Direction.rightDirection.rightDirection.rightDirection.rightDirection.right.
- The
bodybodybodybodybody property is a multiple association - defined as a list of type Square
- The constructor defines a variable
tail
Code does not parse as Elan.Square
- The constructor defines a variable
tail
Code does not parse as Elan.Square
- The constructor defines a variable
tail
Code does not parse as Elan.Square
- The constructor defines a variable
tail
Code does not parse as Elan.Square
- The constructor defines a variable
tail
Code does not parse as Elan. used only for the purpose of initialising properties.
It is first used to initialise the bodybodybodybodybody as a list containing only the tailtailtailtailtail and
is finally used to define the priorTailpriorTailpriorTailpriorTailpriorTail which is a single association of type SquareSquareSquareSquareSquare.
In between it is used in an expression to determine the initial value for the headheadheadheadhead property using a function (more on this in the next heading).
- It is thus legitimate to call functions within the constructor. A constructor may not call any procedure, however.
And you should avoid doing any work in a constructor other than determining appropriate values for initialising its properties.
Principle 5: 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) was calling a function method, using 'dot syntax' on the
the variable tailtailtailtailtail which is an instance of class SquareSquareSquareSquareSquare. A class can define both function methods and procedure
methods, for the following reasons:
- these methods are invoked on an instance using 'dot-syntax' as we shown in the previous example
- they have access to all the properties belonging to that same instance, and they can also invoke other
methods on the same object, prefixed by thisselfMethisMe dot.
This may be seen in the implementation of getAdjacentSquare in the SquareSquareSquareSquareSquare class:
+function getAdjacentSquarename?(d as Directionparameter definitions?) returns SquareType?
1
variable newXname? set to this.xvalue or expression?2
variable newYname? set to this.yvalue or expression?3
+if d is Direction.leftcondition? then
4
reassign newXvariableName? to this.x - 1value or expression?5
elif d is Direction.rightcondition? then6
reassign newXvariableName? to this.x + 1value or expression?7
elif d is Direction.upcondition? then8
reassign newYvariableName? to this.y - 1value or expression?9
elif d is Direction.downcondition? then10
reassign newYvariableName? to this.y + 1value or expression?11
end if
return new Square(newX, newY)value or expression?12
end function
+def getAdjacentSquarename?(d: Directionparameter definitions?) -> SquareType?:
# function1
newXname? = self.xvalue or expression? # variable definition2
newYname? = self.yvalue or expression? # variable definition3
+if d == Direction.leftcondition?:
4
newXvariableName? = self.x - 1value or expression? # reassign variable5
elif d == Direction.rightcondition?: # else if6
newXvariableName? = self.x + 1value or expression? # reassign variable7
elif d == Direction.upcondition?: # else if8
newYvariableName? = self.y - 1value or expression? # reassign variable9
elif d == Direction.downcondition?: # else if10
newYvariableName? = self.y + 1value or expression? # reassign variable11
return Square(newX, newY)value or expression?12
+static SquareType? getAdjacentSquarename?(Direction dparameter definitions?) {
// function1
var newXname? = this.xvalue or expression?;2
var newYname? = this.yvalue or expression?;3
+if (d == Direction.leftcondition?) {
4
newXvariableName? = this.x - 1value or expression?; // reassign variable5
} else if (d == Direction.rightcondition?) {6
newXvariableName? = this.x + 1value or expression?; // reassign variable7
} else if (d == Direction.upcondition?) {8
newYvariableName? = this.y - 1value or expression?; // reassign variable9
} else if (d == Direction.downcondition?) {10
newYvariableName? = this.y + 1value or expression?; // reassign variable11
} // if
return new Square(newX, newY)value or expression?;12
} // function
+Function getAdjacentSquarename?(d As Directionparameter definitions?) As SquareType?
1
Dim newXname? = Me.xvalue or expression? ' variable definition2
Dim newYname? = Me.yvalue or expression? ' variable definition3
+If d = Direction.leftcondition? Then
4
newXvariableName? = Me.x - 1value or expression? ' reassign variable5
ElseIf d = Direction.rightcondition? Then6
newXvariableName? = Me.x + 1value or expression? ' reassign variable7
ElseIf d = Direction.upcondition? Then8
newYvariableName? = Me.y - 1value or expression? ' reassign variable9
ElseIf d = Direction.downcondition? Then10
newYvariableName? = Me.y + 1value or expression? ' reassign variable11
End If
Return New Square(newX, newY)value or expression?12
End Function
+static SquareType? getAdjacentSquarename?(Direction dparameter definitions?) {
// function1
var newXname? = this.xvalue or expression?;2
var newYname? = this.yvalue or expression?;3
+if (d == Direction.leftcondition?) {
4
newXvariableName? = this.x - 1value or expression?; // reassign variable5
} else if (d == Direction.rightcondition?) {6
newXvariableName? = this.x + 1value or expression?; // reassign variable7
} else if (d == Direction.upcondition?) {8
newYvariableName? = this.y - 1value or expression?; // reassign variable9
} else if (d == Direction.downcondition?) {10
newYvariableName? = this.y + 1value or expression?; // reassign variable11
} // if
return new Square(newX, newY)value or expression?;12
} // function
Function and procedure methods defined on a class have similaries and differences to global functions and procedures.
TO STRING
Function method
Where properties define state, methods define behaviour
Function & procedure methods – distinction (like globals) & commonality – optional params EXAMPLES
Invoked on an instance using dot syntax EXAMPLES
Implementation of methods
Why put behaviour onto methods?
The toString method
Procedure method
Principle 6: Object represent identifiable concepts in the application domain
This is encapsulation meaning 1
It is never a complete representation, but it is complete for the purposes of the application
Most of the objects
It not only represents its state, but its behaviour. By making objects the fundamental building blocks of programs they are easier to change and extend
Principle 7: Objects should hide state behind methods, where possible
Does not mean getter and setter methods
This is encapsulation meaning 2
Private properties, public methods – why and how
Can make methods private too – where they are called by other methods in the same class.
Principle 8: Allow objects of different classes to be handled through a common interface
May be handled through interfaces or inheritance
Where appropriate it is possible to deal with instances or two or more separate classes without having to know which type each one is
A class can implement multiple interfaces, as long as they don’t clash
An interface cannot be instantiated – but a concrete class that implements that interface can be
An interface may extend another interface
Each class that implements an interface must provide an implemented of each method, but the implementation may differ (EXAMPLES).
It is likely that more than one class might have exactly the same implementation, and by the DRY principle it is undesirable to repeat the code. So you delegate, in part or in whole
Principle 9: Common implementations may be shared through delegation or inheritance
An abstract class …
An abstract class is like an interface, but more powerful that it may also define concrete implementations of methods, with some limitations
The limitation is that a concrete class may (optionally) inherit only from one abstract class – though it may implement additional interfaces
An abstract class may inherit from another abstract class and/or implement interfaces
Elan’s model of inheritance – what you can and can’t do
-
Principle 10: Application logic should be well-distributed across classes
As much as possible of the code should be defined in classes
With a minimal main and/or standalone (global) constructs
The logic should be distributed as evenly as possible across the classes
Be wary of singletons
In particular of a singleton that is doing most of the work. This pattern is often used to avoid global variables, but be wary of this