Mar
12
2005

Refactoring the set of Predicate with an Interpreter

This evening I continued my journey with the C# Generics. I refactored what I described in my post "DataAccessLayer.FindAll(PublishedBy(Author)) " to be able to use the Design Pattern Interpreter.

The result in the ASPX code behind file is the following:

  1 AndSpec spec = new AndSpec(
  2                         new AndSpec(
  3                             new PublishedSpec(),
  4                             new BeforeDateSpec(DateTime.Parse("01/01/2005"))
  5                         ),
  6                         new OrSpec(
  7                             new AuthorSpec("Mathieu Kempé"),
  8                             new AuthorSpec("Laurent Kempé")
  9                         )
 10                     );
 11 
 12 DataAccessor<Article> articles = new DataAccessor<Article>("GetArticles");
 13 GridView3.DataSource = articles.FindAll(Matching(spec));
 14 GridView3.DataBind();
With the unique Matching Predicate, replacing all the other Predicate:
  1 protected Predicate<Article> Matching(Spec spec)
  2 {
  3 	return delegate(Article a)
  4 	{
  5 		return spec.isSatisfiedBy(a);
  6 	};
  7 }

To achieve this I first added two more properties to my Entity, Article. I did not added new constructors because those two properties are not mandatory:

  1 private DateTime datePublished;
  2 
  3 public DateTime DatePublished
  4 {
  5 	get { return datePublished; }
  6 	set { datePublished = value; }
  7 }
  8 
  9 private DateTime dateModified;
 10 
 11 public DateTime DateModified
 12 {
 13 	get { return dateModified; }
 14 	set { dateModified = value; }
 15 }
Then I modified my Data Access Layer to have it a bit more generic, starting with the interface :
  1 interface IDataAccess<T>
  2 {
  3 	List<T> GetAll();
  4 
  5 	List<T> FindAll(Predicate<T> match);
  6 }
  7 
I removed the method "T Get(Guid uuid)" and "T Get(string uuid)" because that can be easily expressed with a Predicate.
I made a Generic implementation of my Data Access through a DataAccessor class:
  1 namespace TechHeadBrothers.Portal.DAL
  2 {
  3 	public class DataAccessor<T> : IDataAccess<T> where T : new()
  4 	{
  5 		List<T> list = null;
  6 
  7 		public DataAccessor(string sp)
  8 		{
  9 			readFromDatabase(sp);
 10 		}
 11 
 12 		#region Protected Methods
 13 
 14 		protected void readFromDatabase(string sp)
 15 		{
 16 			// Create Instance of Connection and Command Object
 17 			SqlConnection myConnection =
 18 				new SqlConnection(ConfigurationManager.ConnectionStrings["TechHeadBrothers"].ConnectionString);
 19 
 20 			SqlDataAdapter myCommand = new SqlDataAdapter(sp, myConnection);
 21 
 22 			// Mark the Command as a SPROC
 23 			myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
 24 
 25 			// Create and Fill the DataSet
 26 			DataSet ds = new DataSet();
 27 			myCommand.Fill(ds);
 28 
 29 			myConnection.Close();
 30 
 31 			this.list = DataAdapterFactory.createAdapter<T>().Adapt(ds.Tables[0]);
 32 		}
 33 
 34 		#endregion
 35 
 36 		#region IDataAccess<T> Members
 37 
 38 		public List<T> GetAll()
 39 		{
 40 			return list;
 41 		}
 42 
 43 		public List<T> FindAll(Predicate<T> match)
 44 		{
 45 			return list.FindAll(match);
 46 		}
 47 
 48 		#endregion
 49 	}
 50 }
 
I had to implement an Adapter to convert the data from the Database representation to the Entity :
 
  1 abstract class DataAdapter<T> 
  2 {
  3 	public List<T> Adapt(DataTable table)
  4 	{
  5 		List<T> list = new List<T>(table.Rows.Count);
  6 
  7 		foreach (DataRow row in table.Rows)
  8 		{
  9 			list.Add(this.Adapt(row));
 10 		}
 11 
 12 		return list;
 13 	}
 14 
 15 	public abstract T Adapt(DataRow row);
 16 }
 
And a Factory :
 
  1 class DataAdapterFactory
  2 {
  3     public static DataAdapter<T> createAdapter<T>() where T : new()
  4     {
  5         string name = new T().GetType().Name.ToLower();
  6 
  7         if ( name == "article")
  8             return new ArticleAdapter() as DataAdapter<T>;
  9         else if ( name == "author")
 10             return new AuthorAdapter() as DataAdapter<T>;
 11 
 12         return null;
 13     }
 14 }
 
