C# Execute Around Method
Kent Beck called one of the patterns in Smalltalk Best Practice Patterns “Execute Around Method.” It’s a useful pattern for removing duplication in code that requires boilerplate code to be run both before and after the code you really want to write. It’s a much lighter weight method than template methods (no subclassing), which can accomplish the same goal.
As an example, I’ve written the following boilerplate ADO.NET code countless times:
public DataTable GetTable(string query, IDictionary parameters)
{
using (SqlConnection connection = new SqlConnection(this.connectionString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
foreach (DictionaryEntry parameter in parameters)
{
command.Parameters.AddWithValue(
parameter.Key.ToString(), parameter.Value);
}
SqlDataAdapter adapter = new SqlDataAdapter(command);
using (DataSet dataset = new DataSet())
{
adapter.Fill(dataset);
return dataset.Tables0;
}
}
}
}
public void Exec(string query, IDictionary parameters)
{
using (SqlConnection connection = new SqlConnection(this.connectionString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
foreach (DictionaryEntry parameter in parameters)
{
command.Parameters.AddWithValue(
parameter.Key.ToString(), parameter.Value);
}
command.ExecuteNonQuery();
}
}
}
Notice that the connection and parameter management overwhelms the actual code that each method is trying to get to. And the duplication means I have multiple places to change when I decide to do something differently. However, since the using block encloses the relevant code, a simple Extract Method refactoring is not as easy to see.
Here’s the result of applying an Execute Around Method pattern to it.
private delegate object SqlCommandDelegate(SqlCommand command);
public DataTable GetTable(string query, IDictionary parameters)
{
return (DataTable)ExecSql(query, parameters, delegate(SqlCommand command)
{
SqlDataAdapter adapter = new SqlDataAdapter(command);
using (DataSet dataset = new DataSet())
{
adapter.Fill(dataset);
return dataset.Tables0;
}
});
}
public void Exec(string query, IDictionary parameters)
{
ExecSql(query, parameters, delegate(SqlCommand command)
{
return command.ExecuteNonQuery();
});
}
private object ExecSql(string query, IDictionary parameters,
SqlCommandDelegate action)
{
using (SqlConnection connection = new SqlConnection(this.onnectionString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
foreach (DictionaryEntry parameter in parameters)
{
command.Parameters.AddWithValue(
parameter.Key.ToString(), parameter.Value);
}
return action(command);
}
}
}
Much nicer, no?