Archive

Archive for January 6th, 2010

Patterns that cross borders - C# to IronPython

January 6th, 2010 Simon Segal 2 comments

Recent consideration of the Template Method Pattern got me to wondering about how I might achieve a little more dynamism with respect to declaration, definition and instantiation when using the template method pattern. Without delving too deep into the “classical” Template Method Pattern it can be described here in code with the most trivial of examples.

C# Classic Template Method Pattern

abstract class FootballMatch
{
    abstract void StartGame();

    abstract void EndGame();

    public virtual void PlayGame()
    {
        Console.Writeline("Players kick the ball around a field");
    }

    public virtual void CompleteMatch()
    {
        StartGame();
        PlayGame();
        EndGame();
        Console.WriteLine("Match completed thanks for attending!");
    }
}

public class AustralianRulesFootballMatch : FootballMatch
{
    public void StartGame()
    {
        Console.WriteLine("Siren blows to begin game….");
    }

    public void EndGame()
    {
        Console.WriteLine("Siren blows to end game….");
    }
}

public class SoccerFootballMatch : FootballMatch
{
    public void StartGame()
    {
        Console.WriteLine("Referee blows whistle to begin game….");
    }

    public void EndGame()
    {
        Console.WriteLine("Referee blows whistle to end game….");
    }
}

 

What about Functions? And how about runtime implementer?

As described above, the implementing classes are defined at design time as concrete derived classes. Doing this in Languages such as Python and Ruby seems a little more flexible in as much that I don’t really require the derivation through an abstract class.

An IronPython Implementation

class RestuarantEndToEndService:

    def __init__(self, servicename, m1, m2, m3, m4):
        self._serviceName = servicename
        self._greetAndQualifyCustomer = m1
        self._takeCustomerOrder = m2
        self._serveTheCustomerMeal = m3
        self._takePayment = m4

    def prepareTheMeal(self):
        print Chef prepares the meal

    def performService(self):
        print Doing %(svc)s Service % {svc : self._serviceName}
        self._greetAndQualifyCustomer()
        self._takeCustomerOrder()
        self.prepareTheMeal()
        self._serveTheCustomerMeal()
        self._takePayment()

def seatCustomer():
    print Greet and seat the customer at their table

def answerOrdersPhoneLine():
    print Answer the phone and take the customer details

def takeCustomerOrder():
    print Waiter / Waitress takes the customers order

def takeCustomerPhoneOrder():
    print Take the customers order by phone

def serveTheCustomer():
    print Waiter / Waitress serves customer their meal

def deliverTheCustomerPhoneOrder():
    print Driver delivers the customers phone order

def takeCustomerPaymentAtCounter():
    print Cashier takes customer payment

def takeCustomerPaymentOnDelivery():
    print Driver takes customer payment

inhouseService = RestuarantEndToEndService(In House,
                    seatCustomer,
                    takeCustomerOrder,
                    serveTheCustomer,
                    takeCustomerPaymentAtCounter)
deliveryService = RestuarantEndToEndService(Delivered,
                    answerOrdersPhoneLine,
                    takeCustomerPhoneOrder,
                    deliverTheCustomerPhoneOrder,
                    takeCustomerPaymentOnDelivery)

inhouseService.performService()
print ***********************
deliveryService.performService()

The DLR, Interop and shifting the code around

What’s a little more interesting is using this pattern in interop scenarios when the templated class is defined in a statically typed language and the template method is supplied by a dynamic language created for the DLR. This can be demonstrated here with the given abstract class and by using my IronPython WPF interactive console control.

public abstract class TemplateClass
{
    public abstract void Operation1();
    public abstract void Operation2();

    /// <summary>
    /// The Tempate Method
    /// </summary>
    public void TheTemplateMethod()
    {
        Operation1();
        Operation2();
    }
}

ip_swap_abstract_from_csharp

Using the Python Help function we can examine the abstract template class and see how it’s been defined. Looking at the printed help on the class it’s clear that the two abstract methods have ‘tagged along for the ride’ as expected. You wont be able to execute them until you provide an implementation, just as you would expect given it’s an abstract class.

ip_swap_abstract_from_csharp_help

The other scenario involves moving some statically typed code (a method) to the IronPython runtime and using an Action / Delegate to  ‘fill in’ the template algorithm in a dynamically typed class. For example in the EndToEndRestuarantService class defined in Python above, we could push the actions (delegates) over from C# to the IronPython runtime. Given a set of Functions defined in managed C#:

var actions = new Dictionary<string, object>();
actions.Add("seat", seat_customer);
actions.Add("order", take_customerOrder);
actions.Add("serve", serve_the_customer);
actions.Add("pay", take_customer_payment_at_counter);

When you push these functions over the Script Scope running the IronPython code, then you can pass those functions around to any other code running within the same scope. With our Restaurant example we can take our named functions from the C# code at runtime and push them into the constructor of our IronPython class:

ip_swap_abstract_from_csharp_func_constructs

When is this pattern useful?

These patterns can be useful in scenarios where you might be considering scripting your managed C# application and providing DLR supported scripting functionality to the application as well. It can also be quite useful when you want to expose some technology that does not currently support full interop (Entity Framework for example) or perhaps you want to make our application model visible to IronPython or IronRuby. There are of course a plethora of patterns that can be implemented in code that can traverse both environments; its really a matter of what we can devise with some imagination.

Share/Save/Bookmark

Categories: DLR, IronPython, IronRuby Tags: , ,
Creative Commons Attribution-ShareAlike 2.5 Australia
Creative Commons Attribution-ShareAlike 2.5 Australia