Methods in EK9
A method belongs to an aggregate type: classes, traits, components and services have touched on them. Their visibility is covered in class method visibility, trait method visibility and component method visibility.
What are Methods?
A method is like a function bound to a data structure, with direct access to that structure's properties/fields.
- Classes: you control which methods are accessible, which makes classes good for information hiding.
- Traits: methods are always public; a trait is a 'public interface'.
- Components: methods are public or private.
Some languages make you declare a method 'virtual' before it can be overridden. In EK9 any method except a private one can be overridden (a class must still be open or abstract to have a sub-class at all).
Quality Enforcement: the compiler applies quality limits to every method, whatever its visibility (public, protected, private): cyclomatic complexity at most 45, cognitive complexity 35, nesting depth 6, and 100 statements per method (50 for an operator). Variable and parameter names must be descriptive (no temp, flag, data, value, buffer, object). All of this is checked at compile time; see Code Quality.
Mutator Methods
A mutator method alters the state of its object, directly or by triggering some processing. It generally checks the parameters passed in, then alters the internal state, and it should leave that state always valid. It may make deep copies of its parameters, and it may throw exceptions. Above all it may have side effects: alterations to overall program state, network traffic or other processing.
Accessor Methods
A record's properties/fields are public; an object's are not, so accessor methods give other constructs access to specific data held in an object. Take care: sometimes you should return a copy of the state, so that it cannot be altered directly.
Processing Methods
Like a mutator, a processing method triggers processing, but it may or may not alter the object's state. 'Drawing' is an example: the object itself may be unchanged, but its state is used to alter another object (say a 'Canvas').
Utility Methods
Some methods could have been functions, but are better linked to a type of object: for example the startOfDay() method on Time. Any function closely tied to one type is a candidate. In most cases, though, a standalone function is more flexible and reduces coupling.
Defining/Using Methods
A method's name can be overloaded (used again) as long as the number or type of parameters differs. A function cannot: it must have a unique name within its module. See parameter passing for how parameters are declared on methods and passed to them.
Two methods from the class anatomy example: deposit changes the object, and describe returns a value. A method takes its parameters with -> and declares its result with <-. There is no return statement: the result is whatever the <- variable holds when the body ends (see functions). Marking describe as pure says it changes nothing.
//A method that changes the object. deposit() -> amount as Integer balance += amount //A method that returns a value: there is no 'return', the result is the '<-' variable. describe() as pure <- rtn as String: `${owner} holds ${balance}`
Full source: classes_anatomy.ek9 - Download classes_anatomy.ek9
Inside an object, this and super call methods explicitly. That matters when you override a method and still want the super's behaviour. A method that replaces one of its super class must say override; here it calls the original with super.describe():
SavingsAccount extends Account rate as Integer? SavingsAccount() -> owner as String balance as Integer rate as Integer super(owner, balance) this.rate: rate //'override' is required to replace a method of the super class; 'super.' calls the original. override describe() as pure <- rtn as String: `${super.describe()} at ${rate}%` //A sub class with its own properties gives its own '?', '$' and sameness (with '#?'): //Account's cannot know 'rate', so each of these calls Account's first, then adds it. default operator ? default operator $ default operator <=>
Full source: classes_anatomy.ek9 - Download classes_anatomy.ek9
To call a method of a particular trait an object implements, there is specific syntax; see traits.
Sanitized Method Parameters
A method that accepts user input can mark a String parameter sanitized, and the data is cleaned as it enters the method. This matters most for methods that pass external input to database operations, file handling or rendering.
UserService // ... properties updateProfile() -> userId as Integer biography as sanitized String <- success as Boolean: false //The biography is sanitized before this method receives it // ... store the biography default operator ?
Full source: methods_sanitized.ek9 - Download methods_sanitized.ek9
Important: sanitized is part of a method's contract, like pure. An override
may add sanitized to a parameter, but may never drop it from a parameter the super method marks
sanitized, so a caller holding the super type always gets its input cleaned
(Liskov Substitution Principle). See E07940.
For everything about the sanitized modifier, including the InputSanitizer class,
see Input Sanitization.
Summary
The OptionsHandler example has a number of public and private methods. Methods and functions are the main way to trigger processing; operators provide specific, well-defined processing.
Next Steps
Traits, closely related to classes, come next.