MVC Polymorphism – How to Decide What View to Show

mvcpolymorphism

I am making a game of Monopoly. I call a method in my Board class which returns the current players square object! E.g Old Kent Road. Euston, Chance , Free parking. I use polymorphism to decide upon the logic of what should happen depending of the state of the square! E.g I’ll have a property class where a the polymorphic method would run logic to check if it’s owned then show a view which tells the current player how much they owe. However if it is for sale then show a view which shows how much it is to buy with two options to the user 1. Buy 2. Don’t buy

However if we land on a chance, using polymorphism something totally different will occur, all I have to do in my controller or my model is call action() for the logic. It works fine.

Now. My problem

How from the model can I decide what view should be rendered ? An option is to put all the logic in the controller and get the current type of square! But that’s messy and will consist of a lot of conditionals. E.g.

If(typeOfSquare === chance ){
// get the latest chance and call a     
View method to render it with options to the user 
}

I would rather use polymorphism and have and build my logic of the square in seperate classes. E.g ChanceSquae or PropertySquare but that would mean building a property on the square called something like htmlView which will store the html that I can get from the square object and then pass into the view.

This seems like an impossible situation to deal with without getting messy!!

Best Answer

You're on the right track here about what kind of code to avoid.

If your game board works with a list of base Tile object without knowing which is which, the game board cannot reasonably judge how to display a tile since the correct display hinges on the specific tile that it is.

In this case, the board should ask the tile what to render, because the tile knows itself. (Note: I am going to do a more MVC-oriented solution afterwards, this is a basic solution to show the underlying principle at play without MVC specifics)

Something along the lines of:

public abstract class Tile
{
    public abstract Image GetImage();
}

public class FreeParkingTile : Tile
{
    public Image GetImage() => Image.FromFile("FreeParking.png");
}

public class RailwayTile : Tile
{
    // Indicates which of the four railways it is
    public RailwayEnum Type { get; }

    public Image GetImage() => this.Type switch
    {
        RailwayEnum.KingsCross  => Image.FromFile("KingsCross.png"),
        RailwayEnum.Marylebone  => Image.FromFile("Marylebone.png"),
        RailwayEnum.FenchurchSt => Image.FromFile("FenchurchStreet.png"),
        RailwayEnum.LiverpoolSt => Image.FromFile("LiverpoolStreet.png"),
    };

    // Alternatively, if the filename matches the enum:
    public Image GetImage() => Image.FromFile($"{Type}.png");
}

Now, your game board doesn't need to know the specific tiles, because it can just call myTile.GetImage() and be done with it. This maintains the intentional ignorance of the board logic, ensuring that it never needs to know any specifics about any tile in particular.


Since you're working in MVC, instead of returning an image you could be returning a partial view path. This SO answer shows you how it can be done (I leave the adjustments to the above code I added as an exercise).

Adapted for your use case:

@foreach (var tile in Model.Tiles)
{
    Html.RenderPartial(tile.PartialViewPath)
}

Another possible solution, this SO answer shows you how you can use MVC's DisplayFor to your advantage, by building display templates for each individual tile type and then having DisplayFor render the correct template for the correct tile.

Adapted for your use case:

@page
@model MonopolyBoardModel

@foreach (var tile in Model.Tiles)
{
    @Html.DisplayFor(t => tile, tile.GetType().Name)
}

I personally prefer the DisplayFor solution, but as you can see there are several ways in which you can tackle this.