CCEA AS-Level · thinka-original Practice Paper

2024 CCEA AS-Level Software Systems Development CL4 Practice Paper with Answers

Thinka Jun 2024 CCEA AS Level-Style Mock — Software Systems Development CL4

100 marks120 mins2024
An original Thinka practice paper modelled on the structure and difficulty of the Jun 2024 CCEA AS Level Software Systems Development CL4 paper. Not affiliated with or reproduced from CCEA.

Section Unit AS 1 Examination

Answer all six questions in the spaces provided. Complete in black ink only.
18 Question · 100 marks
Question 1 · True/False Concept Identification
6 marks
State whether each of the following statements is TRUE or FALSE.
(a) A class is a blueprint or template used to create objects, but a class itself is not an object. (1)
(b) An integer data type can be used to store a value with a decimal point, such as 3.14, without any loss of precision. (1)
(c) In C# and Java, a constructor has the same name as the class it belongs to. (1)
(d) A local variable declared inside a method can be accessed directly from outside that method, without being passed as a parameter or returned. (1)
(e) The 'private' access modifier allows a field to be accessed directly from any other class in the same program. (1)
(f) Encapsulation involves combining data (fields) and the methods that operate on that data within a single class, while restricting direct access to that data from outside the class. (1)
Show answer & marking scheme

Worked solution

(a) True: a class defines the structure (fields and methods) that its objects will have, but the class itself is not an instance/object; an object is created ('instantiated') from a class using a constructor. (b) False: an integer data type can only store whole numbers; storing a value such as 3.14 in an integer variable would either cause a compile error or result in the decimal part being lost/truncated (e.g. stored as 3), so it cannot be stored without loss of precision — a floating-point type such as float or double is needed. (c) True: in both C# and Java, a constructor must be given exactly the same name as its class (and has no return type). (d) False: a local variable's scope is limited to the method (or block) in which it is declared; it cannot be accessed directly from outside that method unless its value is returned or otherwise passed out. (e) False: the 'private' access modifier restricts access to within the same class only; it specifically prevents direct access from other classes. (f) True: encapsulation bundles data and the methods that operate on it together within a class, and restricts direct external access to that data (e.g. via private fields), typically requiring interaction through the class's own public methods. Final answer: (a) True, (b) False, (c) True, (d) False, (e) False, (f) True.

Marking scheme

1 mark for each correct True/False response. Max 6.
Question 2 · Standalone Validation Method
8 marks
Write a method named isValidPIN that takes one parameter, pin (a String), and returns a boolean value. The method should return true only if:
- pin is exactly 4 characters long, AND
- every character in pin is a numeric digit ('0'-'9').
The method should return false in all other cases. You should write your method using a loop to check each character of pin. [8]
Show answer & marking scheme

Worked solution

