Blazor by Example - A spot of refactoring

February 10, 2020 · 7 minute read · Tags: blazor

Here’s where we got to last time in building a traffic light using Blazor.

TrafficLight.razor.cs

public class TrafficLightBase : ComponentBase
{
    private State _currentState = State.Stop;
    protected TrafficLightState Lights => 
        TrafficLightState.Resolve(_currentState);

    public void Toggle()
    {
        _currentState = _currentState switch
        {
            State.Stop => State.GetReadyToGo,
            State.GetReadyToGo => State.Go,
            State.Go => State.GetReadyToStop,
            State.GetReadyToStop => State.Stop,
            _ => _currentState
        };
    }
}

_currentState refers to an enum.

Every time Toggle is invoked (by clicking a button) _currentState is checked to see what state should come next.

Then, the component re-renders, at which point it will figure out which lights to show by calling TrafficLightState.Resolve, passing in the value of _currentState.

Unleash Blazor's Potential

Blazor promises to make it much easier (and faster) to build modern, responsive web applications, using the tools you already know and understand.

Subscribe to my Practical ASP.NET Blazor newsletter and get instant access to the vault.

In there you'll find step-by-step tutorials, source code and videos to help you get up and running using Blazor's component model.

I respect your email privacy. Unsubscribe with one click.

    Death to the enum

    The enum here feels a little disconnected from our business logic. We’re having to maintain its current value in a field, just so we can figure out:

    • What state we’re currently in
    • What state should come next

    But this feels like core business logic, and potentially something which we shouldn’t have to keep track of in addition to the traffic light state object itself.

    One way to remove an enum, is to make better use of objects, opening the doors to employ polymorphism to trigger different behaviours.

    In this case the question is, if we ditch the enum, how do we know which state we’re looking at? (and which one comes next?)

    One way, is to use C#’s new pattern matching capabilities.

    A word on pattern matching with C#

    Pattern matching is all about testing the “shape” of a value.

    If it’s this shape, do one thing, if it’s that shape, do something else.

    As humans we pattern match all the time. When you look at a banana you recognise it’s shape and appearance, and you know how to peel it.

    You know it’s not an apple, but if it were an apple, you’d recognise that and be able to apply the correct peeling technique to that too.

    In practice, if and switch statements are pattern matching algorithms; the newest versions of C# offer some new tools to make pattern matching easier/more flexible.

    Specifically, it’s now possible to both check for the type of something, and get a reference to the object as that type at the same time.

    switch(shape){
        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;    
    }
    

    (see the official docs for this example in more detail)

    Before you would have had to check the type, then cast the object to that type; this approach condenses those two steps into one line in the switch statement.

    Also (and we won’t use this here, but it’s useful to know) you can use when clauses to further adjust your logic based on specific aspects of the object in question.

    switch(shape){
        case Square s when s.Side == 0;
        case Circle c when c.Radius == 0;
            return 0;
    
        case Square s when:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;    
    }
    

    Finally, we can combine this with the new switch expression syntax…

        var result = shape switch
        {
            Square s when s.Side == 0 => 0,
            Circle c when c.Radius == 0 => 0,
            Square s => s.Side * s.Side,
            Circle c => c.Radius * c.Radius * Math.PI,
        }
    

    One downside here seems to be that you can’t easily express multiple tests resulting in the same answer. Our switch statement above had tests for c.Radius and s.Side both resulting in the answer being returned as 0.

    Alas, in the switch expression we have to write those both out as two separate test/results…

        Square s when s.Side == 0 => 0,
        Circle c when c.Radius == 0 => 0,
    

    A strategy for removing the enum

    So with these pattern matching tools available to us, my plan is to represent the different traffic light states as specific types.

    We can then pattern match on these types to establish what the new state should be when we attempt to cycle the traffic light to its next state.

    First up, we need to create different classes to represent the possible states of our traffic light (doing away with TrafficLightState which we had previously).

    public class StopState : ITrafficLightState
    {
        public bool RedOn { get; } = true;
        public bool AmberOn { get; } = false;
        public bool GreenOn { get; } = false;
    }
    
    public class GetReadyToGoState : ITrafficLightState
    {
        public bool RedOn { get; } = true;
        public bool AmberOn { get; } = true;
        public bool GreenOn { get; } = false;
    }
    
    public class GoState : ITrafficLightState
    {
        public bool RedOn { get; } = false;
        public bool AmberOn { get; } = false;
        public bool GreenOn { get; } = true;
    }
    
    public class GetReadyToStopState : ITrafficLightState
    {
        public bool RedOn { get; } = false;
        public bool AmberOn { get; } = true;
        public bool GreenOn { get; } = false;
    }
    

    Note they all implement the same interface…

    public interface ITrafficLightState
    {
        bool RedOn { get; }
        bool AmberOn { get; }
        bool GreenOn { get; }
    }
    

    Now, we can use pattern matching on the type, to figure out which one we currently have, and which should come next…

    TrafficLight.razor.cs

    public class TrafficLightBase : ComponentBase
    {
        protected ITrafficLightState Lights { get; set; } = new StopState();
    
        protected void Toggle()
        {
            Lights = Lights switch
            {
                StopState _ => new GetReadyToGoState(),
                GetReadyToGoState _ => new GoState(),
                GoState _ => new GetReadyToStopState(),
                GetReadyToStopState _ => new StopState(),
                _ => throw new ArgumentOutOfRangeException(nameof(Lights))
            };
        }
    }
    

    Incidentally, the _ here is a discard variable. It’s a temporary, dummy variable that can’t be used anywhere else in your code. We could have used regular variables here…

    StopState s => new GetReadyToGoState(),
    

    But because we’re not actually using that assigned variable anywhere, it’s neater in this case to use the discard pattern. That way, any attempt to retrieve its value or assign a value to it will generate a compiler error. This makes explicit, we don’t care about the value.

    Now, our Lights property starts off with an instance of StopState.

    When we invoke the Toggle method, we use C# pattern matching (and the new switch expression syntax) to determine the type of the current value of Lights.

    For example, if the type is StopState (which it will be when we first render the component) we’ll instantiate an instance of GetReadyToGoState and assign that to Lights.

    At this point, because our markup is bound to Lights the UI will be updated accordingly.

    What do we gain?

    So this works, but do we gain anything, or does it needlessly add complication to the code?

    Well, now we’re using objects, we can implement different behaviour for each of our traffic light states by updating the relevant class.

    We can also add new states quite easily, by declaring a new implementation of ITrafficLightState and amending our switch expression accordingly.

    In that sense, we are adhering to the “Open Closed Principle” in that we can create new classes and drop them in, without having to modify any of the other implementations.

    We’re also storing this state in one place Lights, without needing a separate field (our enum value) to figure out what state we’re in.

    So overall, it feels like we’re gaining more than we lose.

    Check out the complete source code for the refactored traffic light here.

    Unleash Blazor's Potential

    Blazor promises to make it much easier (and faster) to build modern, responsive web applications, using the tools you already know and understand.

    Subscribe to my Practical ASP.NET Blazor newsletter and get instant access to the vault.

    In there you'll find step-by-step tutorials, source code and videos to help you get up and running using Blazor's component model.

    I respect your email privacy. Unsubscribe with one click.

      Next up

      Finally! Improved Blazor Server reconnection UX
      .NET 9 changes how your Blazor Server app behaves when server connection is lost
      .NET 9 improves JavaScript module importing for Blazor
      .NET 9 ensures your users always get the latest version of your JS modules
      How to use .NET 9 to ensure users always get the latest version of your stylesheets
      .NET 9 changes how static files are served, and it solves a long-standing problem