One should "depend upon abstractions, [not] concretions.
The most basic example is the following, imagine the following implementation, while LINQ
does not exist:
/**
* Filter out all 0 items
*/
public IEnumerable<int> Filter(IEnumerable<int> array)
{
var result = new List<int>();
foreach(var item in array)
{
if (item != 0)
{
result.Add(item);
}
}
return result;
}
Next the customer requires an additional requirement, it should also be possible to filter out items less than zero.
public class FilterOptions
{
public boolean FilterZero { get; set; } = true;
public boolean FilterLessThanZero { get; set; } = false;
}
public IEnumerable<int> Filter(IEnumerable<int> array, FilterOptions options)
{
var result = new List<int>();
foreach(var item in array)
{
if (
(item != 0 && options.FilterZero) || (item < 0 && options.FilterLessThanZero)
)
{
result.Add(item);
}
}
return result;
}
As you may noticed the implementation is getting more and more complicated based on the new requirements. To simplify this, one can just invert the control of filtering to higher order functions:
public IEnumerable<int> FilterWithOptions(IEnumerable<int> array, FilterOptions options)
{
var filtersToApply = new List<Func<IEnumerable<int>, IEnumerable<int>>>();
if (options.FilterGreaterThanTen)
{
filtersToApply.Add(FilterGreaterThanTen);
}
if (options.FilterLessThanZero)
{
filtersToApply.Add(FilterLessThanZero);
}
if (options.FilterZero)
{
filtersToApply.Add(FilterZero);
}
return FilterPipe(filtersToApply);
}
public IEnumerable<int> FilterGreaterThanTen(IEnumerable<int> array)
{
return Filter(array, num => num > 10);
}
public IEnumerable<int> FilterLessThanZero(IEnumerable<int> array)
{
return Filter(array, num => num < 0);
}
public IEnumerable<int> FilterZero(IEnumerable<int> array)
{
return Filter(array, num => num == 0);
}
private IEnumerable<int> Filter(IEnumerable<int> array, Func<int, bool> predicate)
{
var result = new List<int>();
foreach (var item in array)
{
if (predicate(item))
{
result.Add(item);
}
}
return result;
}
private IEnumerable<int> FilterPipe(IEnumerable<Func<IEnumerable<int>, IEnumerable<int>>> functions)
{
return functions.Aggregate(new List<int>(), (ints, func) => func(ints).ToList());
}