28 February 2010

Injecting logic in the middle of an algorithm using “Func<T>” function parameters

Sometimes you run into a situation in which you have a very similar complex pieces of logic, that only differ somewhere deep inside. This happens especially when you are dealing with legacy code that requires all kinds of weird initializers. Consider the following example, which is similar to something I ran into deep down in a CSLA library:

private void DataPortal_Fetch(string a, string b, string c)
{
  var helper = new LoaderHelper();
  helper.Init();
  LegacyObjectCollection result = 
OldStaticHelper.GetByThreeStrings( a, b, c); IsReadOnly = false; result.ForEach(p => Add(SomeNewObject.Get(p, helper))); RaiseListChangedEvents = false; IsReadOnly = true; } private void DataPortal_Fetch(string a, int n) { var helper = new LoaderHelper(); helper.Init(); LegacyObjectCollection result = OldStaticHelper.GetByOtherKeys( a, n); IsReadOnly = false; result.ForEach(p => Add(SomeNewObject.Get(p, helper))); RaiseListChangedEvents = false; IsReadOnly = true; }

The idea clearly was to call some legacy code and transform this into a list of “SomeNewObject”. But the annoying thing about the legacy code was that all kinds of helpers needed to be initialized before OldStaticHelper could be called. And then the logic of transforming the data had been duplicated as well. There are a number of ways to refactor this, but because I was fooling around with functional programming concepts at the time, I tried the following, which almost worked:

private void DataPortal_Fetch(string a, string b, string c)
{
  FetchData(OldStaticHelper.GetByThreeStrings( a, b, c)); 
}

private void DataPortal_Fetch(string a, int n)
{
  FetchData(OldStaticHelper.GetByOtherKeys( a, n));
}

private void FetchData(Func<LegacyObjectCollection> loaderMethod)
{
  var helper = new LoaderHelper();
  helper.Init();
  LegacyObjectCollection result = loaderMethod();
  IsReadOnly = false;
  result.ForEach(p => Add(SomeNewObject.Get(p, helper)));
  RaiseListChangedEvents = false;
  IsReadOnly = true;
}

Although this compiled an ran, it did not work as expected. Upon calling “FetchData” the OldStaticHelper.GetBy… method was instantly executed, in stead of of when “result = loaderMethod()” was called. And I just stated that it would not work unless the “helper” code was initialized. Thus, I needed to use delegates:

private void DataPortal_Fetch(string a, string b, string c)
{
  FetchData(() => OldStaticHelper.GetByThreeStrings( a, b, c)); 
}

private void DataPortal_Fetch(string a, int n)
{
  FetchData(() => OldStaticHelper.GetByOtherKeys( a, n));
}
Or, for those not familiar with the lambda syntax:
private void DataPortal_Fetch(string a, string b, string c)
{
  FetchData(delegate { return OldStaticHelper.GetByThreeStrings( a, b, c)}); 
}

private void DataPortal_Fetch(string a, int n)
{
  FetchData(delegate { return OldStaticHelper.GetByOtherKeys( a, n)});
}
Using this technique you can 'inject' a piece of logic into a larger algorithm, eliminating the need to duplicate code or making all kinds of in-between data sets

No comments: