OdeToCode Refactoring Kata I saw this article that proposes a refactoring exercise and thought I'd give it a try.

Spoiler alert: I'm going to show my solution to the Kata below. You might want to attempt the Kata yourself before you look at this code, so this will not influence you in any way.

I ended up with the following three classes:

Person.cs

public class Person
{
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }

    public bool IsOlderThan(Person person2)
    {
        return BirthDate &gt; person2.BirthDate;
    }
}


Pair.cs

public class Pair
{
    public Person Person1 { get; set; }
    public Person Person2 { get; set; }
    public TimeSpan AgeDifference { get { return Person2.BirthDate - Person1.BirthDate; } }

    public Pair(Person person1, Person person2)
    {
        if (person1.IsOlderThan(person2))
        {
            Person1 = person2;
            Person2 = person1;
        }
        else
        {
            Person1 = person1;
            Person2 = person2;
        }
    }

    public Pair()
    {
        Person1 = Person2 = null;
    }
}


Finder.cs

public class Finder
{
    private readonly List&lt;Person&gt; _person;

    public Finder(List&lt;Person&gt; person)
    {
        _person = person;
    }

    public Pair FindClosestAgeInterval()
    {
        return Find(pairs =&gt; pairs.OrderBy(p =&gt; p.AgeDifference).FirstOrDefault());
    }

    public Pair FindFurthestAgeInterval()
    {
        return Find(pairs =&gt; pairs.OrderByDescending(p =&gt; p.AgeDifference).FirstOrDefault());
    }

    public Pair Find(Func&lt;IEnumerable&lt;Pair&gt;, Pair&gt; pairSelectionLambda)
    {
        var availableDistinctPairs = GetDistinctPairs();

        return pairSelectionLambda(availableDistinctPairs) ?? new Pair();
    }

    private IEnumerable&lt;Pair&gt; GetDistinctPairs()
    {
        for (var i = 0; i &lt; _person.Count - 1; i++)
            for (var j = i + 1; j &lt; _person.Count; j++)
                yield return new Pair(_person[i], _person[j]);
    }
}


The test class basically remained the same (except that I have updated it with the new class/method names).

Overall I think this code is OK (it passes the tests and I could easily understand it 6 months from now).

However, I'm interested to hear your critic opinions, to find out how it can get better.
My $0.02. Your solution is quite good as it stands. Just putting my thing down too.

I'm a fan of interface-based development with factory methods, so I extracted out an interface for the Person class called IPerson:

public interface IPerson
{
    string Name { get; }

    DateTime BirthDate { get; }
}


and then implemented Person as a sealed immutable class (using readonly and a parametrized private constructor) which has a factory method:

public sealed class Person : IPerson
{
    private readonly string name;

    private readonly DateTime birthDate;

    private Person(string name, DateTime birthDate)
    {
        this.name = name;
        this.birthDate = birthDate;
    }

    public string Name { get { return this.name; } }

    public DateTime BirthDate { get { return this.birthDate; } }

    public static IPerson Create(string name, DateTime birthDate)
    {
        return new Person(name, birthDate);
    }
}


I did similarly with the class you called Pair, but I called it PersonBirthdayDifference (I'll freely admit I can't think up a useful name at all). Also note the static Empty property which I put to use in the Finder algorithm rather than just newing up one with no properties set.

public interface IPersonBirthdayDifference
{
    IPerson YoungerPerson { get; }

    IPerson ElderPerson { get; }

    TimeSpan AgeDifference { get; }
}


and

public sealed class PersonBirthdayDifference : IPersonBirthdayDifference
{
    private static readonly IPersonBirthdayDifference empty = Create(null, null);

    private readonly IPerson youngerPerson;

    private readonly IPerson elderPerson;

    private readonly TimeSpan ageDifference;

    private PersonBirthdayDifference(IPerson youngerPerson, IPerson elderPerson)
    {
        this.youngerPerson = youngerPerson;
        this.elderPerson = elderPerson;
        this.ageDifference = (youngerPerson == null) || (elderPerson == null)
            ? default(TimeSpan)
            : elderPerson.BirthDate - youngerPerson.BirthDate;
    }

    public static IPersonBirthdayDifference Empty { get { return empty; } }

    public IPerson YoungerPerson { get { return this.youngerPerson; } }

    public IPerson ElderPerson { get { return this.elderPerson; } }

    public TimeSpan AgeDifference { get { return this.ageDifference; } }

    public static IPersonBirthdayDifference Create(IPerson p1, IPerson p2)
    {
        return new PersonBirthdayDifference(p1, p2);
    }
}


Finally, I update the Finder class by some of the same methods as above, plus introduce a couple more helper classes called GreaterThanComparer and LessThanComparer (not posting that code, it's rather trivial) to keep the switch from executing every iteration of the loop. There's also a healthy dose of LINQ to help declare intent where I can:

public sealed class Finder : IFinder
{
    private readonly IList&lt;IPerson&gt; people;

    private Finder(IEnumerable&lt;IPerson&gt; people)
    {
        this.people = people.ToList();
    }

    public static IFinder Create(IEnumerable&lt;IPerson&gt; people)
    {
        return new Finder(people);
    }

    public IPersonBirthdayDifference Find(FinderType finderType)
    {
        var peopleInOrder = this.PopulateListInOrder();

        return peopleInOrder.Any() ? GetAnswer(peopleInOrder, finderType) : PersonBirthdayDifference.Empty;
    }

    private static IPersonBirthdayDifference GetAnswer(
        IEnumerable&lt;IPersonBirthdayDifference&gt; peopleInOrder,
        FinderType finderType)
    {
        IPersonDifferenceComparer comparer;

        switch (finderType)
        {
            case FinderType.LessThan:
                comparer = LessThanComparer.Create();
                break;
            case FinderType.GreaterThan:
                comparer = GreaterThanComparer.Create();
                break;
            default:
                return PersonBirthdayDifference.Empty;
        }

        var answer = peopleInOrder.First();

        foreach (var person in peopleInOrder)
        {
            if (comparer.Compare(person.AgeDifference, answer.AgeDifference))
            {
                answer = person;
            }
        }

        return answer;
    }

    private IEnumerable&lt;IPersonBirthdayDifference&gt; PopulateListInOrder()
    {
        IList&lt;IPersonBirthdayDifference&gt; peopleInOrder = new List&lt;IPersonBirthdayDifference&gt;();

        for (var i = 0; i &lt; this.people.Count - 1; i++)
        {
            for (var j = i + 1; j &lt; this.people.Count; j++)
            {
                var isYounger = this.people[i].BirthDate &lt; this.people[j].BirthDate;
                var youngerPerson = isYounger ? this.people[i] : this.people[j];
                var elderPerson = isYounger ? this.people[j] : this.people[i];

                peopleInOrder.Add(PersonBirthdayDifference.Create(youngerPerson, elderPerson));
            }
        }

        return peopleInOrder;
    }
}