2

I am reviewing part of a code base, and I come to the exception handling part which is really messy. I would like to replace it with something more elegant. Then I thought it might not be a bad idea if I could have a fluent interface in place that would help me register some policy for a list of exceptions, and let an ExceptionHandlingManager do the rest for me :

Here's an example how it should work:

For<TException>.RegisterPolicy<TPolicy>(a lambda expression that describes the detail);

but I am totally lost. Am I on the right track? When we want to design a fluent interface like this, what's the best approach? I mean if fluent interfaces are part of DSLs, then is designing a fluent interface like designing a language?


This module that I am talking about it's a general module that is responsible for all not handled exceptions.and it's a module of hundred lines like this :

if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    // do list of things things like perform logging to database 
    // and translating/reporting it to user
}
else if (exp.GetType()==typeof(expType2))
{
    //do some other list of things...
    ...
}
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Beatles1692
  • 5,214
  • 34
  • 65

3 Answers3

2

This is my second attempt at answering your question. As I understand correctly, you're trying to get away from nested ifs and elses from a code like this:

if(exp.GetType()==typeof(expType1))
{
    if(exp.Message.Include("something went bad"))
    {
      if(exp.InnerException.Message == "Something inside went bad as well";
      {
        DoX();
        DoY();
      }
    }
}
else if (exp.GetType()==typeof(expType2))
{
  DoZ();
  DoV();
}

Consider now that you've created a chained API that reads like this:

var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessage(message => message.Includes("something went bad"))
   .WithInnerException<SomeInnerException>()
     .HavingAMessage(innerMessage => innerMessage == "Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });

Or even something that looks like this:

var handlingManager = new ExceptionHandlingManager();
handlingManager
 .For<Exception>()
   .HavingAMessageThatIncludes("something went bad")
   .WithInnerException<SomeInnerException>()
     .HavingAMessageEqualTo("Something inside went bad as well")
   .Perform(() => 
   {
     DoX();
     DoY();
   });

Both of these don't actually buy you anything. Let's quickly enumerate the two features for embedded domain specific languages:

  1. They restrict the set of operations you use to the set associated with domain only
  2. They provide a more expressive API that does a better job of describing a problem than a general purpose language.

Now, what's important is that if you created a language for setting up actions based on some object properties (like the two examples I gave you above), it would fulfill point 1, but would not fulfill the point 2. If you compare the "fluent" versions with the "plain C#" one, the "plain C# one" is actually more expressive (less characters) and more readable (I already know C#, but I don't know your API yet), even though the "fluent" version is more verbose (but DSLs and fluent interfaces are not about verbosity, they're about expressiveness and readability).

In other words, the "fluent" version is expecting more of me (learn new API) while providing no advantages in return (the "fluent" API is no more expressive than the plain C# one), which makes me never want to even try the "fluent" version.

Also, you say that you want to get out of nested ifs. Why is that so? In many embedded DSLs, we strive for nesting where it better reflects the structure of the solution (see first example from http://broadcast.oreilly.com/2010/10/understanding-c-simple-linq-to.html - it's Microsoft's embedded DSL for writing XML). Also, take a look at my both examples - I intentionally made the spacing as it is to show you that the nesting doesn't really go away when you switch to dotted().notation().

Another thing. The "fluent" version may give you an illusion that you're being more "declarative" by preconfiguring an object with rules which it will exercise when needed, but that's really no different from taking the "plain C#" version and putting it into a separate object or method and call that method when need arises. Maintainability is exactly the same (actually, the "plain C#" version would probably be more maintainable, because with the "fluent" version, you'd have to extend the API with new methods each time you run into a case that's not already handled by the API).

So, my conclusion is this: if you need a general-purpose DSL for firing actions based on some object comparisons, then stop - C# with its "if", "else", "try" and "catch" statements is already good at this and the gain from making it "fluent" is an illusion. Domain Specific Languages are used to wrap domain-specific operations behind an expressive API and your case doesn't look like it.

If you really want to get away from nested ifs, then a better idea is to change the throwing logic to differentiate the failure scenarios based on exception type, not by exception properties. E.g. instead of:

if(situationA)
{
  throw Exception("XYZ");
}
else if (situationB)
{
  throw Exception("CVZF");
}

make it:

if(situationA)
{
  throw ExceptionXYZ();
}
else if (situationB)
{
  throw ExceptionCVZF();
}

Then you'll need no nested ifs - your exception handling will be:

try
{
  XYZ()
}
catch(ExceptionXYZ)
{
  DoX();
  DoY();
}
catch(ExceptionCVZF)
{
  DoZ();
  DoV();
}
2

I wrote a simple fluent exception handler. It is easily extensible. You can look at it here: http://thetoeb.wordpress.com/2013/12/09/fluent-exceptionhandling/ maybe it can be customized to your goal.

toeb
  • 481
  • 5
  • 9
0

Beatles1692, forgive me, but I'll start with addressing your main concern of refactoring exception handling instead of jumping to the DSL part straight away.

In your question you say the following:

I am reviewing part of a code base, and I come to the exception handling part which is really messy. I would like to replace it with something more elegant.

which I assume is your main concern. So I'll try to provide you a guide on how to make it more elegant - actually the code snippet you provided not being elegant is not about it not being a DSL or fluent interfaces, but about design qualitities. If you have redundancy and coupling in your design, then creating a fluent interface on top of that redundancy and coupling will only make it a "prettier mess".

The answer will be a long one and I'll refer to some code qualities, so if you need further explanations, just let me know. Since there are many variables involved in making such decision (like cost of change, code ownership etc.) I'll try to provide you with the "cleanest" solution and then with one that requires least effort.

In situations such as these, it's good to apply the advice from Gang of Four, the authors of the classical Design Patterns. This advice is: "Encapsulate what varies". in your case, it's the failure handling is what varies, and the variation is based on the exception type. How would I apply it here?

First solution - refactor the smells out of the code completely

The first question I'd ask is this: are you free to modify the code that throws the exceptions? If so, I'd try to encapsulate not inside the code where you catch the exceptions, but inside the code that throws them. That might seem strange for you, but this might allow you to avoid redundancy. As it is, your code is currently coupled to a type of exception in two places.

The first place is where you throw it (you have to know which exception to throw) - greatly simplified, it might look like this:

 if(someSituationTakesPlace())
 {
   throw new ExpType1();
 }
 else if(someOtherSituationTakesPlace()
 {
   throw new ExpType2();
 }

etc. Of course, the conditions might be more complex, there might be multiple different objects and methods where you throw, but essentially, it always boils down to a series of choices like "In situation A, throw Exception X".

The second place where you have this mapping is when you catch the exception - you have to, again, go through a series of if-elses to find out what situation it was and then invoke some logic that would handle it.

To avoid this redundancy, I'd decide on the handling where you throw the exception - you should have all the information you need there. Thus, I'd first define an exception class like this:

public class Failure : Exception
{
  IFailureHandling _handling;

  public Failure(IFailureHandling handling)
  {
    //we're injecting how the failure should be handled
    _handling = handling;
  }
  //If you need to provide additional info from 
  //the place where you catch, you can use parameter list of this method
  public void Handle() 
  {
    _handling.Perform();
  }
}

Then, I'd make a factory that creates such exceptions, binding them, already at this point, with handlers. E.g.:

public class FailureFactory
{
  IFailureHandling _handlingOfCaseWhenSensorsAreDown,
  IFailureHandling _handlingOfCaseWhenNetworkConnectionFailed

  public FailureFactory(
    IFailureHandling handlingOfCaseWhenSensorsAreDown,
    IFailureHandling handlingOfCaseWhenNetworkConnectionFailed
    //etc.
    )
  {
    _handlingOfCaseWhenSensorsAreDown 
      = handlingOfCaseWhenSensorsAreDown;
    _handlingOfCaseWhenNetworkConnectionFailed 
      = handlingOfCaseWhenNetworkConnectionFailed;
    //etc.
  }

  public Failure CreateForCaseWhenSensorsAreDamaged()
  {
    return new Failure(_handlingOfCaseWhenSensorsAreDown);
  }

  public Failure CreateForCaseWhenNetworkConnectionFailed()
  {
    return new Failure(_handlingOfCaseWhenNetworkConnectionFailed);
  }
}

You would typically create just one such factory per whole system and do it in the place where you instantiate all long-running objects (typically there's one such place in an application), so, while instantiating the factory, you should be able to pass all the objects you want it to use through the constructor (funny as it is, this would create a very primitive fluent interface. Remember fluent interfaces are about readability and flow, not only putting.a.dot.every.method.call :-):

var inCaseOfSensorDamagedLogItToDatabaseAndNotifyUser
  = InCaseOfSensorDamagedLogItToDatabaseAndNotifyUser(
      logger, translation, notificationChannel);
var inCaseOfNetworkDownCloseTheApplicationAndDumpMemory 
  = new InCaseOfNetworkDownCloseTheApplicationAndDumpMemory(
      memoryDumpMechanism);

var failureFactory = new FailureFactory(
  inCaseOfSensorDamagedLogItToDatabaseAndNotifyUser,
  inCaseOfNetworkDownCloseTheApplicationAndDumpMemory
);

This way, both the place where you throw the exception and where you catch it are decoupled from the handling logic - which is what varies in your issue! Thus, we have encapsulated what varies! Of course, you're free to provide more advanced fluent interface on top of this.

Now every place where you throw the exception would look like this:

if(sensorsFailed())
{ 
  throw _failureFactory.CreateForCaseWhenSensorsAreDamaged();
}

And a place where you catch all these exceptions would look like this:

try
{
  PerformSomeLogic();
} 
catch(Failure failure)
{
  failure.Handle();
}

This way, the only place where you'd know how to handle each failure case would be within the logic that creates an object of class FailureFactory.

Second solution - use handler

In case you don't own the code that's throwing the exceptions or it would be too costly or too risky to put in the solution outlined above, I'd use a Handler object that would look similarly to the FailureFactory, but instead of creating objects, it would execute the handling himself:

public class FailureHandlingMechanism
{
  _handlers = Dictionary<Type, IFailureHandling>();

  public FailureHandler(Dictionary<Type, IFailureHandling> handlers)
  {
    _handlers = handlers;
  }

  public void Handle(Exception e)
  {
    //might add some checking whether key is in dictionary
    _handlers[e.GetType()].Perform();
  }
}

Instantiating such handling mechanism would already give you a very primitive fluent interface:

var handlingMechanism = new HandlingMechanism(
  new Dictionary<Type, IFailureHandling>()
  {
    { typeof(NullPointerException), new LogErrorAndCloseApplication()}},
    { typeof(ArgumentException}, new LogErrorAndNotifyUser() }
  };

If you wish for even more fluent and less noisy way of configuring such handling mechanism, you can create a builder around the HandlingMechanism that has methods for adding keys and values to dictionary and a method called Build() that created the object for you:

var handlingMechanismThatPerforms = new HandlingMechanismBuilder();
var logErrorAndCloseApplication = new LogErrorAndCloseApplication();
var logErrorAndNotifyUser = new LogErrorAndNotifyUser();

var handlingMechanism = handlingMechanismThatPerforms
  .When<NullPointerException>(logErrorAndCloseApplication)
  .When<ArgumentException>(logErrorAndNotifyUser)
  .Build();

And that's it. Let me know if it helps you in any way!