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