Principles of Programming
AS — Unit 1: Fundamentals of Computer Science
Programming Paradigms
A programming paradigm is a fundamental style or approach to programming that defines how we structure and organise code to solve problems.
There are several major paradigms, and many modern languages support more than one (these are called multi-paradigm languages). The WJEC AS specification requires knowledge of four paradigms: procedural, object-oriented, declarative, and functional.
Procedural Programming
Procedural programming organises code as a sequence of instructions (statements) that are executed in order. The program is broken down into procedures (also called subroutines, functions, or methods) that perform specific tasks.
Key features:
- Programs follow a top-down design – the problem is broken into smaller sub-problems, each solved by a procedure.
- Data and procedures are separate – data is passed to procedures via parameters or accessed through global variables.
- Uses sequence, selection (if/else), and iteration (loops) as control structures.
- Variables have defined scope (local or global).
# Procedural example: calculating average of a list
def calculate_total(numbers):
total = 0
for num in numbers:
total += num
return total
def calculate_average(numbers):
total = calculate_total(numbers)
return total / len(numbers)
marks = [72, 85, 64, 91, 78]
avg = calculate_average(marks)
print(f"Average: {avg}")
' Procedural example: calculating average of a list
Function CalculateTotal(numbers() As Double) As Double
Dim total As Double = 0
For Each num As Double In numbers
total += num
Next
Return total
End Function
Function CalculateAverage(numbers() As Double) As Double
Dim total As Double = CalculateTotal(numbers)
Return total / numbers.Length
End Function
Dim marks() As Double = {72, 85, 64, 91, 78}
Dim avg As Double = CalculateAverage(marks)
Console.WriteLine("Average: " & avg)
Examples of procedural languages: C, Pascal, BASIC, COBOL.
Object-Oriented Programming (OOP)
Object-oriented programming organises code around objects – self-contained entities that combine data (attributes) and behaviour (methods) into a single unit. Objects are created from classes, which serve as blueprints.
Key features:
- Encapsulation – data and methods are bundled together; internal details are hidden.
- Inheritance – a class can inherit attributes and methods from a parent class.
- Polymorphism – objects of different classes can respond to the same method call in different ways.
- Abstraction – complex details are hidden behind a simple interface.
Examples of OOP languages: Java, Python, C#, C++, VB.NET.
Declarative Programming
Declarative programming focuses on what the program should accomplish rather than how to accomplish it. The programmer describes the desired result, and the language implementation determines how to produce it.
-- SQL example (declarative)
SELECT name, grade FROM students WHERE grade > 70 ORDER BY grade DESC;
The programmer does not specify how to loop through records, compare values, or sort results. The database engine handles all of that.
Other examples: Prolog (logic programming), HTML/CSS (markup/styling).
Functional Programming
Functional programming treats computation as the evaluation of mathematical functions. It avoids changing state and mutable data.
Key features:
- First-class functions – functions can be assigned to variables, passed as arguments, and returned from other functions.
- Pure functions – a function always produces the same output for the same input and has no side effects.
- Immutability – data is not modified after creation; new data structures are created instead.
- Higher-order functions – functions that take other functions as parameters or return functions.
# Functional style in Python
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Using map and filter (higher-order functions)
evens = list(filter(lambda x: x % 2 == 0, numbers))
doubled = list(map(lambda x: x * 2, evens))
print(doubled) # Output: [4, 8, 12, 16, 20]
' Functional style in VB.NET using LINQ
Dim numbers() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Dim doubled = numbers.Where(Function(x) x Mod 2 = 0).Select(Function(x) x * 2).ToList()
For Each num In doubled
Console.Write(num & " ") ' Output: 4 8 12 16 20
Next
Examples of functional languages: Haskell, Erlang, Lisp. Many modern languages (Python, JavaScript, C#) support functional features.
Paradigm Comparison Table
| Feature | Procedural | Object-Oriented | Declarative | Functional |
|---|---|---|---|---|
| Core idea | Sequence of instructions | Interacting objects | Describe the result | Evaluate functions |
| Data & behaviour | Separate | Bundled (encapsulation) | Implicit | Functions operate on immutable data |
| State | Mutable variables | Mutable object state | Managed by system | Avoided (immutability) |
| Reuse mechanism | Procedures/functions | Inheritance, composition | Rules/queries | Higher-order functions |
| Examples | C, Pascal | Java, Python, C# | SQL, Prolog | Haskell, Erlang |
For the exam, you need to be able to describe each paradigm, identify which paradigm a given piece of code belongs to, and explain the differences between them. Remember that many languages are multi-paradigm – Python supports procedural, OOP, and functional styles.
Object-Oriented Programming Concepts in Detail
Classes and Objects
A class is a blueprint or template that defines the attributes (data) and methods (behaviour) that objects of that type will have. An object is a specific instance of a class, with its own set of attribute values.
Think of a class as a cookie cutter and objects as the individual cookies made from it. The class defines the shape; each object is a distinct cookie.
class Dog:
def __init__(self, name, breed, age):
self.name = name # Attribute
self.breed = breed # Attribute
self.age = age # Attribute
def bark(self): # Method
return f"{self.name} says Woof!"
def get_info(self): # Method
return f"{self.name} is a {self.age}-year-old {self.breed}"
# Creating objects (instances)
dog1 = Dog("Rex", "German Shepherd", 5)
dog2 = Dog("Bella", "Labrador", 3)
print(dog1.bark()) # Output: Rex says Woof!
print(dog2.get_info()) # Output: Bella is a 3-year-old Labrador
Class Dog
Public Property Name As String
Public Property Breed As String
Public Property Age As Integer
' Constructor
Sub New(name As String, breed As String, age As Integer)
Me.Name = name
Me.Breed = breed
Me.Age = age
End Sub
' Method
Function Bark() As String
Return Name & " says Woof!"
End Function
' Method
Function GetInfo() As String
Return Name & " is a " & Age & "-year-old " & Breed
End Function
End Class
' Creating objects (instances)
Dim dog1 As New Dog("Rex", "German Shepherd", 5)
Dim dog2 As New Dog("Bella", "Labrador", 3)
Console.WriteLine(dog1.Bark()) ' Output: Rex says Woof!
Console.WriteLine(dog2.GetInfo()) ' Output: Bella is a 3-year-old Labrador
Encapsulation
Encapsulation is the principle of bundling data (attributes) and the methods that operate on that data into a single unit (a class), and restricting direct access to some of the object’s components. This is achieved through access modifiers (public, private, protected).
Encapsulation protects an object’s internal state from unintended interference. External code accesses data through getter and setter methods (or properties) rather than directly modifying attributes.
class BankAccount:
def __init__(self, owner, balance):
self.__owner = owner # Private attribute (name mangling)
self.__balance = balance # Private attribute
def get_balance(self): # Getter method
return self.__balance
def deposit(self, amount): # Controlled access
if amount > 0:
self.__balance += amount
return True
return False
def withdraw(self, amount): # Controlled access
if 0 < amount <= self.__balance:
self.__balance -= amount
return True
return False
account = BankAccount("Alice", 1000)
account.deposit(500)
print(account.get_balance()) # Output: 1500
# account.__balance = -999 # This would NOT work as intended (encapsulation)
Class BankAccount
Private _owner As String ' Private attribute
Private _balance As Decimal ' Private attribute
Sub New(owner As String, balance As Decimal)
_owner = owner
_balance = balance
End Sub
' Property with getter only (read-only)
ReadOnly Property Balance As Decimal
Get
Return _balance
End Get
End Property
Function Deposit(amount As Decimal) As Boolean
If amount > 0 Then
_balance += amount
Return True
End If
Return False
End Function
Function Withdraw(amount As Decimal) As Boolean
If amount > 0 AndAlso amount <= _balance Then
_balance -= amount
Return True
End If
Return False
End Function
End Class
Dim account As New BankAccount("Alice", 1000)
account.Deposit(500)
Console.WriteLine(account.Balance) ' Output: 1500
' account._balance = -999 ' This would cause a compiler error (encapsulation)
Inheritance
Inheritance allows a new class (the child or subclass) to inherit attributes and methods from an existing class (the parent or superclass). The child class can extend or override the inherited behaviour.
Inheritance promotes code reuse and establishes an “is-a” relationship: a Dog is an Animal, a Car is a Vehicle.
class Animal:
def __init__(self, name, species):
self.name = name
self.species = species
def make_sound(self):
return "Some generic sound"
def get_info(self):
return f"{self.name} is a {self.species}"
# Cat inherits from Animal
class Cat(Animal):
def __init__(self, name, indoor):
super().__init__(name, "Cat") # Call parent constructor
self.indoor = indoor # Additional attribute
def make_sound(self): # Override parent method
return f"{self.name} says Meow!"
def is_indoor(self): # Additional method
return self.indoor
cat = Cat("Whiskers", True)
print(cat.get_info()) # Inherited method: Whiskers is a Cat
print(cat.make_sound()) # Overridden method: Whiskers says Meow!
print(cat.is_indoor()) # New method: True
Class Animal
Public Property Name As String
Public Property Species As String
Sub New(name As String, species As String)
Me.Name = name
Me.Species = species
End Sub
Overridable Function MakeSound() As String
Return "Some generic sound"
End Function
Function GetInfo() As String
Return Name & " is a " & Species
End Function
End Class
' Cat inherits from Animal
Class Cat
Inherits Animal
Public Property Indoor As Boolean
Sub New(name As String, indoor As Boolean)
MyBase.New(name, "Cat") ' Call parent constructor
Me.Indoor = indoor ' Additional attribute
End Sub
Overrides Function MakeSound() As String ' Override parent method
Return Name & " says Meow!"
End Function
Function IsIndoor() As Boolean ' Additional method
Return Indoor
End Function
End Class
Dim cat As New Cat("Whiskers", True)
Console.WriteLine(cat.GetInfo()) ' Inherited method: Whiskers is a Cat
Console.WriteLine(cat.MakeSound()) ' Overridden method: Whiskers says Meow!
Console.WriteLine(cat.IsIndoor()) ' New method: True
Polymorphism
Polymorphism (meaning “many forms”) allows objects of different classes to be treated through the same interface. The same method name can behave differently depending on which class the object belongs to.
class Shape:
def area(self):
return 0
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
# Polymorphism in action
shapes = [Rectangle(5, 3), Circle(4), Rectangle(2, 7)]
for shape in shapes:
print(f"Area: {shape.area():.2f}")
# Each object calls its OWN version of area()
# Output:
# Area: 15.00
# Area: 50.27
# Area: 14.00
MustInherit Class Shape
MustOverride Function Area() As Double
End Class
Class Rectangle
Inherits Shape
Public Property Width As Double
Public Property Height As Double
Sub New(width As Double, height As Double)
Me.Width = width
Me.Height = height
End Sub
Overrides Function Area() As Double
Return Width * Height
End Function
End Class
Class Circle
Inherits Shape
Public Property Radius As Double
Sub New(radius As Double)
Me.Radius = radius
End Sub
Overrides Function Area() As Double
Return Math.PI * Radius ^ 2
End Function
End Class
' Polymorphism in action
Dim shapes() As Shape = {New Rectangle(5, 3), New Circle(4), New Rectangle(2, 7)}
For Each shape As Shape In shapes
Console.WriteLine("Area: " & shape.Area().ToString("F2"))
Next
' Each object calls its OWN version of Area()
' Output:
' Area: 15.00
' Area: 50.27
' Area: 14.00
Summary of OOP Concepts
| Concept | Description | Benefit |
|---|---|---|
| Class | Blueprint for creating objects | Defines structure and behaviour |
| Object | An instance of a class | Represents a specific entity |
| Encapsulation | Bundling data and methods; hiding internal state | Protects data integrity; reduces complexity |
| Inheritance | Child class inherits from parent class | Code reuse; establishes relationships |
| Polymorphism | Same method name, different behaviour | Flexibility; extensibility |
| Abstraction | Hiding complex details behind simple interfaces | Reduces complexity for the user |
Low-Level vs High-Level Languages
Low-Level Languages
Low-level languages have very little or no abstraction from the computer’s hardware. They deal directly with memory addresses, registers, and machine instructions.
Machine code:
- Binary instructions (patterns of 1s and 0s) that the CPU can execute directly.
- Each instruction corresponds to a single operation (e.g., load, store, add).
- Completely hardware-specific – machine code for one CPU architecture will not work on another.
- Extremely difficult for humans to read or write.
Assembly language:
- Uses human-readable mnemonics (e.g.,
MOV,ADD,SUB,JMP,CMP) to represent machine code instructions. - Has a (mostly) one-to-one mapping to machine code – each assembly instruction corresponds to one machine code instruction.
- Still hardware-specific – different CPU architectures have different instruction sets.
- Requires an assembler to translate into machine code.
Example assembly code:
MOV AX, 5 ; Load the value 5 into register AX
MOV BX, 3 ; Load the value 3 into register BX
ADD AX, BX ; Add BX to AX (result: 8 in AX)
High-Level Languages
High-level languages provide a high level of abstraction from the hardware. The programmer does not need to manage memory addresses, registers, or CPU-specific instructions.
- Use English-like keywords and familiar mathematical notation.
- One high-level statement may translate to many machine code instructions.
- Portable – the same source code can (in principle) run on different hardware platforms.
- Easier to read, write, debug, and maintain.
- Examples: Python, VB.NET, Java, C#, JavaScript.
Comparison Table
| Feature | Low-Level Languages | High-Level Languages |
|---|---|---|
| Abstraction | Little or none | High |
| Readability | Difficult for humans | Easy for humans |
| Portability | Hardware-specific | Mostly portable |
| Execution speed | Very fast (close to hardware) | Slower (needs translation) |
| Memory control | Direct access to memory/registers | Managed automatically |
| Development time | Slow and error-prone | Faster and more productive |
| Use cases | Device drivers, embedded systems, OS kernels | Applications, web development, data processing |
| Translation needed | Assembler (assembly) or none (machine code) | Compiler or interpreter |
The key trade-off is control and speed vs productivity and portability. Low-level languages give direct hardware control and faster execution, but high-level languages allow programmers to be far more productive and write more maintainable code.
Translators
Source code must be translated into machine code before the CPU can execute it. There are three types of translator: assemblers, compilers, and interpreters.
Assembler
- Translates assembly language into machine code.
- Performs a (mostly) one-to-one translation – each assembly mnemonic maps to one machine code instruction.
- Resolves labels and symbolic addresses into actual memory addresses.
- The output is an executable machine code file.
Compiler
- Translates the entire high-level source code into machine code (or an intermediate form) all at once, before the program is run.
- Produces a standalone executable file (e.g.,
.exeon Windows). - The source code is not needed to run the compiled program.
- Compilation can be slow, but execution of the compiled program is fast.
- Errors are reported after the whole program is analysed, which can make debugging harder.
Interpreter
- Translates and executes high-level source code one statement at a time.
- No executable file is produced – the interpreter must be present every time the program runs.
- Stops immediately when an error is encountered, reporting the exact line, which aids debugging.
- Slower execution than compiled code, because each statement is translated every time the program runs.
Comparison Table
| Feature | Assembler | Compiler | Interpreter |
|---|---|---|---|
| Input | Assembly language | High-level source code | High-level source code |
| Output | Machine code | Executable file | No file (executes directly) |
| Translation | One-to-one | Whole program at once | One line at a time |
| Execution speed | Fast (native code) | Fast (pre-compiled) | Slow (translated at runtime) |
| Error reporting | After assembly | After full compilation | Immediately at the error line |
| Debugging | Difficult | Harder (errors after compilation) | Easier (stops at error line) |
| Distribution | Machine code only | Executable only (protects source) | Source code required |
| Portability | Hardware-specific | Platform-specific executable | Portable (if interpreter available) |
A compiler translates the entire source code into an executable before the program runs. An interpreter translates and executes one line at a time during runtime. An assembler translates assembly language (low-level) into machine code.
A common exam question is to explain why a developer might use an interpreter during development (easier debugging, immediate feedback) but compile the final product for distribution (faster execution, source code protection). Many real-world development workflows use both.
Stages of Compilation
A compiler does not simply translate code in one step. The compilation process involves several distinct stages, each transforming the source code into a form closer to machine code.
1. Lexical Analysis
The lexical analyser (or lexer/tokeniser) reads the source code character by character and converts it into a stream of tokens. It also:
- Removes whitespace and comments (these are not needed for execution).
- Identifies keywords (e.g.,
if,while,for), identifiers (variable and function names), operators (e.g.,+,=,<), literals (e.g.,42,"hello"), and punctuation (e.g.,;,{,}). - Builds a symbol table that records each identifier, its type, and its scope.
- Reports lexical errors (e.g., illegal characters).
Example: The statement total = price * quantity would be tokenised as:
| Token | Type |
|---|---|
total |
Identifier |
= |
Assignment operator |
price |
Identifier |
* |
Multiplication operator |
quantity |
Identifier |
2. Syntax Analysis (Parsing)
The syntax analyser (or parser) takes the stream of tokens and checks that they follow the grammatical rules of the language. It:
- Builds a parse tree (also called an abstract syntax tree or AST) that represents the hierarchical structure of the code.
- Checks that statements are grammatically correct (e.g., every opening bracket has a matching closing bracket, keywords are used correctly).
- Reports syntax errors (e.g., missing semicolons, unmatched brackets, incorrect use of keywords).
3. Semantic Analysis
The semantic analyser checks the meaning of the code to ensure it makes logical sense. It:
- Type checking – ensures operations are performed on compatible types (e.g., you cannot add a string to an integer without conversion).
- Scope checking – ensures variables are declared before use and used within their valid scope.
- Consistency checking – ensures functions are called with the correct number and type of arguments.
4. Code Generation
The code generator translates the validated parse tree into machine code (or an intermediate representation). It:
- Assigns variables to memory locations or CPU registers.
- Translates high-level constructs (loops, conditionals) into sequences of low-level instructions.
- Produces object code that is functionally equivalent to the source code.
5. Code Optimisation
The optimiser improves the generated code to make it run faster or use less memory, without changing its behaviour. Common optimisations include:
- Removing redundant code – eliminating instructions that have no effect.
- Constant folding – evaluating constant expressions at compile time (e.g., replacing
3 * 4with12). - Loop optimisation – moving calculations that do not change out of loops.
- Register allocation – making efficient use of CPU registers to reduce memory access.
Optimisation can occur at multiple stages and may significantly improve performance.
Summary of Compilation Stages
| Stage | Input | Output | Purpose |
|---|---|---|---|
| Lexical analysis | Source code (characters) | Token stream + symbol table | Tokenise; remove whitespace/comments |
| Syntax analysis | Token stream | Parse tree (AST) | Check grammar; build tree structure |
| Semantic analysis | Parse tree | Annotated parse tree | Check types, scope, consistency |
| Code generation | Annotated parse tree | Machine code / object code | Produce executable instructions |
| Code optimisation | Unoptimised code | Optimised code | Improve efficiency |
Linkers and Loaders
Linker
A linker combines one or more object code files (produced by the compiler) with any required library code into a single executable file. It resolves references between separately compiled modules.
When a program uses external functions (e.g., from a maths library or a standard I/O library), the compiler produces object code that contains unresolved references to those external functions. The linker:
- Combines all object code modules into one file.
- Resolves external references by linking them to the correct library code.
- Produces a complete, standalone executable.
There are two types of linking:
- Static linking – library code is copied into the executable at compile time. The executable is self-contained but larger.
- Dynamic linking – the executable contains references to shared library files (e.g.,
.dllon Windows) that are loaded at runtime. The executable is smaller, but the libraries must be available on the user’s machine.
Loader
A loader is a part of the operating system that loads an executable file from secondary storage into main memory (RAM) and prepares it for execution by the CPU.
The loader:
- Allocates memory space for the program.
- Copies the executable code and data from disk into RAM.
- Resolves any remaining dynamic library references.
- Sets the program counter to the starting address of the program so execution can begin.
IDE Features
An Integrated Development Environment (IDE) is a software application that provides a comprehensive set of tools for software development within a single interface. Examples include Visual Studio, PyCharm, Eclipse, and VS Code.
Key Features of an IDE
| Feature | Description | Benefit |
|---|---|---|
| Code editor | A text editor with syntax highlighting, line numbering, and code indentation | Makes code easier to read; highlights language keywords in colour |
| Auto-complete / IntelliSense | Suggests variable names, function names, and keywords as you type | Speeds up coding; reduces typos; helps recall syntax |
| Syntax highlighting | Displays different code elements (keywords, strings, comments, variables) in different colours | Improves readability; makes errors easier to spot |
| Compiler / interpreter | Built-in translator to compile or interpret code directly within the IDE | No need to switch to a separate tool; one-click build and run |
| Debugger | Tools for stepping through code line by line, setting breakpoints, and inspecting variable values | Helps find and fix logic errors by observing program behaviour |
| Error diagnostics | Highlights syntax errors and warnings in real time, often with suggested fixes | Catches mistakes as you type, before you even try to compile |
| Version control integration | Built-in support for Git or other VCS tools | Track changes, collaborate, and revert to previous versions |
| Project management | Organises files, resources, and settings for a project | Keeps related files together; simplifies navigation |
| Run/build tools | One-click compilation, execution, and testing | Streamlines the development workflow |
| Console/terminal | Built-in terminal for running commands | No need to switch to an external terminal |
Debugging Tools in Detail
Debugging is one of the most important features of an IDE. Key debugging capabilities include:
- Breakpoints – markers placed on specific lines of code where execution will pause. This allows the programmer to examine the state of the program at that point.
- Stepping – executing the program one line at a time (step over, step into, step out) to observe exactly what happens at each stage.
- Watch window – monitors the values of selected variables as the program executes.
- Call stack – shows the sequence of function calls that led to the current point of execution, which is particularly useful for debugging recursive functions.
- Variable inspection – hovering over a variable to see its current value.
IDE questions often ask you to describe specific features and explain how they help the programmer. Always link the feature to a practical benefit. For example: “The debugger allows the programmer to set breakpoints and step through code line by line, which helps identify logic errors by observing the actual values of variables at each stage of execution.”
Constants and Variables
A variable is a named storage location in memory whose value can change during program execution. A constant is a named value that is set once and cannot be changed during execution.
Why Use Constants?
- Maintainability — if a value needs to change (e.g. VAT rate), you only change it in one place
- Readability —
VAT_RATEis more meaningful than0.2scattered through code - Safety — prevents accidental modification of values that should not change
- Performance — some compilers can optimise constants more efficiently
# Python — Constants and variables
VAT_RATE = 0.2 # Python has no true constants, but UPPER_CASE convention signals "do not change"
MAX_ATTEMPTS = 3
price = float(input("Enter price: "))
total = price + (price * VAT_RATE)
print(f"Total including VAT: £{total:.2f}")
' VB.NET — Constants and variables
Const VAT_RATE As Double = 0.2
Const MAX_ATTEMPTS As Integer = 3
Dim price As Double
Console.Write("Enter price: ")
price = CDbl(Console.ReadLine())
Dim total As Double = price + (price * VAT_RATE)
Console.WriteLine("Total including VAT: £" & total.ToString("F2"))
MOD and DIV Operators
MOD (modulus) returns the remainder after integer division. DIV (integer division) returns the whole number result of division, discarding the remainder.
| Expression | Result | Explanation |
|---|---|---|
17 MOD 5 |
2 | 17 ÷ 5 = 3 remainder 2 |
17 DIV 5 |
3 | 17 ÷ 5 = 3 remainder 2 |
20 MOD 4 |
0 | 20 ÷ 4 = 5 remainder 0 (exactly divisible) |
7 MOD 2 |
1 | 7 ÷ 2 = 3 remainder 1 (odd number check) |
100 DIV 7 |
14 | 100 ÷ 7 = 14 remainder 2 |
# Python — MOD and DIV
number = 17
remainder = number % 5 # MOD — result: 2
quotient = number // 5 # DIV (integer division) — result: 3
# Common use: check if a number is even
if number % 2 == 0:
print("Even")
else:
print("Odd")
# Common use: extract digits
last_digit = 1234 % 10 # Result: 4
remaining = 1234 // 10 # Result: 123
' VB.NET — MOD and DIV
Dim number As Integer = 17
Dim remainder As Integer = number Mod 5 ' MOD — result: 2
Dim quotient As Integer = number \ 5 ' DIV (integer division) — result: 3
' Common use: check if a number is even
If number Mod 2 = 0 Then
Console.WriteLine("Even")
Else
Console.WriteLine("Odd")
End If
MOD and DIV are commonly used in exam questions for tasks like checking if a number is even or odd (n MOD 2), extracting individual digits from a number, or converting between units (e.g. converting minutes to hours and remaining minutes: hours = minutes DIV 60, mins = minutes MOD 60).
Self-Documenting Identifiers
Self-documenting code is code that can be understood by reading it, without needing additional comments. The most important technique is using meaningful identifier names.
Naming Conventions
| Convention | Style | Example | Typically Used In |
|---|---|---|---|
| camelCase | First word lowercase, subsequent words capitalised | studentAge, totalMarks |
Java, JavaScript, VB.NET variables |
| PascalCase | Every word capitalised | GetStudentName, CalculateTotal |
VB.NET methods, C# classes |
| snake_case | Words separated by underscores | student_age, total_marks |
Python variables and functions |
| UPPER_SNAKE_CASE | All capitals with underscores | MAX_SIZE, VAT_RATE |
Constants in most languages |
Good vs Bad Naming
| Bad | Good | Why |
|---|---|---|
x |
student_count |
Describes what the data represents |
calc() |
calculate_average() |
Describes what the function does |
flag |
is_logged_in |
Boolean names should read as true/false questions |
lst |
exam_results |
Describes the content of the collection |
temp |
previous_total |
Avoids ambiguity |
Rogue Values and Sentinel Values
A rogue value (also called a sentinel value) is a special value entered by the user to signal the end of data input. It must be a value that could never be valid data in the context of the program.
Example: Using -1 as a Rogue Value for Positive Marks
# Python — Rogue value to end input
total = 0
count = 0
mark = int(input("Enter a mark (-1 to finish): "))
while mark != -1:
total += mark
count += 1
mark = int(input("Enter a mark (-1 to finish): "))
if count > 0:
average = total / count
print(f"Average mark: {average:.1f}")
else:
print("No marks entered.")
' VB.NET — Rogue value to end input
Dim total As Integer = 0
Dim count As Integer = 0
Console.Write("Enter a mark (-1 to finish): ")
Dim mark As Integer = CInt(Console.ReadLine())
While mark <> -1
total += mark
count += 1
Console.Write("Enter a mark (-1 to finish): ")
mark = CInt(Console.ReadLine())
End While
If count > 0 Then
Dim average As Double = total / count
Console.WriteLine("Average mark: " & average.ToString("F1"))
Else
Console.WriteLine("No marks entered.")
End If
When choosing a rogue value, it must be outside the range of valid data. For example, -1 works for positive marks but would not work for temperature readings (which can be negative). Always state clearly why the rogue value could never be confused with valid data.
UML Class Diagrams
A UML (Unified Modelling Language) class diagram is a visual representation of a class in object-oriented programming. It shows the class name, its attributes (data), and its methods (behaviours).
Class Diagram Notation
┌──────────────────────────┐
│ ClassName │
├──────────────────────────┤
│ - privateAttribute: Type │
│ # protectedAttribute: Type│
│ + publicAttribute: Type │
├──────────────────────────┤
│ + publicMethod(): Type │
│ - privateMethod(): Type │
└──────────────────────────┘
Access Modifiers
| Symbol | Modifier | Meaning |
|---|---|---|
+ |
Public | Accessible from anywhere |
- |
Private | Only accessible within the class |
# |
Protected | Accessible within the class and its subclasses |
Example: Inheritance Diagram
┌──────────────────────┐
│ Person │
├──────────────────────┤
│ # name: String │
│ # dateOfBirth: Date │
├──────────────────────┤
│ + getName(): String │
│ + getAge(): Integer │
└──────────┬───────────┘
│ inherits
┌─────┴─────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Pupil │ │ Staff │
├─────────────┤ ├─────────────┤
│ - form: Str │ │ - role: Str │
│ - year: Int │ │ - salary: Dec│
├─────────────┤ ├─────────────┤
│ + getForm() │ │ + getRole() │
└─────────────┘ └─────────────┘
In this diagram, Pupil and Staff both inherit from Person, gaining the name, dateOfBirth, getName() and getAge() members. Each subclass adds its own specific attributes and methods.
In exam questions, you may be asked to read or draw a class diagram. Pay attention to the access modifiers (+, -, #) and the inheritance arrows. A subclass inherits all public and protected members from the parent class.