Does this match any known design pattern?
I'm working on a simple, internal ASP.NET app for a small company. I've designed the data access layer so it's database-agnostic.
I have the following:
- IDataHelper - An interface requiring methods such as FillDataTable(), ExecuteNonQuery(), etc.
- MySqlHelper - Implements IDataHelper for MySQL, the only database I support so far.
- Static data access classes - Encapsulate data access methods for different parts of the app. They call methods on an IDataHelper to execute queries.
And finally, I have a static class that creates an IDataHelper for the data access classes to call. In the future it will create the appropriate helper based on the d开发者_JAVA百科atabase specified in the config file. For now it's just hard-coded to create a MySqlHelper:
public static class DataHelperContainer
{
private static IDataHelper dataHelper;
public static IDataHelper DataHelper
{
get { return dataHelper; }
}
static DataHelperContainer()
{
string connectionString = ConfigurationManager
.ConnectionStrings["myapp"].ConnectionString;
// Support for other databases will be added later.
dataHelper = new MySqlHelper(connectionString);
}
}
My questions are:
- What should I name this? "DataHelperContainer" doesn't seem right, since that implies some kind of list.
- Is this a good or bad design? If it's bad, what are its faults?
- Is this similar to any known design pattern? Can it be refactored to conform to one? It seems remotely like a factory, but I'm not sure.
Sorry about the long post and multiple questions. :)
Thanks!
It looks like the Strategy pattern. With the strategy pattern would would be able to change the underlying functionality during the runtime of the program, and would be able to create new functionality without changing the basic flow of your data layer. [Functions are guarantied via the IHelper interface]
You could name it DataHelperFactory.
It's a good pattern. You definitely don't want to leak things like
ConfigurationManager.ConnectionStrings["myapp"].ConnectionString
all over the place! A fault is that it's static which makes it difficult to test any code using it.This is most like the factory pattern or possibly service locator (not strategy).
Currently, your code will look like this:
public class MyClass
{
public void DoSomething()
{
var helper = DataHelperFactory.Create();
helper.ExecuteNonQuery("some sql");
}
}
This is not easily testable because you'd have to modify your app.config in order to change what helper you get back in your test. Maybe you want to test what happens when helper.ExecuteNotQuery throws an exception.
Using dependency injection, your class would change to:
public class MyClass
{
private IDataHelper helper;
public MyClass(IDataHelper helper)
{
this.helper = helper;
}
public void DoSomething()
{
helper.ExecuteNonQuery("some sql");
}
}
The trade-off here is that now you have to deal with supplying the IDataHelper dependency in the calling context. This is where IoC containers like Unity, Windsor, and StructureMap enter. This is more complex though and maybe not worth it in your situation.
Using a factory (even static) is great. It allows you to use other patterns like the decorator to add additional behavior. Consider a scenario where you want to sanitize your sql string and make sure nothing bad is sent to your db:
public class SanitizingDataHelper : IDataHelper
{
private IDataHelper helper;
public SanitizingDataHelper(IDataHelper helper)
{
this.helper = helper;
}
public void ExecuteNotQuery(string sql)
{
sql = EscapeHarmfulSql(sql);
helper.ExecuteNonQuery(sql);
}
private string EscapeHarmfulSql(string sql)
{
...
}
}
Your factory can then do something like this:
public class DataHelperFactory
{
public IDataHelper Create()
{
...
var helper = new MySqlDataHelper(connectionString);
return new SanitizingDataHelper(helper);
}
}
For the data access classes it seems like it might be a Data Access Objects (DAO) pattern, but I'm not sure exactly how you are implementing that. Andy West is right you definitely have a Strategy pattern in there.
精彩评论