Multi Search Engine Enhancement Using Design Patterns

Before reading it might be worth having a look at my previous post on this mini project, the current structure looks like this:

Current Class Structure

As you can see it is fairly straight forward to plugin your own SearchEngine implementation, either by taking advantage of the AbstractSearchEngine or by implementing the ISearchEngine interface.

In both cases the client wanting to plugin their custom searchengine implementation only needs to concentrate on writing code to extract search results from the searchengine they are querying.

One of the problems at the moment is that the searches are performed synchronously which could potentially make the UI un-responsive if the query takes a while, this is a big no-no, so ideally I would like to make the calls asynchronously and then the UI (or any other listening object) can be notified when results are returned.

We could start by making some changes to the structure:

Updated Structure V1

Now we have introduced a new method SearchAsync and also a new event SearchCompleted we have also introduced the same to the AbstractSearchEngine class, there are some issues with these changes:

  1. This could be a first sign of polluting an interface unrelated responsibility – Any client who wants to plug in their own searchengine cannot just concentrate on implementing code to bring back results from a searchengine, they must now implement a asynchronous searching strategy.
  2. Using this approach as stated above we have made it the clients responsibility to add the asynchronous code this could bring about some issues such as the client just not bothering and instead just making a synchronous call to the searchengine or badly implemented asynchronous code that may use threading badly.
  3. There may already be quite a few implemented searchengines that rely on this interface by making this change we are breaking there compatibility with ISearchEngine for a change that may not be affecting them at all

We need to think of another way… We already have a nicely defined ISearchEngine interface which we know we can call into and it will bring back results and allow us to navigate those results, the only thing we are adding is the extra behaviour to perform the calls asynchronously ideally we would like to add this behaviour without effecting what we have already! Enter Decorator stage right :)

The intent of the decorator is to add behaviour dynamically to existing objects which is what were after, So lets come up with an alternative structure:

Updated Structure V2

We now have a brand new class irrelevant fields have been omitted which takes the ISearchEngine in its constructor in most cases this new class will delegate calls straight through to the decorated class however if a search is in progress it needs to take other actions. Using TDD I came up with the class below:

public class AsyncSearchEngineDecorator : ISearchEngine
{
 private delegate void PagingDelegate();
 private delegate void SearchingDelegate(string SearchString);

	private ISearchEngine decoratedSearchEngine;
 private volatile bool resultsReturned = false;
 private readonly object locker = new object();

	public event EventHandler SearchComplete;

	private void SetResultsReturnedTo(bool value)
 {
 	lock (locker)
 	{
 		resultsReturned = value;
 	}
 }

	private bool ResultsReturnedValue()
 {
 	lock (locker)
 	{
 		return resultsReturned;
 	}
 }

	public AsyncSearchEngineDecorator(ISearchEngine searchEngineToDecorate)
 {
 	if (searchEngineToDecorate == null)
 		throw new ArgumentNullException("searchEngineToDecorate", "searchEngineToDecorate cannot be null");

		this.decoratedSearchEngine = searchEngineToDecorate;
 }

	public void Search(string searchText)
 {
 	SetResultsReturnedTo(false);

		PerformAsync(new SearchingDelegate(delegate(string text)
 	{
 		decoratedSearchEngine.Search(text);
 	}), new object[] {searchText});
 }

	private void RaiseSearchComplete()
 {
 	if (SearchComplete != null)
 		SearchComplete(this, EventArgs.Empty);
 }

	public string Name
 {
 	get { return decoratedSearchEngine.Name; }
 }

	public bool HasResults
 {
 	get {
 		if (ResultsReturnedValue())
 			return decoratedSearchEngine.HasResults;
 		else
 			return false;
 	}
 }

	public IList Results
 {
 	get {
 		if (ResultsReturnedValue())
 			return decoratedSearchEngine.Results;
 		else
 			return new List();
 	}
 }

	public void NextPage()
 {
 	if (ResultsReturnedValue())
 	{
 		PerformAsync(new PagingDelegate(delegate
 		{
 			decoratedSearchEngine.NextPage();
 		}));
 	}
 	else
 		RaisePagingWhileSearchInProgressException("NextPage");
 }

	public void PreviousPage()
 {
 	if (ResultsReturnedValue())
 	{
 		PerformAsync(new PagingDelegate(delegate
 		{
 			decoratedSearchEngine.PreviousPage();
 		}));
 	}
 	else
 		RaisePagingWhileSearchInProgressException("PreviousPage");
 }

	private static void RaisePagingWhileSearchInProgressException(string method)
 {
 	throw new InvalidOperationException(string.Format("{0} cannot be called while search is in progress", method));
 }

	public int PageSize
 {
 	get
 	{
 		return decoratedSearchEngine.PageSize;
 	}
 	set
 	{
 		decoratedSearchEngine.PageSize = value;
 	}
 }

	public int CurrentPage
 {
 	get {
 		if (ResultsReturnedValue())
 			return decoratedSearchEngine.CurrentPage;
 		else
 			return -1;
 	}
 }

	private void PerformAsync(Delegate action)
 {
 	PerformAsync(action, null);
 }

	private void PerformAsync(Delegate action, object[] args)
 {
 	SetResultsReturnedTo(false);

		ThreadPool.QueueUserWorkItem(delegate
 	{
 		action.DynamicInvoke(args);
 		SetResultsReturnedTo(true);
 		RaiseSearchComplete();
 	});
 }
}

If there is any issues with the threading code please give me heads up :) Now in my UI I can use the new class as so:

searchEngine = new AsyncSearchEngineDecorator(searchEngine);
searchEngine.SearchComplete += new EventHandler(HandleSearchPerformed);

You may thinking at this point… “hang on you class diagram looks nothing like the class diagram from the GoF book” …and you would be right, one mistake that some people can make when learning about design patterns is that the structure of your implementation must match the structure shown, this is not the case there are many ways to implement a design pattern I too got hung up on using the class diagram as a template for how design patterns should be created, however I read a chapter from Refactoring to Patterns that brought it into perspective that if you need to introduce a design pattern then implement it in the best way for your surroundings not the best way according to a class diagram from GoF or some design patterns website.

So what have we ended up with, well we have created a new class which has the responsibility of making calls against an ISearchEngine aysnchronously by doing this we have kept both the ISearchEngine and AbstractSearchEngine responsibilities to be just concerned with pulling results back from a custom searchengine. The changes to the UI have been minimal it simply wraps the existing searchEngine into the new AsyncSearchEngineDecorator object and handles the SearchComplete event.

About these ads

One thought on “Multi Search Engine Enhancement Using Design Patterns

  1. Hi, Neat post. There is an issue with your site in internet explorer, would test this?

    IE still is the marketplace chief and a big
    component of folks will miss your magnificent writing due to
    this problem.

Comments are closed.