14
\$\begingroup\$

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;
    }
}
\$\endgroup\$
  • \$\begingroup\$ I've edited your question to provide a minimum of context. Feel free to further edit and describe each class; see How to get the best value of Code Review on meta. \$\endgroup\$ – Mathieu Guindon Aug 13 '15 at 1:45
  • 1
    \$\begingroup\$ Grammatical Nitpick: It should be "{0} has split the enemy in two" rather than "{0} has splitted the enemy in two". Similarly, "{0} has hit the enemy's heart" rather than"<whoever> has hitted the enemy's heart". And "{0} has converted the enemy in monkey" should either be "{0} has converted the enemy into a monkey" or better yet: "{0} has transformed the enemy into a monkey". \$\endgroup\$ – Tory Aug 13 '15 at 18:56
20
\$\begingroup\$

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.

\$\endgroup\$
  • 2
    \$\begingroup\$ Wow. I learned quite a lot from reading this. Thank you! I might even apply this in the future. \$\endgroup\$ – Ismael Miguel Aug 13 '15 at 8:34
  • 3
    \$\begingroup\$ I would add to what Mat's saying that if you are going to use underscore notation, prefacing every access of an instance variable with this. as you've done in CharacterBuilder is defeating the point. \$\endgroup\$ – Nick Udell Aug 13 '15 at 13:29
  • \$\begingroup\$ Hey, thanks so much, I have learned so much about it. I have changed the code with all reviews, should I edit the main post? \$\endgroup\$ – Edson Alcalá Aug 14 '15 at 4:53
  • 1
    \$\begingroup\$ @EdsonAlcalá don't edit the updated code into the question, that would be against site policy. Instead, ask a new question - more votes for everyone! \$\endgroup\$ – Mathieu Guindon Aug 14 '15 at 5:05
8
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Hey, thanks for you comment, do you think you can explain a little more why the Item, in this case the Sword should have a property of type ICharacter? How the code will looks like, I mean't to, how the Owner will use it. \$\endgroup\$ – Edson Alcalá Aug 14 '15 at 4:50
6
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Hello, thanks for your comment, can you clarify "I would create a seperate Interface/Class for defining the character action methods and give the class constructor an ICharacter parameter." \$\endgroup\$ – Edson Alcalá Aug 14 '15 at 4:47
  • \$\begingroup\$ I was just pointing out that, defining the actions inside the character classes is a bad practice. Always try to seperate data containing classes and method containing classses from each each other. Its up to you to decide how to define the method containing classes. For example you could make it dependant to an item or a character type or both! You implement that dependency by defining the constructor that need the parameter of the type you want the dependency with. \$\endgroup\$ – MrErdalUral Aug 14 '15 at 12:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.