From the word decorator, it adds new functionality (Decoration) to the operations of your instantiated object. If you will try to analyze the design it is like specializing a class through inheritance but here we are doing it on runtime. The Idea is to have an abstract class that will be the base type of all the components. As you instantiate the object you are passing an object of the same type and return a newly instantiated object of the same base type but has additional process on the methods that is being decorated. As you continue on decorating the object by instantiating another decorator object, the functionality accumulates. recursion happens by calling an overidden method until it goes to the first base class that was instantiated.
To understand the concept, imagine += operator in C# which accumulates the sum of numbers:
A = 5;
B = 10;
C = 15;
A += B;
A += C;
on the last line A has a component of B and C (5 + 10 + 15) which we accumalate by the += operator.
Let us see now the design in Action. Below is the UML diagram of the DatabaseProvider Decorator I created:
To understand the concept, imagine += operator in C# which accumulates the sum of numbers:
A = 5;
B = 10;
C = 15;
A += B;
A += C;
on the last line A has a component of B and C (5 + 10 + 15) which we accumalate by the += operator.
Let us see now the design in Action. Below is the UML diagram of the DatabaseProvider Decorator I created:
Click Image to Enlarge
Before I adapted the Decorator Pattern here, the original purpose of the DatabaseProvider is to Provide a Template (IDatabase) of basic Database Execution for all kinds of database. With that, I can use this with my DatabaseProviderFactory without even worrying on what connection I am using. As the need arises, I have thought of a way of discovering Parameters of the stored procedure then extracting the values for that parameter on the Entity being passed on the decorator object. Actually this design is just part of my own MVC implementation which I might also discuss in the future.
Let us see now the codes :
Here is the IDatabase Interface which provides the template for all kind of Database
public interface IDatabase : IDisposable
{
string CommandText { get; set; }
CommandType CommandType { get; set; }
string ConnectionString { get; set; }
void AddParameterWithValue(string parameterName, object value);
void AddParameter(IDataParameter parameter);
IDataReader ExecuteReader();
int ExecuteNonQuery();
object ExecuteScalar();
DataSet ExecuteDataset();
List<T> ExecuteToEntity<T>(T instance) where T : IEntity<T>;
}
This next class is the Decorator Abstract Class which the main functionality is to save an object of IDatabase, which is also his type, that is passed on the constructor.
public abstract class DBDecorator : IDatabase
{
protected IDatabase _database;
public DBDecorator(IDatabase database)
{
_database = database;
}
public string CommandText
{
get{return _database.CommandText;}
set{_database.CommandText = value;}
}
public CommandType CommandType
{
get{return _database.CommandType;}
set{_database.CommandType = value;}
}
public string ConnectionString
{
get{return _database.ConnectionString;}
set{_database.ConnectionString = value;}
}
public virtual void AddParameterWithValue(string parameterName, object value)
{
_database.AddParameterWithValue(parameterName, value);
}
public virtual void AddParameter(IDataParameter parameter)
{
_database.AddParameter(parameter);
}
public virtual IDataReader ExecuteReader()
{
return _database.ExecuteReader();
}
public virtual int ExecuteNonQuery()
{
return _database.ExecuteNonQuery();
}
public virtual object ExecuteScalar()
{
return _database.ExecuteScalar();
}
public virtual DataSet ExecuteDataset()
{
return _database.ExecuteDataset();
}
public virtual List<T> ExecuteToEntity<T>(T instance) where T : IEntity<T>
{
return _database.ExecuteToEntity(instance);
}
public void Dispose()
{
_database.Dispose();
}
}
Now the last class adds two new private methods, DiscoverParameter and AssignParameter which is being called before the execusion of the actual Base Class.
public class SQLDatabaseDecorator<T> : DBDecorator
{
private List _parameterNames = new List();
private T _entity;
public SQLDatabaseDecorator(IDatabase db, T entity)
: base(db)
{
_entity = entity;
}
private void DiscoverParameter()
{
PersistenceManager<list<string>> parameterNamesPM =
new PersistenceManager<list<string>>(PersistIn.Application,
CommandText + "_SQLDatabaseDecorator");
if (parameterNamesPM.Exists())
{
_parameterNames = parameterNamesPM.Get();
}
else
{
using (SqlConnection connection = new SqlConnection(this.ConnectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
command.CommandText = this.CommandText;
command.CommandType = this.CommandType;
SqlCommandBuilder.DeriveParameters(command);
foreach (SqlParameter param in command.Parameters)
{
_parameterNames.Add(param.ParameterName);
}
parameterNamesPM.Add(_parameterNames);
}
}
}
private void AssignParameters()
{
foreach (string paramName in _parameterNames)
{
if (_entity.GetType().GetProperties().ToList()
.Exists(x => ("@" + x.Name.ToUpper()) == paramName.ToUpper()))
{
PropertyInfo pi = _entity.GetType().GetProperties().ToList()
.Find(x => ("@" + x.Name.ToUpper()) == paramName.ToUpper());
base.AddParameterWithValue(paramName, pi.GetValue(_entity, null));
}
}
}
public override IDataReader ExecuteReader()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteReader();
}
public override int ExecuteNonQuery()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteNonQuery();
}
public override object ExecuteScalar()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteScalar();
}
public override DataSet ExecuteDataset()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteDataset();
}
public override List<U> ExecuteToEntity<U>(U instance)
{
DiscoverParameter();
AssignParameters();
return base.ExecuteToEntity(instance);
}
}
Now the classes are ready, here is how we use the decorator objects :
Company InsertCompany = new Company();
InsertCompany.CompanyName = "My Company";
InsertCompany.Address = "Global City Taguig";
InsertCompany.PhoneNumber = "111-11-11";
InsertCompany.UserId = 1;
string connectionString = "Data Source=.;Initial Catalog=GE;
Integrated Security=false;User Id=sa;Password=password;";
IDatabase target = new SQLDatabaseProvider(connectionString);
using (target = new SQLDatabaseDecorator(target, InsertCompany))
{
target.CommandText = "AddCompany";
target.CommandType = System.Data.CommandType.StoredProcedure;
InsertCompany.CompanyId = Convert.ToInt32(target.ExecuteScalar());
System.Console.WriteLine("New Company Id was inserted with Company Id {0}",
InsertCompany.CompanyId);
}
If you notice, I did not pass any parameters on the DatabaseProvider. The Decorator is doing it for us. It reads the parameter in the database then locate the values on the entity which in this case is the Company Object.
Happy Programming!!!
Let us see now the codes :
Here is the IDatabase Interface which provides the template for all kind of Database
public interface IDatabase : IDisposable
{
string CommandText { get; set; }
CommandType CommandType { get; set; }
string ConnectionString { get; set; }
void AddParameterWithValue(string parameterName, object value);
void AddParameter(IDataParameter parameter);
IDataReader ExecuteReader();
int ExecuteNonQuery();
object ExecuteScalar();
DataSet ExecuteDataset();
List<T> ExecuteToEntity<T>(T instance) where T : IEntity<T>;
}
This next class is the Decorator Abstract Class which the main functionality is to save an object of IDatabase, which is also his type, that is passed on the constructor.
public abstract class DBDecorator : IDatabase
{
protected IDatabase _database;
public DBDecorator(IDatabase database)
{
_database = database;
}
public string CommandText
{
get{return _database.CommandText;}
set{_database.CommandText = value;}
}
public CommandType CommandType
{
get{return _database.CommandType;}
set{_database.CommandType = value;}
}
public string ConnectionString
{
get{return _database.ConnectionString;}
set{_database.ConnectionString = value;}
}
public virtual void AddParameterWithValue(string parameterName, object value)
{
_database.AddParameterWithValue(parameterName, value);
}
public virtual void AddParameter(IDataParameter parameter)
{
_database.AddParameter(parameter);
}
public virtual IDataReader ExecuteReader()
{
return _database.ExecuteReader();
}
public virtual int ExecuteNonQuery()
{
return _database.ExecuteNonQuery();
}
public virtual object ExecuteScalar()
{
return _database.ExecuteScalar();
}
public virtual DataSet ExecuteDataset()
{
return _database.ExecuteDataset();
}
public virtual List<T> ExecuteToEntity<T>(T instance) where T : IEntity<T>
{
return _database.ExecuteToEntity(instance);
}
public void Dispose()
{
_database.Dispose();
}
}
Now the last class adds two new private methods, DiscoverParameter and AssignParameter which is being called before the execusion of the actual Base Class.
public class SQLDatabaseDecorator<T> : DBDecorator
{
private List
private T _entity;
public SQLDatabaseDecorator(IDatabase db, T entity)
: base(db)
{
_entity = entity;
}
private void DiscoverParameter()
{
PersistenceManager<list<string>> parameterNamesPM =
new PersistenceManager<list<string>>(PersistIn.Application,
CommandText + "_SQLDatabaseDecorator");
if (parameterNamesPM.Exists())
{
_parameterNames = parameterNamesPM.Get();
}
else
{
using (SqlConnection connection = new SqlConnection(this.ConnectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
command.CommandText = this.CommandText;
command.CommandType = this.CommandType;
SqlCommandBuilder.DeriveParameters(command);
foreach (SqlParameter param in command.Parameters)
{
_parameterNames.Add(param.ParameterName);
}
parameterNamesPM.Add(_parameterNames);
}
}
}
private void AssignParameters()
{
foreach (string paramName in _parameterNames)
{
if (_entity.GetType().GetProperties().ToList()
.Exists(x => ("@" + x.Name.ToUpper()) == paramName.ToUpper()))
{
PropertyInfo pi = _entity.GetType().GetProperties().ToList()
.Find(x => ("@" + x.Name.ToUpper()) == paramName.ToUpper());
base.AddParameterWithValue(paramName, pi.GetValue(_entity, null));
}
}
}
public override IDataReader ExecuteReader()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteReader();
}
public override int ExecuteNonQuery()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteNonQuery();
}
public override object ExecuteScalar()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteScalar();
}
public override DataSet ExecuteDataset()
{
DiscoverParameter();
AssignParameters();
return base.ExecuteDataset();
}
public override List<U> ExecuteToEntity<U>(U instance)
{
DiscoverParameter();
AssignParameters();
return base.ExecuteToEntity(instance);
}
}
Now the classes are ready, here is how we use the decorator objects :
Company InsertCompany = new Company();
InsertCompany.CompanyName = "My Company";
InsertCompany.Address = "Global City Taguig";
InsertCompany.PhoneNumber = "111-11-11";
InsertCompany.UserId = 1;
string connectionString = "Data Source=.;Initial Catalog=GE;
Integrated Security=false;User Id=sa;Password=password;";
IDatabase target = new SQLDatabaseProvider(connectionString);
using (target = new SQLDatabaseDecorator
{
target.CommandText = "AddCompany";
target.CommandType = System.Data.CommandType.StoredProcedure;
InsertCompany.CompanyId = Convert.ToInt32(target.ExecuteScalar());
System.Console.WriteLine("New Company Id was inserted with Company Id {0}",
InsertCompany.CompanyId);
}
If you notice, I did not pass any parameters on the DatabaseProvider. The Decorator is doing it for us. It reads the parameter in the database then locate the values on the entity which in this case is the Company Object.
Happy Programming!!!
If you learned from this post please leave a comment.
ReplyDeletei dont think you actually need this..
ReplyDeletejust look at the System.Data.Common Namespace..
Why invent if there's already one?
Hi Jan,
ReplyDeleteThanks for leaving a comment.
I know about System.Data.Common. Actually, I just didn't show the code for SQLDatabaseProvider which shows some of the classes from System.Data.Common. Maybe I should post another topic about Adapter Design and Abstract Factory so you can understand more why do I need to create an IDatabase interface.
The purpose of this design I have is to pass the Entity Object and the Class will be the one to look for the values to be supplied on the Stored Procedures.
I think you have an interesting topic. However, i think you use a not so appropriate application of your chosen design pattern. You got the right idea about the use of decorator and that is adding responsibility to an object at runtime without resorting to subclassing. I think Factory Method and the Abstract Factory is appropriate for your example. Adapter pattern is different though. Still, thanks for sharing your idea. Keep it up.
ReplyDelete