The method first checks whether pin is exactly 4 characters long (using pin.Length in C# or pin.length() in Java); if it is not, the method returns false immediately, since a PIN of the wrong length can never be valid. If the length check passes, the method then loops through each character of pin in turn (using an index i from 0 up to, but not including, the length), testing each character with char.IsDigit(pin[i]) in C# or Character.isDigit(pin.charAt(i)) in Java; if any character is found that is not a digit, the method returns false immediately, since a single non-digit character makes the whole PIN invalid. If the loop completes without finding any non-digit character, this means every character checked was a digit and the length was already confirmed to be 4, so the method returns true. Verifying with sample data: for pin = '1234' (length 4, all digits), the length check passes and the loop finds no non-digit characters, so the method correctly returns true; for pin = '123' (length 3), the length check fails immediately and the method correctly returns false; for pin = '12a4' (length 4, but 'a' at index 2 is not a digit), the length check passes but the loop detects 'a' is not a digit and correctly returns false. Final answer: the method as written above correctly validates that pin is exactly 4 digits long, returning true only when both conditions are met.

Marking scheme

1 mark: correct method header (name, parameter type String/string, return type boolean/bool). 1 mark: correct length check (pin.Length/pin.length() compared to 4). 1 mark: correct return false when length check fails. 1 mark: correct loop structure iterating over all characters of pin (correct bounds, e.g. 0 to length-1). 1 mark: correct check that each character is a digit (char.IsDigit/Character.isDigit or equivalent, e.g. comparing against '0' and '9'). 1 mark: correct return false when a non-digit character is found. 1 mark: correct return true after the loop completes with no non-digit characters found. 1 mark: overall correct, well-structured logic (e.g. appropriate early-exit/short-circuit structure). Max 8. Accept alternative correct solutions, e.g. using pin.All(char.IsDigit) in C#, or a boolean flag variable instead of early return, provided the logic is equivalent and correct.
Question 3 · 2D Array Implementation & Processing
5 marks
A small shop uses a 2D array named weeklySales to store the number of units sold of 3 products over a 4-week period, declared as a 3 x 4 array of integers, weeklySales[product][week] (product 0 = Product A, product 1 = Product B, product 2 = Product C; week 0-3 = weeks 1-4).

Write a statement to declare and initialise the array weeklySales with the following data:
Product A: 12, 15, 9, 20
Product B: 8, 11, 14, 10
Product C: 25, 22, 18, 30 [5]
Show answer & marking scheme

Worked solution

The array is declared as a 2D array of integers, with 3 rows (one per product) and 4 columns (one per week). In C#, a rectangular 2D array is declared using int[,] and initialised with a nested brace list, one inner brace group per row: { {12, 15, 9, 20}, {8, 11, 14, 10}, {25, 22, 18, 30} }; the first inner group (Product A) contains 12, 15, 9, 20; the second (Product B) contains 8, 11, 14, 10; the third (Product C) contains 25, 22, 18, 30. In Java, a 2D array is declared using int[][] with an equivalent nested-brace initialiser. Checking the structure: there are exactly 3 inner groups (matching 3 products) and each inner group contains exactly 4 values (matching 4 weeks), so weeklySales[0] is Product A's data, weeklySales[1] is Product B's, and weeklySales[2] is Product C's, as required. Final answer: as shown above in both C# and Java.

Marking scheme

1 mark: correct 2D array declaration syntax (int[,] in C# or int[][] in Java) with the array named weeklySales. 1 mark: correct overall structure of 3 rows x 4 columns (3 inner groups of 4 values each). 3 marks: correct data values in the correct row order (1 mark per correctly placed product row: Product A = 12,15,9,20; Product B = 8,11,14,10; Product C = 25,22,18,30). Max 5. Accept an equivalent solution that declares the array first and assigns values via separate statements or a loop, provided the final array contents and dimensions are correct.
Question 4 · 2D Array Implementation & Processing
5 marks
Using the 2D array weeklySales described in the previous question, write a method named getProductTotal that takes the array weeklySales and an integer parameter productIndex, and returns the total sales (an int) for that product across all 4 weeks, using a loop. [5]
Show answer & marking scheme

Worked solution

The method declares a variable total, initialised to 0, to accumulate the sum. It then loops through each week index from 0 up to (but not including) 4, adding the value stored at weeklySales[productIndex, week] (C#) or weeklySales[productIndex][week] (Java) to total on each pass; after the loop has processed all 4 weeks, the method returns total. Verifying with the data from the previous question: for productIndex = 0 (Product A: 12, 15, 9, 20), total = 12 + 15 + 9 + 20 = 56; checking by a second route, grouping differently: (12 + 20) + (15 + 9) = 32 + 24 = 56, confirming the same result, so calling getProductTotal with productIndex 0 on this data would correctly return 56. Final answer: the method as written above correctly sums and returns the four weekly values for the specified product.

Marking scheme

1 mark: correct method header (name, parameters weeklySales and productIndex, return type int). 1 mark: total variable correctly declared and initialised to 0. 1 mark: correct loop bounds iterating over all 4 weeks. 1 mark: correct indexing and accumulation (weeklySales[productIndex, week] or weeklySales[productIndex][week] added to total). 1 mark: correct return of total. Max 5. Accept use of a fixed literal 4 or a dynamically obtained length (e.g. weeklySales.GetLength(1) in C#, or weeklySales[0].length in Java) for the loop bound.
Question 5 · 2D Array Implementation & Processing
5 marks
Using the 2D array weeklySales described in Question 3, write a method named getWeekWithHighestTotalSales that takes the array weeklySales as a parameter and returns the index (0-3) of the week with the highest combined sales across all three products, using nested loops. [5]
Show answer & marking scheme

Worked solution

The outer loop iterates over each week (0 to 3); for each week, the inner loop iterates over each of the 3 products, adding weeklySales[product, week] (or [product][week]) to a running total, weekTotal, for that week. After the inner loop completes, the method compares weekTotal with the best total found so far (bestTotal, starting at 0); if weekTotal is greater, both bestTotal and bestWeek are updated. After all weeks have been checked, bestWeek is returned. Verifying using the data from Question 3 (Product A: 12,15,9,20; Product B: 8,11,14,10; Product C: 25,22,18,30): week 0 total = 12+8+25 = 45; week 1 total = 15+11+22 = 48; week 2 total = 9+14+18 = 41; week 3 total = 20+10+30 = 60. Tracing the algorithm: week 0 gives 45 > 0, so bestTotal=45, bestWeek=0; week 1 gives 48 > 45, so bestTotal=48, bestWeek=1; week 2 gives 41, which is not > 48, so no change; week 3 gives 60 > 48, so bestTotal=60, bestWeek=3. The method therefore correctly returns 3, matching the week (week 4, index 3) with the highest total sales (60) in this data, confirming the logic is correct. Final answer: the method as written above correctly identifies and returns the index of the week with the highest combined sales.

Marking scheme

1 mark: correct method header (name, parameter weeklySales, return type int). 1 mark: correct nested loop structure (outer loop over weeks, inner loop over products, or a logically equivalent structure). 1 mark: correct calculation of the running total for each week (weekTotal correctly accumulated within the inner loop, reset for each new week). 1 mark: correct comparison and update of bestTotal/bestWeek when a higher week total is found. 1 mark: correct return of bestWeek. Max 5. Accept equivalent alternative structures, e.g. calculating each week's total using a separate helper method.
Question 6 · 2D Array Implementation & Processing
4 marks
A new week (week 5) of sales data needs to be added to weeklySales, but the array was declared with a fixed size of 3 x 4 and cannot be resized. Explain what a programmer would need to do to accommodate this extra week of data, and state one limitation of using a standard array (rather than another data structure) in this situation. [4]
Show answer & marking scheme

Worked solution

Because a standard array's size is fixed at the point it is created and cannot be altered afterwards, accommodating an extra week of data requires the programmer to declare a new array with a larger size (in this case, 3 rows by 5 columns, to hold the extra week), then copy every existing value from the old 3 x 4 array into the corresponding position in the new, larger array, before finally adding the new week's three values (one per product) into the new column. Only once this copying process is complete can the new array be used in place of the old one. The key limitation this illustrates is that a standard array has a fixed size, so any change to the amount of data it needs to store requires this manual process of creating a new array and copying all existing elements across; this is inefficient, both in terms of the extra code required and the processing time/memory used each time it happens, particularly if data needs to be added repeatedly over time, whereas a dynamic/resizable data structure, such as a list, is designed to grow (or shrink) automatically as items are added or removed, without the programmer needing to manually manage this resizing process. Final answer: create a new, larger array and copy the existing data across, then add the new week's values; the key limitation is that arrays have a fixed size, making this manual resize-and-copy process necessary and inefficient compared with a dynamic data structure such as a list.

Marking scheme

2 marks: correct description of the process needed (create a new, larger array; copy existing data from the old array into the new array; add the new data). 2 marks: correct statement of the limitation (arrays have a fixed size, so a new array must be created and existing data copied across to change size, which is inefficient), ideally with reference to a suitable alternative dynamic data structure (e.g. a list). Max 4.
Question 7 · Class Design, Constructor, Business Logic & Substring Search
5 marks
A small business uses a class to represent items available for hire. Write a class named EquipmentItem with the following private fields: itemName (String), dailyRate (double), isAvailable (boolean). Write a constructor for the class that takes parameters for itemName and dailyRate, sets these fields accordingly, and sets isAvailable to true by default. [5]
Show answer & marking scheme

Worked solution

The class EquipmentItem is declared with three private fields, matching the required types: itemName as a String/string, dailyRate as a double, and isAvailable as a boolean/bool; declaring these fields private means they cannot be accessed directly from outside the class, in line with encapsulation. The constructor is given exactly the same name as the class, and takes two parameters, itemName and dailyRate, matching the two values the class user is expected to supply when creating a new item; inside the constructor, this.itemName and this.dailyRate are assigned from the corresponding parameters (using 'this' to distinguish the field from the parameter of the same name), and isAvailable is explicitly set to true, giving every newly created EquipmentItem a sensible default state (available for hire) without the caller needing to specify it. Final answer: the class and constructor as written above correctly declare the three required private fields and initialise a new EquipmentItem from the two supplied parameters, defaulting isAvailable to true.

Marking scheme

1 mark: correct class declaration with the three private fields, all correctly typed (String/string, double, boolean/bool). 1 mark: correct constructor header (same name as class, two parameters itemName and dailyRate). 1 mark: correct assignment of itemName from the parameter. 1 mark: correct assignment of dailyRate from the parameter. 1 mark: correct default assignment of isAvailable to true. Max 5.
Question 8 · Class Design, Constructor, Business Logic & Substring Search
5 marks
Write a method named calculateRentalCost within the EquipmentItem class described in the previous question. The method takes one integer parameter, numberOfDays, and returns the total rental cost (a double) for renting the item for that many days, calculated as dailyRate multiplied by numberOfDays. If the item is not available (isAvailable is false), the method should return 0 instead. [5]
Show answer & marking scheme

Worked solution

The method first checks whether the item is unavailable, using the condition !isAvailable (true when isAvailable is false); if the item is unavailable, the method returns 0 immediately, since an unavailable item cannot generate a rental cost. If the item is available, the method calculates and returns dailyRate multiplied by numberOfDays, giving the total rental cost. Verifying with example values: if dailyRate = 15.0 and numberOfDays = 4, and the item is available, the method returns 15.0 x 4 = 60.0; checking by repeated addition as a second route, 15.0 + 15.0 + 15.0 + 15.0 = 60.0, confirming the calculation is correct. If the same item had isAvailable = false, the method would correctly return 0 regardless of numberOfDays. Final answer: the method as written above correctly returns the rental cost when the item is available, and 0 when it is not.

Marking scheme

1 mark: correct method header (name, parameter numberOfDays of type int, return type double). 1 mark: correct check of isAvailable (or !isAvailable). 1 mark: correct return of 0 when the item is not available. 1 mark: correct calculation (dailyRate multiplied by numberOfDays). 1 mark: correct return of the calculated value when the item is available. Max 5.
Question 9 · Class Design, Constructor, Business Logic & Substring Search
5 marks
Write a method named markAsRented within the EquipmentItem class. The method takes no parameters and sets isAvailable to false, but only if the item is currently available; if the item is already unavailable, the field should be left unchanged. The method should return a boolean indicating whether the item was successfully marked as rented (true), or not (false, if it was already unavailable). [5]
Show answer & marking scheme

Worked solution

The method checks the current value of isAvailable; if it is true (the item is currently available), the method sets isAvailable to false (marking the item as rented) and returns true, to indicate the operation succeeded. If isAvailable is already false (the item was already unavailable), the if-block is skipped entirely, isAvailable is left unchanged, and the method falls through to return false, correctly indicating the item could not be marked as rented because it was already unavailable. Verifying with example states: starting with isAvailable = true, calling markAsRented() sets isAvailable to false and returns true; calling markAsRented() again immediately afterwards, with isAvailable now false, does not change isAvailable (it remains false) and correctly returns false, confirming the method behaves as required in both cases. Final answer: the method as written above correctly marks an available item as rented and returns true, or leaves an already-unavailable item unchanged and returns false.

Marking scheme

1 mark: correct method header (name, no parameters, return type boolean/bool). 1 mark: correct check of the current value of isAvailable. 1 mark: correct assignment of isAvailable to false within the true branch only. 1 mark: correct return of true when the item was successfully marked as rented. 1 mark: correct return of false when the item was already unavailable (with isAvailable correctly left unchanged). Max 5.
Question 10 · Class Design, Constructor, Business Logic & Substring Search
4 marks
Write a method named getItemCode within the EquipmentItem class. The method returns a String consisting of the first three characters of itemName, converted to uppercase (for example, if itemName is 'projector', the method should return 'PRO'). You may assume itemName always contains at least three characters. [4]
Show answer & marking scheme

Worked solution

The method first extracts the first three characters of itemName using Substring(0, 3) in C# (which takes 3 characters starting at index 0) or substring(0, 3) in Java (which takes characters from index 0 up to, but not including, index 3); in both languages, this call extracts the characters at positions 0, 1 and 2. The resulting substring is then converted to uppercase using ToUpper() in C# or toUpperCase() in Java, and this final value is returned. Verifying with the example given: for itemName = 'projector', characters at positions 0, 1, 2 are 'p', 'r', 'o', so the substring extracted is 'pro'; converting this to uppercase gives 'PRO', matching the expected output stated in the question. Final answer: the method as written above correctly returns 'PRO' for itemName = 'projector', and more generally returns the first three characters of itemName in uppercase.

Marking scheme

1 mark: correct method header (name, no parameters, return type String/string). 1 mark: correct extraction of the first three characters (Substring(0,3) in C# or substring(0,3) in Java). 1 mark: correct conversion to uppercase (ToUpper()/toUpperCase()). 1 mark: correct return of the final value. Max 4. Accept equivalent alternative correct substring calls (e.g. Substring(0,3) chained directly with ToUpper() in a single statement).
Question 11 · Class Design, Constructor, Business Logic & Substring Search
4 marks
A programmer wants to check whether the word 'broken' appears anywhere within a String description of an equipment item's condition, for example description = 'Screen has a small crack, otherwise not broken'. Write a boolean expression, or a short code snippet, that would evaluate to true if the substring 'broken' is found anywhere within description, and false otherwise. [4]
Show answer & marking scheme

Worked solution

Both C# and Java provide a built-in substring-search facility on the String type. In C#, description.Contains("broken") searches description for the substring "broken" and directly returns a bool: true if the substring is found anywhere within description, and false if it is not. In Java, description.contains("broken") behaves identically, directly returning a boolean. An equally valid alternative uses IndexOf/indexOf, which returns the starting position of the first occurrence of the substring, or -1 if it is not found at all; comparing this result to -1 (description.IndexOf("broken") != -1, or the Java equivalent) gives an expression that evaluates to true exactly when the substring is present. Verifying with the example given: description = 'Screen has a small crack, otherwise not broken' does contain the word 'broken' (near the end of the string), so both description.Contains("broken") and description.IndexOf("broken") != -1 correctly evaluate to true for this example; if description instead read 'Screen is fully intact', neither expression would find the substring, and both would correctly evaluate to false. Final answer: description.Contains("broken") in C#, or description.contains("broken") in Java (or the equivalent IndexOf/indexOf comparison to -1), correctly evaluates to true if and only if 'broken' appears anywhere within description.

Marking scheme

1 mark: correct identification that a substring-search facility is needed (e.g. Contains/contains, or IndexOf/indexOf). 1 mark: correct method name and case-correct syntax for the chosen language. 1 mark: correct argument passed (the literal string "broken"). 1 mark: expression correctly evaluates to a boolean indicating presence/absence of the substring (e.g. correct comparison to -1 if using IndexOf/indexOf). Max 4. Accept either the Contains/contains approach or the IndexOf/indexOf approach, or any other correct, equivalent substring-search method available in the chosen language.
Question 12 · Testing Terminology & Test Plan Formulation
5 marks
State what is meant by each of the following testing terms:
(a) normal data (1)
(b) boundary/extreme data (1)
(c) erroneous (invalid) data (1)
(d) expected result (1)
(e) actual result (1)
Show answer & marking scheme

Worked solution

(a) Normal data refers to data values that fall clearly within the range a program is designed to handle, representing typical, realistic use of the program; testing with normal data checks that the program produces correct results under everyday conditions. (b) Boundary (or extreme) data refers to data values that sit right at, or just outside, the edge of an acceptable range (for example, the smallest or largest value that should still be accepted, or the first value just beyond that limit); testing with boundary data specifically checks that a program handles these edge cases correctly, since errors often occur at boundaries (e.g. using a wrong comparison operator such as > instead of >=). (c) Erroneous (or invalid) data refers to data that should not be accepted by the program, because it is of the wrong data type, wrong format, or falls outside the valid range; testing with erroneous data checks that the program correctly identifies and rejects (rather than incorrectly processes) invalid input. (d) The expected result is the result a tester works out, in advance of running a test, that the program should produce for a given piece of test data, based on the program's specification/requirements. (e) The actual result is the result the program genuinely produces when the test is actually run with that test data; comparing the actual result against the expected result determines whether the test has passed (they match) or failed (they do not match). Final answer: definitions as given above for normal data, boundary/extreme data, erroneous/invalid data, expected result and actual result.

Marking scheme

1 mark for each correct definition: (a) normal data; (b) boundary/extreme data; (c) erroneous/invalid data; (d) expected result; (e) actual result. Max 5. Accept alternative correct wording that captures the same meaning.
Question 13 · Testing Terminology & Test Plan Formulation
5 marks
Complete a test plan for the isValidPIN method written in Question 2, by giving suitable test data and the expected result for each of the following test types:
(a) normal (valid) data (1)
(b) boundary data (1)
(c) erroneous data of the wrong length (1)
(d) erroneous data containing a non-numeric character (1)
(e) Explain why testing with erroneous data is an important part of thoroughly testing a method such as isValidPIN. (1)
Show answer & marking scheme

Worked solution

(a) For normal (valid) data, '1234' is exactly 4 characters long and every character is a digit, so isValidPIN should return true. (b) For boundary data, '123' is exactly one character shorter than the required length of 4, testing the edge of the length condition; since the length check requires exactly 4 characters, '123' should be rejected and isValidPIN should return false. (c) For erroneous data of the wrong length, '12345' is 5 characters long, which fails the length check, so isValidPIN should return false. (d) For erroneous data containing a non-numeric character, '12a4' is exactly 4 characters long (passing the length check), but the character 'a' at position 2 is not a digit, so the character-checking loop should detect this and isValidPIN should return false. Each of these expected results is consistent with tracing the method written in Question 2 by hand using the given test data. (e) Testing only with data the method is expected to handle correctly (normal data) would not reveal whether the method also correctly rejects data it is not supposed to accept; a method that always returned true, for example, would pass every normal-data test but would be seriously flawed, since it would fail to protect the system from invalid input. Testing with erroneous data specifically checks that the validation logic (both the length check and the digit check) is actually working to detect and reject invalid input, which is the whole purpose of a validation method; without this, invalid data (e.g. a PIN of the wrong length, or containing letters) could be wrongly accepted, potentially causing errors or security issues later in the program. Final answer: (a) '1234' -> true; (b) '123' -> false; (c) '12345' -> false; (d) '12a4' -> false; (e) erroneous-data testing confirms the method actually detects and rejects invalid input, which is the core purpose of a validation method, and which normal-data testing alone cannot confirm.

Marking scheme

(a) 1 mark for valid normal test data with the correct expected result (true). (b) 1 mark for valid boundary test data (e.g. a string one character shorter or longer than the required length) with the correct expected result (false). (c) 1 mark for valid erroneous test data of the wrong length with the correct expected result (false). (d) 1 mark for valid erroneous test data containing a non-numeric character, of otherwise correct length, with the correct expected result (false). (e) 1 mark for a valid explanation of the importance of erroneous-data testing (e.g. confirms the method correctly rejects invalid input, which normal-data testing alone cannot show). Max 5. Own figure/logic rule applies: expected results must be consistent with the candidate's own test data traced through the isValidPIN logic.
Question 14 · Inheritance Hierarchy, Virtual/Override & Polymorphic Processing
7 marks
A business wants to model different types of employee for a payroll system. Write the Employee base class described below:
- two protected fields: name (String) and basicPay (double);
- a constructor that takes parameters for name and basicPay and assigns them to the fields;
- a method named calculatePay that takes no parameters, returns a double, and returns the value of basicPay. This method should be written so that it CAN be overridden by a subclass. [7]
Show answer & marking scheme

Worked solution

The Employee class declares two protected fields, name and basicPay, with protected access chosen (rather than private) specifically so that subclasses inheriting from Employee can access these fields directly. The constructor is given the same name as the class and takes two parameters, name and basicPay, which are assigned to the corresponding fields using 'this' to distinguish the field from the parameter. The calculatePay method takes no parameters, has return type double, and simply returns basicPay, giving the base (default) pay calculation for a plain Employee. Crucially, for this method to be overridable by a subclass, C# requires the method to be explicitly marked with the 'virtual' keyword (without this, an attempt to override it in a subclass would not compile as intended); in Java, by contrast, instance methods are overridable by default (unless explicitly marked 'final'), so no special keyword is required for Java's calculatePay to be overridable. Final answer: the class as written above correctly declares the required protected fields, a constructor that assigns them, and a calculatePay method that returns basicPay and is override-ready (virtual in C#; overridable by default in Java).

Marking scheme

1 mark: correct class declaration named Employee. 1 mark: correct two protected fields, name and basicPay, correctly typed (String/string and double). 1 mark: correct constructor header (same name as class, two parameters). 1 mark: correct assignment of both fields within the constructor. 1 mark: correct calculatePay method header (no parameters, return type double). 1 mark: correct return of basicPay. 1 mark: method correctly written to be overridable (use of 'virtual' in C#; in Java, accept the method as written since Java methods are overridable by default unless marked final — credit given for NOT using 'final'). Max 7.
Question 15 · Inheritance Hierarchy, Virtual/Override & Polymorphic Processing
7 marks
Write a class named SalesEmployee that inherits from the Employee class described in the previous question. SalesEmployee should have:
- one additional private field, commission (double);
- a constructor that takes parameters for name, basicPay and commission; it should call the Employee constructor to set name and basicPay, and then set the commission field;
- an overridden calculatePay method that returns basicPay plus commission. [7]
Show answer & marking scheme

Worked solution

The class header declares SalesEmployee as inheriting from Employee, using ': Employee' in C# or 'extends Employee' in Java; this gives SalesEmployee access to Employee's protected fields (name, basicPay) and its constructor, in addition to its own new private field, commission. The constructor takes three parameters, name, basicPay and commission; it uses ': base(name, basicPay)' in C#, or an explicit call to 'super(name, basicPay)' as the first statement in Java, to invoke the Employee constructor and correctly initialise the inherited fields, before assigning the commission parameter to the commission field. The calculatePay method is marked 'override' in C# (matching the 'virtual' method it overrides in Employee) and annotated '@Override' in Java (a good-practice annotation, though not strictly required for overriding to work in Java); it returns basicPay + commission, using the protected basicPay field inherited directly from Employee, giving sales employees a different pay calculation from the base Employee class. Verifying with example values: for a SalesEmployee with basicPay = 1600 and commission = 450, calculatePay would correctly return 1600 + 450 = 2050. Final answer: the class as written above correctly inherits from Employee, adds and initialises the commission field via constructor chaining, and overrides calculatePay to return basicPay plus commission.

Marking scheme

1 mark: correct class header showing inheritance from Employee (': Employee' in C# or 'extends Employee' in Java), with the additional private field commission correctly typed (double). 1 mark: correct constructor header (three parameters: name, basicPay, commission). 2 marks: correct call to the base/super constructor passing name and basicPay (1 mark for correct syntax — ': base(...)' or 'super(...)' as the first statement in the constructor body — 1 mark for correct arguments passed). 1 mark: correct assignment of the commission field. 1 mark: correct override method header (override/@Override, correct return type double, no parameters). 1 mark: correct calculation and return (basicPay + commission). Max 7.
Question 16 · Inheritance Hierarchy, Virtual/Override & Polymorphic Processing
7 marks
Write a second class named ManagerEmployee that also inherits from Employee. ManagerEmployee should have:
- one additional private field, teamSize (int);
- a constructor that takes parameters for name, basicPay and teamSize; it should call the Employee constructor to set name and basicPay, and then set the teamSize field;
- an overridden calculatePay method that returns basicPay plus a bonus of £50 for every member of the team, i.e. basicPay + (teamSize x 50). [7]
Show answer & marking scheme

Worked solution

Following the same pattern as SalesEmployee, ManagerEmployee inherits from Employee and adds one new private field, teamSize, of type int. The constructor takes three parameters, name, basicPay and teamSize; it calls the Employee constructor via ': base(name, basicPay)' in C# or 'super(name, basicPay)' as the first statement in Java to initialise the inherited fields, then assigns teamSize to the new field. The overridden calculatePay method returns basicPay plus a bonus calculated as teamSize multiplied by 50 (representing £50 per team member), giving managers a different pay calculation based on how many staff they manage. Verifying with example values: for a ManagerEmployee with basicPay = 2200 and teamSize = 4, calculatePay would correctly return 2200 + (4 x 50) = 2200 + 200 = 2400; checking this by a second route, adding 50 four times, 50+50+50+50 = 200, and 2200 + 200 = 2400, confirming the same result. Final answer: the class as written above correctly inherits from Employee, adds and initialises the teamSize field via constructor chaining, and overrides calculatePay to return basicPay plus £50 per team member.

Marking scheme

1 mark: correct class header showing inheritance from Employee, with the additional private field teamSize correctly typed (int). 1 mark: correct constructor header (three parameters: name, basicPay, teamSize). 2 marks: correct call to the base/super constructor passing name and basicPay (1 mark for correct syntax, 1 mark for correct arguments). 1 mark: correct assignment of the teamSize field. 1 mark: correct override method header (override/@Override, correct return type double, no parameters). 1 mark: correct calculation and return (basicPay + (teamSize x 50)). Max 7.
Question 17 · Inheritance Hierarchy, Virtual/Override & Polymorphic Processing
7 marks
The following array holds a mixture of Employee, SalesEmployee and ManagerEmployee objects:
Employee[] staff = new Employee[3];
staff[0] = new Employee("Aine", 1800);
staff[1] = new SalesEmployee("Ben", 1600, 450);
staff[2] = new ManagerEmployee("Cara", 2200, 4);

Write a method named getTotalPayroll that takes the array staff as a parameter and returns the total pay (a double) for all employees in the array, calling calculatePay on each element. [7]
Show answer & marking scheme

Worked solution

The method declares total, initialised to 0, and loops through every element of the staff array (from index 0 up to, but not including, staff.Length/staff.length). On each pass, it calls staff[i].CalculatePay() (or calculatePay() in Java) and adds the result to total. Although staff is declared as an array of the base type Employee[], and the code always writes exactly the same call, staff[i].CalculatePay(), this call demonstrates polymorphism: at run-time, each object 'knows' its own actual type, so calling CalculatePay() on the Employee object at staff[0] runs Employee's own version (returning just basicPay), while calling it on the SalesEmployee object at staff[1] runs SalesEmployee's overridden version (returning basicPay + commission), and calling it on the ManagerEmployee object at staff[2] runs ManagerEmployee's overridden version (returning basicPay + teamSize x 50) — the correct, overridden version is automatically selected for each object, without the loop needing to know or check each element's specific type. Verifying with the example data given: staff[0] (Employee, basicPay 1800) contributes 1800; staff[1] (SalesEmployee, basicPay 1600, commission 450) contributes 1600 + 450 = 2050; staff[2] (ManagerEmployee, basicPay 2200, teamSize 4) contributes 2200 + (4 x 50) = 2400. Total = 1800 + 2050 + 2400 = 6250; checking by a second route, summing in a different order, (1800 + 2400) + 2050 = 4200 + 2050 = 6250, confirming the same result, so calling getTotalPayroll(staff) on this example data would correctly return 6250. Final answer: the method as written above correctly sums the polymorphically calculated pay of every employee in the array, returning 6250 for the example data given.

Marking scheme

1 mark: correct method header (name, parameter staff of type Employee[], return type double). 1 mark: total variable correctly declared and initialised to 0. 1 mark: correct loop bounds iterating over every element of staff. 2 marks: correct polymorphic call to CalculatePay()/calculatePay() on each array element (1 mark for correct syntax, 1 mark for correctly relying on the object's own run-time type to select the correct overridden version, rather than checking each element's type explicitly). 1 mark: correct accumulation of the result into total. 1 mark: correct return of total. Max 7.
Question 18 · Inheritance Hierarchy, Virtual/Override & Polymorphic Processing
6 marks
Explain what is meant by 'polymorphism' in object-oriented programming, and explain how the getTotalPayroll method in the previous question demonstrates polymorphism. [6]
Show answer & marking scheme

Worked solution

Polymorphism ('many forms') is the object-oriented programming feature that allows objects of different classes, related through inheritance from a common superclass (or through implementing a shared interface), to be handled in a uniform way by code written in terms of the superclass/interface type, while each object still behaves according to its own specific, overridden implementation of a shared method; the correct method version to run is determined automatically at run-time, based on the actual type of the object, not the declared type of the variable/array element referring to it. Applying this to the getTotalPayroll method: the parameter staff is declared with type Employee[], meaning, from the compiler's point of view, it is simply an array of Employee references; however, the actual objects stored in the array, as shown in the example, are a mixture of a plain Employee, a SalesEmployee, and a ManagerEmployee. The loop in getTotalPayroll always executes the identical line of code, staff[i].calculatePay(), for every element, regardless of its actual type; yet because calculatePay is a virtual/overridable method that each subclass overrides with its own specific calculation, the program correctly runs Employee's version for staff[0], SalesEmployee's version for staff[1], and ManagerEmployee's version for staff[2], each producing the correct result for that particular kind of employee. This is polymorphism in action: it allows getTotalPayroll to correctly process a mixed collection of related objects through a single, simple, uniform loop, without needing separate code (e.g. type-checking and casting) to handle each specific subclass differently. Final answer: polymorphism allows objects of different, related subclasses to be processed uniformly (e.g. in one array/loop) via a shared method call, with the correct overridden version automatically run for each object at run-time; getTotalPayroll demonstrates this because its single, identical call to calculatePay() correctly triggers Employee's, SalesEmployee's or ManagerEmployee's own version, depending on each array element's actual type.

Marking scheme

2 marks: correct general explanation of polymorphism (objects of different, related subclasses treated uniformly via a shared superclass/reference type or interface, with the correct overridden method version invoked for each, determined at run-time). 2 marks: correct identification that staff/staff[] is declared as the base type Employee[] but actually holds a mixture of Employee, SalesEmployee and ManagerEmployee objects. 2 marks: correct explanation that the identical call staff[i].calculatePay() correctly invokes each object's own (overridden) version at run-time, based on its actual type, without the method needing to check or know each element's specific type. Max 6.

Ready to test yourself?

Turn these notes into exam-style practice. Get unlimited AI questions on this topic with instant marking and explanations.

Practise This Topic

Wondering how well you actually know this?

thinka is an AI practice app for DSE students: unlimited questions, instant auto-marking, and detailed step-by-step solutions. 100,000+ students use it to confirm they actually know it, not just think they do.

Want more questions like this? Practise unlimited on thinka, instant answers included.

Start Practising Free