Here is the concrete implementation for the Article Entity Adapter:
 
  1 class ArticleAdapter : DataAdapter<Article>
  2 {
  3     public override Article Adapt(DataRow row)
  4     {
  5         Article article = new Article((string)row["Title"],
  6                                       (string)row["Description"],
  7                                       (string)row["Author"],
  8                                         (bool)row["isPublished"],
  9                                         (Guid)row["uuid"]);
 10 
 11         if (row["DatePublished"] != DBNull.Value)
 12         {
 13             article.DatePublished = (DateTime)row["DatePublished"];
 14         }
 15 
 16         if (row["DateModified"] != DBNull.Value)
 17         {
 18             article.DateModified = (DateTime)row["DateModified"];
 19         }
 20 
 21         return article;
 22     }
 23 }
 
I made also a Business Layer generic class, but has it is just for the moment a wrapper around the DataAccessor, I will not show it.
 
And finally the Interpreter Design Pattern:
 
  1 public abstract class Spec
  2 {
  3 	public abstract bool isSatisfiedBy(Article article);
  4 }
With the different concrete Specifications:
  1 public class PublishedSpec : Spec
  2 {
  3 	public PublishedSpec()
  4 	{
  5 	}
  6 
  7 	public override bool isSatisfiedBy(Article article)
  8 	{
  9  		return (article.isPublished == true);
 10 	}
 11 }
 12 
 13 public class BeforeDateSpec : Spec
 14 {
 15 	private DateTime date;
 16 
 17 	public DateTime Date
 18 	{
 19 		get { return date; }
 20 		set { date = value; }
 21 	}
 22 
 23 	public BeforeDateSpec(DateTime date)
 24 	{
 25 		this.Date = date;
 26 	}
 27 
 28 	public override bool isSatisfiedBy(Article article)
 29 	{
 30 		return (article.DatePublished < this.Date);
 31 	}
 32 }
 33 
 34 public class AuthorSpec : Spec
 35 {
 36 	private string author;
 37 
 38 	public string Author
 39 	{
 40 	  get { return author;}
 41 	  set { author = value; }
 42 	}
 43 
 44 	public AuthorSpec (string author)
 45 	{
 46 		this.Author = author;
 47 	}
 48 
 49 	public override bool isSatisfiedBy(Article article)
 50 	{
 51  		return (article.Author == this.Author);
 52 	}
 53 }
 54 
 55 public class NotSpec : Spec
 56 {
 57 	private Spec specToNegate;
 58 
 59 	public NotSpec(Spec specToNegate)
 60 	{
 61 		this.specToNegate = specToNegate;
 62 	}
 63 
 64 	public override bool isSatisfiedBy(Article article)
 65 	{
 66 		return !specToNegate.isSatisfiedBy(article);
 67 	}
 68 }
 69 
 70 public class AndSpec : Spec
 71 {
 72 	private Spec augend;
 73 	public Spec Augend
 74 	{
 75 	  get { return augend;}
 76 	}
 77 
 78 	private Spec addend;
 79 	public Spec Addend
 80 	{
 81 	  get { return addend;}
 82 	}
 83 	
 84 	public AndSpec (Spec augend, Spec addend)
 85 	{
 86 		this.augend = augend;
 87 		this.addend = addend;
 88 	}
 89 
 90 	public override bool  isSatisfiedBy(Article article)
 91 	{
 92  		return Augend.isSatisfiedBy(article) && Addend.isSatisfiedBy(article);
 93 	}
 94 }
 95 
 96 public class OrSpec : Spec
 97 {
 98 	private Spec augend;
 99 	public Spec Augend
100 	{
101 		get { return augend; }
102 	}
103 
104 	private Spec addend;
105 	public Spec Addend
106 	{
107 		get { return addend; }
108 	}
109 
110 	public OrSpec(Spec augend, Spec addend)
111 	{
112 		this.augend = augend;
113 		this.addend = addend;
114 	}
115 
116 	public override bool isSatisfiedBy(Article article)
117 	{
118 		return Augend.isSatisfiedBy(article) || Addend.isSatisfiedBy(article);
119 	}
120 }

Comments (1) -

lrxapqxosl

9PDcxo  http://zqddigbsocev.com/">zqddigbsocev, [url=http://qsvdidbdvmyd.com/]qsvdidbdvmyd[/url], [link=http://yfwvybgijorb.com/]yfwvybgijorb[/link], http://tvfonboolyuh.com/

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About Laurent

Laurent Kempé

Laurent Kempé is the editor, founder, and primary contributor of Tech Head Brothers, a French portal about Microsoft .NET technologies.

He is currently employed by Innoveo Solutions since 10/2007 as a Senior Solution Architect, certified Scrum Master and Founding Member.

Founder, owner and Managing Partner of Jobping, which provides a unique and efficient platform for connecting Microsoft skilled job seekers with employers using Microsoft technologies.

Laurent was awarded Most Valuable Professional (MVP) by Microsoft from April 2002 to April 2012.

JetBrains Academy Member
Certified ScrumMaster
My status

Twitter

Flickr

www.flickr.com
This is a Flickr badge showing public photos and videos from Laurent Kempé. Make your own badge here.

Month List

Page List