This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.
Title
Thinking about the null-object pattern
Description
I had never thought of an <c>if</c> statement as a type-check until a Smalltalk programmer explained it to me in this video. She explained how Smalltalk has six keywords---according to <a href="https://en.wikipedia.org/wiki/Smalltalk">Wikipedia</a>, they're <c>true</c>, <c>false</c>, <c>nil</c>, <c>self</c>, and <c>super</c>, but her list had <c>thisContext</c> on it as well<fn>---and you can get rid of conditions and turn them into message-passing instead, <i>as God intended</i>.
<media href="https://www.youtube.com/watch?v=OMPfEXIlTVE" src="https://www.youtube.com/v/OMPfEXIlTVE" source="YouTube" width="560px" author="Sandi Metz" date="May 1, 2015" caption="RailsConf 2015 - Nothing is Something">
From the official video description,
<bq>Our code is full of hidden assumptions, things that seem like nothing, secrets that we did not name and thus cannot see. These secrets represent missing concepts and this talk shows you how to expose those concepts with code that is easy to understand, change and extend. <b>Being explicit about hidden ideas makes your code simpler, your apps clearer and your life better. Even very small ideas matter. Everything, even nothing, is something.</b></bq>
<h>The null-object pattern</h>
<img attachment="null-object_pattern.webp" align="right" caption="Null-object pattern">I often use sentinel (or placeholder) objects so that I don't have to query a condition, like <c>if (a == null) { }</c>. Instead, as Sandi says, you just <iq>pass a message</iq>. She calls it the <iq>null-object pattern</iq> or an <iq>active nothing</iq>. Fine, cool. Lots of names for it.
As she also noted, you don't get <i>rid</i> of the conditional, but you <i>move</i> it to the place where the decision <i>should</i> be made, rather than propagating the decision to every caller or dependency.
She spent a lot of time on it, but it's basically about the following pattern, which is drastically simplified from what you'd probably find in the wild.
<code>
interface IAnimal
{
public string Name { get; }
}
class Animal : IAnimal
{
public string Name { get; init; }
}
List<ianimal> animals = [new Animal { Name = "Pig" }, null, new Animal { Name = "Cow" }];
foreach (var animal in animals)
{
<hl>if (animal != null)</hl>
{
Console.WriteLine(animal.Name);
}
<hl>else</hl>
{
Console.WriteLine("no animal");
}
}</code>
The <hl>condition</hl> is the problem, because every client of that list has to deal with the possibility of <c>nulls</c>. One way to handle it would be to move the condition out of the (obvious) loop, eliding <c>nulls</c> from the list, as shown below.
<code><hl>var actualAnimals = animals.Where(a => a != null);</hl>
foreach (var animal in <hl>actual</hl>Animals)
{
Console.WriteLine(animal.Name);
}</code>
You still have the conditional, of course, but you're also handling it just <i>once</i> and then letting the rest of your code be free of needing to deal with possible <c>nulls</c>.
However, this <i>hides</i> the <i>length</i> of the original list, which is not always what you want. What if you want to represent the "empty" slots? What if, as the talk is called, <iq>Nothing is Something</iq>? Then you would use the <iq>null-object pattern</iq>.
<code><hl>class MissingAnimal : IAnimal
{
public Name => "no animal";
}</hl>
var actualAnimals = animals.<hl>Select</hl>(a => a <hl>?? new MissingAnimal()</hl>);
foreach (var animal in actualAnimals)
{
Console.WriteLine(animal.Name);
}</code>
Voila.
<h>Composition beats inheritance</h>
In the second act of this 36-minute talk, she demonstrates how to use composition rather than inheritance by ruthlessly applying the single-responsibility principle. She starts with a simple-looking class that returns some data.
<code>class Thing
{
private IEnumerable<string> _data;
public Thing(IEnumerable<string> data)
{
_data = data ?? throw new ArgumentNullException(nameof(data));
}
public IEnumerable<string> Data => _data;
}</code>
She then shows how you can use inheritance to make two descendants, one of which returns the data in a random order and another that returns the data with each entry doubled.
<code>class Thing
{
private IEnumerable<string> _data;
public Thing(IEnumerable<string> data)
{
_data = data ?? throw new ArgumentNullException(nameof(data));
}
public <hl>virtual</hl> IEnumerable<string> Data => _data;
}
class RandomThing
{
public override IEnumerable<string> Data => base.Data.Shuffle();
}
class DoubleThing
{
public override IEnumerable<string> Data => base.Data.Zip(data, (x, y) => new[] { x, y }).SelectMany(o => o).
}</code>
Now try to make one that returns the data in a random order and doubles each entry. Don't repeat yourself.
With inheritance, you're quickly <a href="https://www.youtube.com/watch?v=nh3lICcIVrE">in a tight spot</a>.
The thing to remember is that you've now introduced two new features to <c>Things</c>, which kind of slipped in there: <c>RandomThing</c> <i>orders</i> the data but does not <i>transform</i> it, whereas <c>DoubleThing</c> <i>transforms</i> the data but doesn't touch the <i>order</i>.
It sounds like the <c>Thing</c> now has <i>two</i> responsibilities, i.e., it addresses two <i>concerns</i>.
The answer is to separate out these two concerns into components and then to inject those components into the <c>Thing</c>. It's always the same answer. It's boring, right? Boring is good.
This is an intermediate step, to illustrate the simplest form of composition, with the fewest changes. It's going to be more code than we'd like, but let's go ahead and write it.
<code>class Transformer
{
public virtual IEnumerable<string> Transform(IEnumerable<string> data) => data;
}
class Doubler : Transformer
{
public override IEnumerable<string> Transform(IEnumerable<string> data)
{
return data.Zip(data, (x, y) => new[] { x, y }).SelectMany(o => o);
}
}
class Sorter
{
public virtual IEnumerable<string> Sort(IEnumerable<string> data) => data;
}
class Shuffler : Sorter
{
public override IEnumerable<string> Sort(IEnumerable<string> data) => data.Shuffle();
}
class Thing(IEnumerable<string> data, Transformer transformer, Sorter sorter)
{
public IEnumerable<string> Data => sorter.Sort(transformer.Transform(data));
}
new Thing(["A", "B", "C"], new Doubler(), new Shuffler());</code>
This is immediately obviously suboptimal First of all, we should recognize that changing the order and transforming the data aren't different operations. They're both functions on a sequence that return another sequence. Instead of passing in a <c>Sorter</c> and a <c>Transformer</c>, as in the example in the video, we could instead pass in a sequence of transfomers to apply.
<code>class Transformer
{
public virtual IEnumerable<string> Transform(IEnumerable<string> data) => data;
}
class Doubler : Transformer
{
public override IEnumerable<string> Transform(IEnumerable<string> data)
{
return data.Zip(data, (x, y) => new[] { x, y }).SelectMany(o => o);
}
}
class Shuffler : <hl>Transformer</hl>
{
public override IEnumerable<string> <hl>Transform</hl>(IEnumerable<string> data) => data.Shuffle();
}
class Thing(IEnumerable<string> data, IEnumerable<transformer> transformers)
{
public IEnumerable<string> Data => <hl>transformers.Aggregate(data, (current, t) => t.Transform(current))</hl>;
}
new Thing(["A", "B", "C"], [new Doubler(), new Shuffler()]);</code>
Another thing we can notice is how rigid this all is in the type of the item. Let's make this a more generalized pattern.
<code>class Transformer<<hl>T</hl>>
{
public virtual IEnumerable<<hl>T</hl>> Transform(IEnumerable<<hl>T</hl>> data) => data;
}
class Doubler<<hl>T</hl>> : Transformer<<hl>T</hl>>
{
public override IEnumerable<<hl>T</hl>> Transform(IEnumerable<<hl>T</hl>> data)
{
return data.Zip(data, (x, y) => new[] { x, y }).SelectMany(o => o);
}
}
class Shuffler<<hl>T</hl>> : Transformer<<hl>T</hl>>
{
public override IEnumerable<<hl>T</hl>> Transform(IEnumerable<<hl>T</hl>> data) => data.Shuffle();
}
class Thing<<hl>T</hl>>(IEnumerable<<hl>T</hl>> data, IEnumerable<transformer<<> T</hl>>> transformers)
{
public IEnumerable<<hl>T</hl>> Data => transformers.Aggregate(data, (current, t) => t.Transform(current));
}
new Thing<hl><string></hl>(["A", "B", "C"], [new Doubler<hl><string></hl>(), new Shuffler<hl><string></hl>()]);</code>
Note that now we have all of our logic independent of the type of item in the sequences. It's only in creating the <c>Thing</c> that you decide on the item type.
The <c>Transformer</c> is called a <i>functional interface</i>---i.e., an interface with a single function---which would be type-compatible with a function signature in Java, but still isn't in C#. It's kind of clunky and repeats a bunch of code. Can we get rid of it? Can we also get rid of the dynamic dispatch (i.e., the <c>virtual</c> and <c>override</c>)?
Sure, we can.
<code>class Thing<t>(IEnumerable<t> data, IEnumerable<func> , IEnumerable<t>>> transformers)
{
public IEnumerable<t> Data => transformers.Aggregate(data, (current, t) => t(current));
}
new Thing<string>(["A", "B", "C"], [<hl>data => data.Zip(data, (x, y) => new[] { x, y }).SelectMany(o => o), Shuffle</hl>]);</code>
Well, that's a lot less code, but it's a bit messy at the declaration point. One nice thing, though, is that we're only declaring the item type once now, in the type parameter to <c>Thing</c>.
We can clean that up a bit but we're going to be limited by the requirement to specify the type parameter as soon as we leave the constructor of the <c>Thing</c>. The <c>Shuffle</c> part is succinct enough but the <c>Double</c> part isn't at all obvious.
How about we encode the types in a static method call?
<code><hl>public static class ThingTools
{
public static IEnumerable<t> Double<t>(this IEnumerable<t> data)
{
return data.Zip(data, (x, y) => new[] { x, y }).SelectMany(o => o);
}
}</hl>
new Thing<string>(["A", "B", "C"], [<hl>ThingTools.Double</hl>, Shuffle]);</code>
That's quite a bit better. Now that we already have a helper class, we can keep improving things by making another helper method that allows us to create a <c>Thing</c> by passing in a collection of items without specifying the item type explicitly. Instead, the item type is picked up from the <c>data</c> passed in.
<code>public static class ThingTools
{
public static IEnumerable<t> Double<t>(this IEnumerable<t> data)
{
return data.Zip(data, (x, y) => new[] { x, y }).SelectMany(o => o);
}
<hl>public static Thing<t> Create<t>(IEnumerable<t> data, IEnumerable<func> , IEnumerable<t>>> transformers)
{
return new Thing<t>(data,transformers);
}</hl>
}
<hl>ThingTools.Create</hl>(["A", "B", "C"], [ThingTools.Double, data => data.Shuffle()]);</code>
Isn't that fun?
Did I go too far? You decide what you want to use based on your comfort level with any of the versions that use composition (don't use the ones that don't use composition).