Lab: Overriding#
Objective#
In this lab, we will delve into the concept of overriding methods in object oriented programming. We will explore how to effectively use inheritance to modify the behavior of methods in derived classes. The focus will be on understanding the mechanics and implications of overriding methods in a class hierarchy.
Provided Code#
Carefully review the provided code. Notice that we have a base class named Sequence
. This class includes a parameterless constructor and two methods: int Next()
and int[] Take(int n)
. The Next
method is virtual, allowing it to be overridden in derived classes, while Take
is not.
This design allows Next
to be customized in subclasses, while Take
maintains consistent behavior across all sequences.
class Sequence
{
int n = 0;
public Sequence () { }
public Sequence (int n)
=> this.n = n;
public virtual int Next()
=> n++;
public int[] Take(int n)
{
int[] result = new int[n];
for (int i = 0; i < n; i++)
result[i] = Next();
return result;
}
}
Sequence seq = new Sequence();
foreach(int num in seq.Take(3))
Console.WriteLine(num);
0
1
2
Sequence seq = new Sequence(50);
foreach(int num in seq.Take(3))
Console.WriteLine(num);
50
51
52
Instructions#
Our goal is to create subclasses that inherit from Sequence
and override the Next
method to exhibit different behaviors.
Step 1: Even numbers#
Let’s begin by creating a subclass called EvenNumbers
.
It should override the method Next
and only return even numbers.
When you’re done you should be able to run the following code:
Sequence even1 = new EvenNumbers();
foreach(int num in even1.Take(3))
Console.WriteLine(num);
2
4
6
Sequence even2 = new EvenNumbers(1000);
foreach(int num in even2.Take(3))
Console.WriteLine(num);
1002
1004
1006
🤔 Reflection
How does overriding the Next
method in the subclass alter the behavior of the Take
method?
Step 2: Triangular numbers#
Let’s now create a subclass called TriangularNumbers
that inherits from Sequence
.
Override the method Next
and implement it so that it always return the next number in the sequence of triangular numbers.
When you’re done you should be able to run the following code:
Sequence triangular = new TriangularSequence();
Console.WriteLine(String.Join(", ", triangular.Take(12)));
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78
Step 3: Fibonacci numbers#
Let’s now create a subclass called FibonacciSequence
that inherits from Sequence
.
Override the method Next
and implement it so that it always return the next number in the Fibonacci sequence.
When you’re done you should be able to run the following code:
Sequence fibonacci = new FibonacciSequence();
Console.WriteLine(String.Join(", ", fibonacci.Take(12)));
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233
Step 4: Contrast to hiding#
Reflect over the differences between hiding and overriding by replacing the keyword override
with the keyword new
in all subclasses.
What does this change? How does this change the behavior of the code?
Challenge#
Let’s explore a more advanced application of our Sequence
hierarchy that uses both inheritance, subtyping, and object composition at the same time.
Create a new class called SequenceSum
. This class should have a constructor that takes a Sequence
as an argument. The idea is that each time we ask the SequenceSum
for the next number, it should ask the underlying sequence for the next number, add that to the currently running sum (starting from 0) and return the new sum.
When you’re done you should be able to run the following code:
Sequence naturals = new Sequence();
Sequence summedNaturals = new SequenceSum(naturals);
Console.WriteLine(String.Join(", ", summedNaturals.Take(6)));
0, 1, 3, 6, 10, 15
Sequence summedNaturals = new SequenceSum(new TriangularSequence());
for (int i=1; i<=5; i++)
{
Sequence naturals = new TriangularSequence();
string summands = String.Join(" + ", naturals.Take(i));
int sum = summedNaturals.Next();
Console.WriteLine($"{summands} = {sum}");
}
1 = 1
1 + 3 = 4
1 + 3 + 6 = 10
1 + 3 + 6 + 10 = 20
1 + 3 + 6 + 10 + 15 = 35