The following code and tests work towards the same outcome as achieved in the TypeScript implementation of the Simple Factory pattern.
The aim here is to compare and contrast against the TypeScript code whilst learning C#.
namespace DesignPatterns.Creational;
public enum VehicleType
{
Car,
Bike,
}
public interface IVehicle
{
public string Move();
}
internal class Car : IVehicle
{
public string Move()
{
return "Driving a car...";
}
}
internal class Bike : IVehicle
{
public string Move()
{
return "Riding a bike...";
}
}
public static class SimpleFactory
{
public static IVehicle CreateVehicle(VehicleType vehicleType)
{
return vehicleType switch
{
VehicleType.Car => new Car(),
VehicleType.Bike => new Bike(),
_ => throw new ArgumentException("Invalid vehicle type.")
};
}
}
Code language: C# (cs)
And the associated NUnit tests:
using System;
using DesignPatterns.Creational;
using NUnit.Framework;
namespace DesignPatternsUnitTests.Creational;
public class Tests
{
[Test]
public void ShouldCreateACar()
{
var vehicle = SimpleFactory.CreateVehicle(VehicleType.Car);
Assert.AreEqual(vehicle.Move(), "Driving a car...");
}
[Test]
public void ShouldCreateABike()
{
var vehicle = SimpleFactory.CreateVehicle(VehicleType.Bike);
Assert.AreEqual(vehicle.Move(), "Riding a bike...");
}
[Test]
public void ShouldThrowForAnInvalidVehicleType()
{
Assert.Throws<ArgumentException>(() =>
SimpleFactory.CreateVehicle((VehicleType)999), "Invalid vehicle type");
}
}
Code language: C# (cs)
Mostly this code is very similar to the TypeScript approach.
However, there are a few interesting differences to cover.
public enum VehicleType
Both C# and TypeScript support defining enums using the enum
keyword.
During this process I learned that in C# you can define the underlying type of an enum
, for example setting the type to byte
, or short
, or int
.
By default, int
is used.
In TypeScript, there is only number
.
Taking the enum
from the code above, I could specify the underlying type to be short
as follows:
public enum VehicleType : short
{
Car,
Bike,
}
Code language: C# (cs)
I’m not really sure why I might want or need to do this, but it’s interesting to know all the same.
I do know a short
goes from -32,768 to 32,767… so it seems unlikely I’d ever actually need the full range of values that an int
provides. But hey, who am I to make wild claims like that at this stage?
What I do know is that I cannot use string
values as the underlying type, like I can in TypeScript:
Keeping Implementations internal
Rather than spilling out the guts / internals of my Car
and Bike
classes, I made use of the internal
keyword when defining my classes:
internal class Car : IVehicle
{
public string Move()
{
return "Driving a car...";
}
}
Code language: C# (cs)
My theory here is that if I am masking away the object creation behind the SimpleFactory
, I might not want to expose the classes directly.
As best I understand it,internal
will mean only code within my current Project would be able to directly instantiate new
instances of those classes.
However I did have to make the IVehicle
and VehicleType
both public
. Otherwise, how would outside callers be able to work with the code?
Pattern Matching Using switch
Hands down my favourite part of this code is the switch
expression.
There’s two reasons I love this:
- Being able to directly
return
theswitch
outcome, and; - Pattern matching on the
VehicleType
value
Pattern matching itself is a really nice feature I first learned about in Elixir. That is one thing I really, really hope comes to TypeScript / JavaScript some time soon.
Direct returns from a switch expression are just lovely.
Unlike the exhaustive switch approach from TypeScript, we have the two matches and then the default case (_
=> …) which acts as our catch all.
The one downside here is that adding in a new value to the enum
does not blow everything up:
Talk about subtle. A tiny green (my eyes!) line under the “s”. Not hard to miss at all.
There is perhaps a better way here, so if you know it, please shout up in the comments.
Requiring Assert.Throws
As a result of not being able to implement an exhaustive switch
, I have had to re-introduce a unit test I was able to eliminate in the TypeScript code:
[Test]
public void ShouldThrowForAnInvalidVehicleType()
{
Assert.Throws<ArgumentException>(() =>
SimpleFactory.CreateVehicle((VehicleType)999), "Invalid vehicle type");
}
Code language: C# (cs)
In many ways this is like the Jest test code that works with exceptions.
The code that is expected to fail is wrapped in a lambda. The outcome of that lambda is then, somehow, used by NUnit to determine that things went wrong as expected.
The second optional argument is the error message I expect.
But the trickiest part of this code was in figuring out how to provide a bad value to the CreateVehicle
method.
Given that I had defined that CreateVehicle
accepts the VehicleType
enum, and I only have two allowable values in there, I had to figure out how to trick the code into running, in order to get the fail I desired.
What I’m doing here is casting any random value – 999 in this case – to be a VehicleType
. That was sheer luck really. I figured hey, I just learned an enum is int
underneath, can I trick it somehow by pretending any int
is a valid value in that enum. And it worked.
Sometimes I get lucky.