Reputation: 13018
I need to filter and order data entries after criteria that a user selects through a dropdown. Selectable will be things like "newest entries first", "oldest entries first", "lowest price first" etc.
I could just create an enum for the options and switch/case where I retrieve the data, but I'd rather do this in an easily extendable way.
What design pattern would fit the situation best?
Upvotes: 6
Views: 1964
Reputation: 12938
Everyone has mentioned the strategy pattern. Just thought I'd post my simple implementation. No need to make it more complicated than necessary.
public enum SortMethod
{
Newest,
Oldest,
LowestPrice,
}
public class Foo
{
public DateTime Date {get;set;}
public decimal Price {get;set;}
}
...
var strategyMap = new Dictionary<SortMethod, Func<IEnumerable<Foo>, IEnumerable<Foo>>>
{
{ SortMethod.Newest, x => x.OrderBy(y => y.Date) },
{ SortMethod.Oldest, x => x.OrderByDescending(y => y.Date) },
{ SortMethod.LowestPrice, x => x.OrderBy(y => y.Price) }
};
...
var unsorted = new List<Foo>
{
new Foo { Date = new DateTime(2012, 1, 3), Price = 10m },
new Foo { Date = new DateTime(2012, 1, 1), Price = 30m },
new Foo { Date = new DateTime(2012, 1, 2), Price = 20m }
};
var sorted = strategyMap[SortMethod.LowestPrice](unsorted);
Upvotes: 5
Reputation: 65446
As mentioned in the comments, this sounds like a job for the Strategy pattern. You'll recognise it as this already features heavily in the .NET framework.
Here's an example using IComparer, or using the LINQ extension methods in 3.5 which I prefer. You'll still need to add a factory-style method to the base class that works out which comparer it should use, or you could store this as part of the data in the drop down list.
static void Main(string[] args)
{
List<User> users = new List<User>();
users.Add(new User() { Name = "Larry", Age = 35 });
users.Add(new User() { Name = "Bob", Age = 25 });
users.Add(new User() { Name = "Brian", Age = 30 });
NameComparer sorter = new NameComparer();
IEnumerable<User> sortedUsers = sorter.Sort(users);
NameComparer35 sorter35 = new NameComparer35();
IEnumerable<User> sortedUsers35 = sorter35.Sort(users);
}
public abstract class MyComparer<T> : IComparer<T> where T: User
{
public abstract int Compare(T x, T y);
public IEnumerable<T> Sort(IEnumerable<T> items)
{
items.ToList().Sort(this);
return items;
}
}
public abstract class MyComparer35<T> where T : User
{
public abstract IEnumerable<T> Sort(IEnumerable<T> items);
}
public class NameComparer35 : MyComparer35<User>
{
public override IEnumerable<User> Sort(IEnumerable<User> items)
{
return items.OrderBy(u => u.Name);
}
}
public class NameComparer : MyComparer<User>
{
public override int Compare(User x, User y)
{
return x.Name.CompareTo(y.Name);
}
}
public class AgeComparer : MyComparer<User>
{
public override int Compare(User x, User y)
{
return x.Age.CompareTo(y.Age);
}
}
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return string.Format("{0} {1}", Name, Age);
}
}
Upvotes: 0
Reputation: 6017
I am not always good at naming the right pattern to fit my thought, but my initial thought is why not make a simple class for each option and implement IComparer(T) and then load those items as your dropdown options. You would likely only need a name property in addition to the interface method.
public class NameSorter: IComparer<WhateverObj>
{
public String DisplayName;
public int Compare(WhateverObj x, WhateverObj y)
{
}
}
Upvotes: 0