patterncsharpMinor
OdeToCode Refactoring Kata
Viewed 0 times
refactoringkataodetocode
Problem
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
Pair.cs
Finder.cs
The test class basically remained the
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 > 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 _person;
public Finder(List person)
{
_person = person;
}
public Pair FindClosestAgeInterval()
{
return Find(pairs => pairs.OrderBy(p => p.AgeDifference).FirstOrDefault());
}
public Pair FindFurthestAgeInterval()
{
return Find(pairs => pairs.OrderByDescending(p => p.AgeDifference).FirstOrDefault());
}
public Pair Find(Func, Pair> pairSelectionLambda)
{
var availableDistinctPairs = GetDistinctPairs();
return pairSelectionLambda(availableDistinctPairs) ?? new Pair();
}
private IEnumerable GetDistinctPairs()
{
for (var i = 0; i < _person.Count - 1; i++)
for (var j = i + 1; j < _person.Count; j++)
yield return new Pair(_person[i], _person[j]);
}
}The test class basically remained the
Solution
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
and then implemented
I did similarly with the class you called
and
Finally, I update the
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 people;
private Finder(IEnumerable people)
{
this.people = people.ToList();
}
public static IFinder Create(IEnumerable 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 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 PopulateListInOrder()
{
IList peopleInOrder = new List();
for (var i = 0; i < this.people.Count - 1; i++)
{
for (var j = i + 1; j < this.people.Count; j++)
{
var isYounger = this.people[i].BirthDate < 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;
}
}Code Snippets
public interface IPerson
{
string Name { get; }
DateTime BirthDate { get; }
}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);
}
}public interface IPersonBirthdayDifference
{
IPerson YoungerPerson { get; }
IPerson ElderPerson { get; }
TimeSpan AgeDifference { get; }
}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);
}
}public sealed class Finder : IFinder
{
private readonly IList<IPerson> people;
private Finder(IEnumerable<IPerson> people)
{
this.people = people.ToList();
}
public static IFinder Create(IEnumerable<IPerson> 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<IPersonBirthdayDifference> 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<IPersonBirthdayDifference> PopulateListInOrder()
{
IList<IPersonBirthdayDifference> peopleInOrder = new List<IPersonBirthdayDifference>();
for (var i = 0; i < this.people.Count - 1; i++)
{
for (var j = i + 1; j < this.people.Count; j++)
{
var isYounger = this.people[i].BirthDate < 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;
}
}Context
StackExchange Code Review Q#10726, answer score: 2
Revisions (0)
No revisions yet.