Flexible C# with OOP Principles: Moving from Static Functions to Flexible Object-Oriented
mohamed Tayel
Posted on October 30, 2024
Meta Description: Learn how to refactor C# code step-by-step, transitioning from static functions to a flexible, object-oriented design. Discover practical examples that showcase the benefits of applying OOP principles for better code flexibility, maintainability, and extensibility.
Introduction
This article aims to guide you in transforming C# code from a simple, rigid function to a more flexible, object-oriented approach. Each step builds upon the previous one, making the code easier to modify and extend over time.
Step 1: The Basic Example – Summing All Numbers
We’ll start with a simple function that sums up all the numbers in an array.
Example 1: Summing All Numbers
int Sum(int[] numbers)
{
int total = 0;
foreach (var number in numbers)
{
total += number;
}
return total;
}
Step-by-Step Explanation:
-
int Sum(int[] numbers)
: This is a function that takes an array of integers (numbers
) as input. -
int total = 0;
: We initialize a variable (total
) to hold the sum of the numbers. -
foreach (var number in numbers)
: We loop through each number in the array. -
total += number;
: For each number, we add it tototal
. -
return total;
: Finally, we return the total sum.
Problem: This function sums all numbers. If you want to sum only odd or even numbers, you’d have to modify it repeatedly, making it inflexible.
Step 2: Adding a Condition – Summing Odd Numbers
Let’s add a condition to sum only odd numbers.
Example 2: Summing Odd Numbers
int SumOddNumbers(int[] numbers)
{
int total = 0;
foreach (var number in numbers)
{
if (number % 2 != 0)
{
total += number;
}
}
return total;
}
Step-by-Step Explanation:
-
int SumOddNumbers(int[] numbers)
: This function is similar to the previous one but focuses on summing odd numbers. -
if (number % 2 != 0)
: We check if a number is odd (remainder when divided by 2 is not 0). - If it’s odd, we add it to
total
. -
return total;
: We return the sum of only the odd numbers.
Problem: This solution is slightly better but still lacks flexibility. You’d have to create a new function for each condition (e.g., for even numbers).
Step 3: Making It Flexible with a Delegate
To make the code more flexible, we’ll use a delegate to determine which numbers to sum.
Example 3: Using a Delegate
int Sum(int[] numbers, Func<int, bool> selector)
{
int total = 0;
foreach (var number in numbers)
{
if (selector(number))
{
total += number;
}
}
return total;
}
Step-by-Step Explanation:
-
Func<int, bool> selector
: This is a delegate that takes an integer and returns a boolean. It acts as a filter for the numbers to sum. -
if (selector(number))
: We callselector
for each number. If it returnstrue
, the number is added tototal
. - Example Usage:
int[] numbers = { 1, 2, 3, 4, 5 };
int sumOfOdds = Sum(numbers, n => n % 2 != 0); // Sums odd numbers
int sumOfEvens = Sum(numbers, n => n % 2 == 0); // Sums even numbers
-
Explanation of Usage:
-
n => n % 2 != 0
is a lambda expression that checks ifn
is odd. -
n => n % 2 == 0
is another lambda expression that checks ifn
is even.
-
Now, you can sum based on any condition without changing the Sum
function.
Step 4: Refactoring to an Object-Oriented Approach
To fully embrace object-oriented programming (OOP), let’s separate the selection logic into classes.
Example 4: Creating a Selector
Class
public class Selector
{
private readonly Func<int, bool> _criteria;
public Selector(Func<int, bool> criteria)
{
_criteria = criteria;
}
public bool IsSelected(int number)
{
return _criteria(number);
}
}
Step-by-Step Explanation:
-
Selector
Class: This class encapsulates the selection logic. -
Func<int, bool> _criteria
: A private field that stores the selection logic. -
IsSelected(int number)
: This method checks if a number meets the criteria.
Example 5: Refactoring to a NumberArray
Class
public class NumberArray
{
private readonly int[] _numbers;
public NumberArray(int[] numbers)
{
_numbers = numbers;
}
public int Sum(Selector selector)
{
int total = 0;
foreach (var number in _numbers)
{
if (selector.IsSelected(number))
{
total += number;
}
}
return total;
}
}
Step-by-Step Explanation:
-
NumberArray
Class: This class encapsulates the array of numbers and the summing logic. -
Sum(Selector selector)
: Uses theSelector
class to filter numbers before summing them. - Example Usage:
var numbers = new NumberArray(new[] { 1, 2, 3, 4, 5 });
var oddSelector = new Selector(n => n % 2 != 0);
var sumOfOdds = numbers.Sum(oddSelector); // Output: 9
var evenSelector = new Selector(n => n % 2 == 0);
var sumOfEvens = numbers.Sum(evenSelector); // Output: 6
-
Explanation of Usage:
- We create a
NumberArray
with an array of numbers. - We create two
Selector
objects: one for odd numbers and one for even numbers. - The
Sum
method uses theSelector
to determine which numbers to add.
- We create a
Step 5: Making It Extensible
With this OOP structure, adding new criteria becomes easy.
Example 6: Adding a New Selector
var everyOtherSelector = new Selector((n, index) => index % 2 == 0);
var sumOfEveryOther = numbers.Sum(everyOtherSelector); // Output: 6
Step-by-Step Explanation:
- We create a new
Selector
that sums every other number. - The
Sum
method uses this selector to filter the numbers. - You can add different selectors without changing the existing classes.
Conclusion
By refactoring code step-by-step, we moved from a simple summing function to a flexible, object-oriented solution. This approach makes the code more adaptable, maintainable, and easier to extend as requirements change.
Each step was explained in detail to help you understand the benefits of transitioning to an object-oriented design.
Posted on October 30, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.