In business applications, while creating searching
algorithms you need to create a search context which consists of a combination
of multiple algebraic expressions. A simple example is given below where the
filter for searching books is created by combining multiple expressions as in the code sample below.
[TestMethod]
public void
GetAllBooksInRangeByTopicShouldReturnAllBooksInThePriceRangeFilteredByTopic()
{
var amazonService = new
BookStore();
const decimal
fromRange = 50;
const decimal toRange
= 100;
const string genre = "Travel Guide";
var books =
amazonService.GetBooksByPriceRangeAndGenre(fromRange, toRange, genre);
Assert.IsTrue(books.Any());
}
public IEnumerable<Book> GetBooksByPriceRangeAndGenre(decimal from, decimal
to, string genre)
{
return GetAll().Where(x =>
x.Genre.Name.ToLower().Contains(genre.Trim().ToLower())
&&
(x.Price >= from && x.Price <= to));
}
We can refactor these expressions to a more readable and maintainable
structure by creating specifications using the interpreter pattern as given
below.
public IEnumerable<Book> GetBooksByPriceRangeAndGenre(decimal from, decimal
to, string genre)
{
var bookSpec = GetBookSpec(genre, from, to);
return GetAll().Where(bookSpec.SatisfiedBy());
}
private BookSpecification
GetBookSpec(string genre, decimal from, decimal
to)
{
return new BookSpecification(from,
to, genre);
}
public class BookSpecification : ISpecification<Book>
{
private readonly decimal _from;
private readonly decimal _to;
private readonly string _genre;
public BookSpecification(decimal
from, decimal to, string
genre)
{
_from
= from;
_to =
to;
_genre = genre;
}
public Expression<Func<Book, bool>> SatisfiedBy()
{
return new GenreSpecification(_genre).AND(new PriceRangeSpecification(_from,
_to)).SatisfiedBy();
}
}
public class GenreSpecification : ISpecification<Book>
{
private readonly string _genre;
public GenreSpecification(string
genre)
{
_genre = genre.Trim().ToLower();
}
public Expression<Func<Book, bool>> SatisfiedBy()
{
return new Specification<Book>(x
=> x.Genre.Name.ToLower().Contains(_genre)).SatisfiedBy();
}
}
public class PriceRangeSpecification : ISpecification<Book>
{
private readonly decimal _from;
private readonly decimal _to;
public PriceRangeSpecification(decimal from, decimal
to)
{
_from
= from;
_to =
to;
}
public Expression<Func<Book, bool>> SatisfiedBy()
{
return new Specification<Book>(x
=> x.Price >= _from && x.Price <= _to).SatisfiedBy();
}
}
No comments:
Post a Comment