RPG Character Builder

Posted on

Problem

This implements a builder pattern for RPG game characters. Is this a valid builder pattern implementation?

  • There are 3 characters: Paladin, Wizard and Elfo.
  • There are 3 items: Potion, Sword, Arch.
using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace RPGEngine.Tests
{
[TestFixture]
public class RPGEngineTest
{
    [Test]
    public void GivenAPaladin_WithSword_Return()
    {
        //Arrange
        var paladin = new
            CharacterBuilder()
            .Init(new Paladin())
            .WithSword()
            .Named("Conan")
            .Build();

        //Act
        var result = paladin.Attack();

        //Assert                    
        Assert.AreEqual("Conan has splitted the enemy in two", result);
    }

    [Test]
    public void GivenAPaladin_WithArch_Return()
    {
        //Arrange
        var paladin = new
            CharacterBuilder()
            .Init(new Paladin())
            .WithArch()
            .Named("Conan")
            .Build();

        //Act
        var result = paladin.Attack();

        //Assert                    
        Assert.AreEqual("Conan has hitted the enemy's heart", result);
    }

    [Test]
    public void GivenAnElfo_WithArch_Return()
    {
        //Arrange
        var elfo = new
          CharacterBuilder()
          .Init(new Elfo())
          .WithArch()
          .Named("Légolas")
          .Build();

        //Act
        var result = elfo.Attack();

        //Assert                    
        Assert.AreEqual("Légolas has hitted the enemy's heart", result);
    }

    [Test]
    public void GivenAnElfo_WithPotion_Return()
    {
        //Arrange
        var elfo = new
          CharacterBuilder()
          .Init(new Elfo())
          .WithPotion()
          .Named("Légolas")
          .Build();

        //Act
        var result = elfo.Attack();

        //Act

        //Assert                    
        Assert.AreEqual("Légolas has converted the enemy in monkey", result);
    }
    [Test]
    public void GivenAWizard_WithPotion_Return()
    {
        //Arrange
        var Wizard = new
          CharacterBuilder()
          .Init(new Wizard())
          .WithPotion()
          .Named("Merlín")
          .Build();

        //Act
        var result = Wizard.Attack();

        //Assert                    
        Assert.AreEqual("Merlín has converted the enemy in monkey", result);
    }
    [Test]
    public void GivenAWizard_WithSword_Return()
    {
        //Arrange
        var Wizard = new
          CharacterBuilder()
          .Init(new Wizard())
          .WithSword()
          .Named("Merlín")
          .Build();

        //Act
        var result = Wizard.Attack();

        //Assert                    
        Assert.AreEqual("Merlín has splitted the enemy in two", result);
    }
}
public static class Message
{
    private const String MessageForSword = "{0} has splitted the enemy in two";
    private const String MessageForArch = "{0} has hitted the enemy's heart";
    private const String MessageForPotion = "{0} has converted the enemy in monkey";

    private static Dictionary<AttackType, String> messageTypes = new Dictionary<AttackType, String>()
    {
        { AttackType.Sword , MessageForSword },
        { AttackType.Arch, MessageForArch },
        { AttackType.Potion, MessageForPotion }
    };
    public static string GetResultOf(AttackType typeOfAttack)
    {
        return messageTypes[typeOfAttack];
    }
}
public enum AttackType
{
    Sword,
    Arch,
    Potion
}
public interface Item
{
    string Use();
}
public class Sword : Item
{
    public string Use()
    {
        return Message.GetResultOf(AttackType.Sword);
    }
}
public class Arch : Item
{
    public string Use()
    {
        return Message.GetResultOf(AttackType.Arch);
    }
}
public class Potion : Item
{
    public string Use()
    {
        return Message.GetResultOf(AttackType.Potion);
    }
}
public interface ICharacter
{
    String Attack();
    Item Item { get; set; }
    String Name { get; set; }
}
public class Paladin : ICharacter
{
    public String Name { get; set; }
    public Item Item { get; set; }
    public String Attack()
    {
        return string.Format(Item.Use(), this.Name);
    }
}
public class Elfo : ICharacter
{
    public String Name { get; set; }
    public Item Item { get; set; }
    public String Attack()
    {
        return string.Format(Item.Use(), this.Name);
    }
}
public class Wizard : ICharacter
{
    public String Name { get; set; }
    public Item Item { get; set; }
    public String Attack()
    {
        return string.Format(Item.Use(), this.Name);
    }
}
public class CharacterBuilder
{
    private ICharacter _Personaje;
    public CharacterBuilder Init(ICharacter personaje)
    {
        _Personaje = personaje;
        return this;
    }
    public CharacterBuilder WithSword()
    {
        this._Personaje.Item = new Sword();
        return this;
    }
    public CharacterBuilder WithArch()
    {
        this._Personaje.Item = new Arch();
        return this;
    }
    public CharacterBuilder WithPotion()
    {
        this._Personaje.Item = new Potion();
        return this;
    }

    public CharacterBuilder Named(string name)
    {
        this._Personaje.Name = name;
        return this;
    }
    public ICharacter Build()
    {
        return this._Personaje;
    }
}

Solution

Styling Nitpicks

private ICharacter _Personaje;

I like the underscore prefix for private fields. I like it, because I don’t like plain camelCase fields for two reasons:

  • They look just like parameters and local variables
  • Disambiguating them from parameters/locals involves sprinkling redundant this qualifiers all over the code, which I find amounts to annoying junk.

So I like the underscore prefix you put there. What I don’t like though, is that the field is PascalCase with an underscore. Private fields should be camelCase, or _camelCase.

One more nitpick: string is a C# language alias for the System.String class. Both are completely equivalent, in absolutely every single possible situation. I can live with either being used. I can even live with string being used as a type in declarations and String being used when calling string methods such as String.Format. But inconsistency is annoying:

public interface Item
{
    string Use();
}

public interface ICharacter
{
    String Attack();
    Item Item { get; set; }
    String Name { get; set; }
}

public String Attack()
{
    return string.Format(Item.Use(), this.Name);
}

I’d just use string everywhere.


Builder Review

public CharacterBuilder Init(ICharacter personaje)
{
    _Personaje = personaje;
    return this;
}

So wait a minute…

var builder = new CharacterBuilder().WithSword();

This would throw a rather surprising NullReferenceException, because the Init method wasn’t called and the private field isn’t initialized. How about constructor-injecting the ICharacter instance instead?

var character = new Wizard();
var builder = new CharacterBuilder(character).WithSword();

And now the private field can be made readonly, because it’s initialized in the constructor and the CharacterBuilder object is always in a valid state.

There’s a problem though:

var character = new CharacterBuilder(new Wizard())
    .WithSword()
    .WithPotion()
    .WithArch()
    .WithPotion()
    .WithPotion()
    .Build();

What am I expecting here? And what am I getting? An unnamed wizard with 1 potion. And I thought I’d be getting an unnamed wizard archer with a sword and 3 potions.

The problem is that ICharacter can only ever have 1 name and 1 item: this isn’t a very good scenario for a builder pattern.

I would suggest to change the interface a bit:

public interface ICharacter
{
    string Name { get; }
    Item Item { get; set; }
    string Attack();
}

So that you could have:

public class Paladin : ICharacter
{
    public Paladin(string name, Item item)
    {
        Name = name;
        Item = item;
    }

    public string Name { get; }
    public Item Item { get; set; }

    public string Attack()
    {
        return string.Format(Item.Use(), this.Name);
    }
}

I left Item get/set, because I would think a character’s equipment could change throughout the game (perhaps the Paladdin would use a LongSword or a MythrilSword at one point) – the point is, if there’s an instance of an object, it’s in a valid state. Always.

With your current code, this blows up with a NullReferenceException:

var player = new Paladin();
player.Attack();

Drop that builder, it’s not helping you.


Now, what would be a valid case for a builder? Say you wanted to generate a village:

var village = new VillageBuilder()
    .Building(new SmallHouse())
    .Building(new SmallHouse())
    .Building(new LargeHouse())
    .Building(new ArmorShop())
    .Building(new WeaponShop())
    .Building(new Inn())
    .Villager(new WomanInRed())
    .Villager(new MadScientist())
    .Villager(new PossessedChild())
    .Build();

You see the difference? The interface for a Village doesn’t know how many Building or Villager it’s going to have (nor what actual type they might be) – the builder pattern is useful here, because you can build villages with completely different features using the exact same builder code, and the Build method could be called at any given point, the village object would always be in a valid state.

Mat’s Mug made some really good points. I would like to add a few things.

I don’t like the Message class. What will you do when you’ll want to have a spear? A gun? 10 other weapons? 10 more items, which are not weapons? Your class will quickly grow to a point, where it will be real pain to maintain. This looks much better in my opinion:

public class Sword : Item
{
    public ICharacter Owner { get; set; }

    public string Use()
    {
        return String.Format("{0} has splitted the enemy in two", Owner);
    }
}

If you want to have a place to hold your strings – add them to .resx file. Visual Studio will generate a class to access those strings via properties.

public string Use()
{
    return String.Format(Resources.UseSwordMessage, Owner);
}

Also, interface names should have an I prefix, so it should be IItem.

I wouldn’t have defined an action method for ICharacter interface and its child classes because if you need to create new actions for them you would need to copy and paste the same code for each ICharacter type you defined.

Instead I would create a seperate Interface/Class for defining the character action methods and give the class constructor an ICharacter parameter.

Also try to keep in mind that if you want to create a more scaleable program your classses should only have one responsibility. In your case ICharacter classes have the responsibility to keep data. Give doAction kind of responsibilities to another one. And make them talk to each other. This will save you from copy/pasting your code and also you won’t need to remember which paste goes where.

It is the S of SOLID principles:
See here.

Leave a Reply

Your email address will not be published. Required fields are marked *