Montag, 19. November 2012

Exceptions mal anders! Oder Messages mit Exceptions. ;-) Ein Antipattern.

Irgendwann hatten wir uns unterhalten ob man denn Exceptions nicht auch anders verwenden könnte.
Zum Beispiel für den Nachrichtenaustausch innerhalb einer Applikation.
Klingt skurril, ist skurril, aber gesagt, getan. Ich habe mich mal dran gesetzt und ein kleines Beispiel programmiert, dass den Normalfall sozusagen zur Ausnahme (Exception) werden lässt. 

Hier das Beispiel für einen kleinen Order-Service:
 //Als erstes eine Klasse Order
public class Order
{
    public int ID { get; set; }
    public string Comment { get; set; }
    public decimal Total { get; set; }
}

 //Dann eine Klasse OrderMessage als allgemeine Basisklasse für Benachrichtigungen über die Änderung an einer Order.
 public class OrderMessage : Exception
 {
     public Order Order { get; set; }
     public OrderMessage(Order order)
     {
         Order = order;
     }
 }

//Nun eine Klasse um speziell das Anlegen einer neuen Order zu Benachrichtigen
public class OrderCreatedMessage : OrderMessage
{
    public DateTime CreateDate { get; set; }
    public OrderCreatedMessage(Order order, DateTime createDate) 
    : base(order)
    {
        CreateDate = createDate;
    }
}

//Und hier der OrderService mit der Create Methode, die auch die OrderCreatedMessage auslöst.

public class OrderService
{
    public void Create()
    {
        var p = new Order() {ID = 10, Comment = "This is a new order.", 
        Total = 100.00m};
        throw new OrderCreatedMessage(p, DateTime.Now);
    }
}

//Verwenden kann man das Ganze nun so.
try
    {
        new OrderService().Create();
    }
    catch (OrderCreatedMessage orderCreated)
    {
        Console.WriteLine(orderCreated.CreateDate);
        throw;
    }
Das Schöne ist nun, dass man für beliebige Aktionen (natürlich auch für Ausnahmen) eine Nachricht definieren kann.
Die Nachricht (Exception) durchwandert den gesamten Call-Stack bis zum Aufrufer und kann an beliebigen Positionen innerhalb des Call-Stacks gefangen (catch) und behandelt werden.
Wenn die Nachricht gecatcht wurde, dann kann entweder eine neue Nachricht erzeugt oder die Nachricht mit einem einfachen throw weiter geleitet werden.
Eigentlich gar nicht so dumm?! ;-)
Man erspart sich das Verknüpfen von Events und es wird quasi vom Framewprk schon garantiert, dass die Nachricht den gesamten Call-Stack durchläuft.