How to create template lambda functions for the configuration of a class with a lot of property / setter permutations.
In some situations you may want to provide some interface flexibility that allows the customisation of properties / setters of a class like
System.Windows.Controls.Button .... or you simply may just want better control over how your class objects properties and setters are configured. Templating functions are a simple way to package these for later application.
To illustrate the possibilities for templating styles; I'm going to use the
TestObjectCI in the previous posts to illustrate how this can done. As you should see this contrived class has a number of properties, and a few setter methods, including that of its dependency that can be configured.
Composing with void returns method calls (setters / properties assignments)
When configuring these properties or setter methods, we are typically engaging with void returning method calls. Which as you might know are not typically suitable for standard composition.
Let's look at a few contrived examples of different template lambda functions for the configuration of our
TestObjectCI instance:
PHP:
Action<TestObjectCI> bgTemplate = o => {
o.drawBackground = false;
o.titleMessage = "Hello, Pluto!";
};
Action<TestObjectCI> bgTemplate1 = o => {
o.drawBackground = false;
o.titleMessage = "Hello, Mars!";
};
Action<TestObjectCI> bgTemplate2 = o => {
o.drawBackground = false;
o.titleMessage = "Hello, Jupiter!";
};
Above we have 3 lambda functions that take a single parameter; and instance of
TestObjectCI and apply some property changes.
Now let's define another lambda function to enable the border property:
PHP:
Action<TestObjectCI> boTemplate = o => {
o.drawBorder = true;
};
Let's see how we'd use these template functions:
PHP:
var test = new TestObjectCI(123, new Dependency());
bgTemplate(test); // drawbackground = false, titleMessage = "Hello, Pluto!"
boTemplate(test); // drawBorder = true
That certainly isn't much more elegant than just setting the properties directly.
Question now is how can we concatenate these templates
In order to create a composite lambda function that will not only disable the
drawBackground, set the
titleMessage but will also enable
drawBorder.
Without of course having create another new Lambda function.
Why? Think of the permutations with a class with a lot of styling properties
How could you provide flexibility of styling if you were limited to the permutations of lambda functions you had created beforehand. Boilerplate nightmare scenario.
There is of course a better solution for this
We can work around this void returning method / setter composition problem by creating some extension methods that will help us concatenate two void returning functions.
PHP:
static class ExtensionTemplateMethods {
// Concatenate the two Unary non value returning functions
// By apply the first followed by the se
// (A) -> void + (A) -> void i.e. two Action<A> functions
public static Action<A> Concat<A>(this Action<A> f, Action<A> g)
{
return (a) => {
f(a);
g(a);
};
}
// Pipe a value into a Unary Non Value returning function and return the result
// i.e. Apply an Action<A> to A and return the result.
// Mutable pipe; returns a reference to the input object
public static A Pipe<A>(this A a, Action<A> f)
{
f(a);
return a;
}
}
Let's see how we use this to concatenate templates and apply it to a target object:
First let's define a few more templates
PHP:
Action<TestObjectCI> shadowTemplate = o => {
o.setDrawShadow(false);
};
Action<TestObjectCI> stuffTemplate = o => {
o.dependency.setStuff(33);
};
Now let's concatenate the
bgTemplate with the
boTemplate and the
stuffTemplate and store this combination as
bgBoShadowTemplate
PHP:
var bgBoShadowTemplate = bgTemplate.Concat(boTemplate).Concat(stuffTemplate);
... let's apply it to our
test instance.
PHP:
test.Pipe(bgBoShadowTemplate);
This of course can all be done at the same time in 1 instruction:
PHP:
test.Pipe(bgTemplate1.Concat(boTemplate).Concat(shadowTemplate));
This is all being affected to the mutable state of
test object i.e. 1 change will potentially overwrite another.
How to avoid this is of course a subject of immutability; which is a new post that will have to wait until tomorrow.