Dependency Injection — Refactoring of the project

We are continuing the dependency injection section, in this article we will refactorize the code from the previous entry and, as we remember, that code was not very friendly to read, test and expand. Today we will try to change it :)

First we have to refactorize this code to have what to inject, so you can come to the conclusion that we use a dependency injection to and satisfy solid rules, which means that dependency injection somehow suggests that we follow these rules.

At the beginning, let’s see how the classes of the characters have changed, namely the Thief, Mage, Barbarian and Paladin classes, and as we remember in that example, these classes inherit from the concrete class and should not. I dare even think they can not, amen.

Let’s see how you can deal with it. With the examples of the corrected code I will also say what in the next article from the revised code we will still improve (a litttle bit pomp and circumstance ) already using dependency injection :)

So that you do not have to look at the previous article how these classes looked I will paste here, they looked like this:

namespace SpaghettiGameProject
{
class CharacterClass
{
public int Strength;
public int Stamina;
}
    class Barbarian : CharacterClass
{
public Barbarian()
{
this.Strength = 18;
this.Stamina = 17;
}
}
    class Paladin : CharacterClass
{
public Paladin()
{
this.Strength = 14;
this.Stamina = 14;
}
}
    class Thief : CharacterClass
{
public Thief()
{
this.Strength = 10;
this.Stamina = 12;
}
}
    class Mage: CharacterClass
{
public Mage()
{
this.Strength = 8;
this.Stamina = 6;
}
}
}

And now we changed them to something like:

namespace RefactoredGameProject
{
public interface ICharacter
{
int Strength { get; set; }
int Stamina { get; set; }
}
    public class Barbarian : ICharacter
{
private int _strength = 18;
public int Strength { get => _strength; set => _strength=value; }
        private int _stamina = 17;
public int Stamina { get => _stamina; set => _stamina = value; }
}
    public class Paladin : ICharacter
{
private int _strength = 14;
public int Strength { get => _strength; set => _strength = value; }
        private int _stamina = 14;
public int Stamina { get => _stamina; set => _stamina = value; }
}
    public class Mage : ICharacter
{
private int _strength = 8;
public int Strength { get => _strength; set=>_strength = value; }
        private int _stamina = 6;
public int Stamina { get => _stamina; set=> _stamina = value; }
}
    public class Thief : ICharacter
{
private int _strength = 10;
public int Strength { get => _strength; set => _strength = value; }
        private int _stamina = 12;
public int Stamina { get => _stamina; set => _stamina = value; }
}
}

As you can see now, these classes implement the interface, so they depend on abstraction and the variables are better insulated with the implemented interface.

Let’s move to distributing skill points after choosing a class.

The code before the changes from the previous example looks like this:

public void CreateCharacter(CharacterClass characterclass)
{
this.characterclass = characterclass;
     if (characterclass is Barbarian)
{
characterclass.Strength += 15;
characterclass.Stamina += 9;
     }
else if (characterclass is Paladin)
{
characterclass.Strength += 12;
characterclass.Stamina += 8;
     }
else if (characterclass is Mage)
{
characterclass.Strength += 7;
characterclass.Stamina += 6;
     }
else if (characterclass is Thief)
{
characterclass.Strength += 10;
characterclass.Stamina += 10;
}
}

Well, there is some tragedy 😥 this method does more than one thing, so it will be hard to understand and to expand and it is very hard to test.

And now the code after changes:

namespace RefactoredGameProject
{
public class CharacterSkillPoints
{
public Paladin paladin;
public Barbarian barbarian;
public Mage mage;
public Thief thief;
        public CharacterSkillPoints()
{
paladin = new Paladin();
barbarian = new Barbarian();
mage = new Mage();
thief = new Thief();
}
        public void GiveOutSkillPointsForPaladin()
{
paladin.Strength += 12;
paladin.Stamina += 8;
}
        public void GiveOutSkillPointsForBarbarian()
{
barbarian.Strength += 15;
barbarian.Stamina += 9;
}
        public void GiveOutSkillPointsForMage()
{
mage.Strength += 7;
mage.Stamina += 6;
}
        public void GiveOutSkillPointsForThief()
{
thief.Strength += 10;
thief.Stamina += 10;
}
}
}

Does not it look better? All instances of the character classes are separated into one class as it should be, so we have a lot easier to understand and test.

However, this is not what I want, we create instances of all classes of characters, and as we know, the user will choose only one class, but we do not know which class and methods are separate for the distribution of skills for each class. It would not be better to create only one class that the user will use and only one method of distributing skills?

In the next article you will see how dependency injection makes life easier :)

Let’s see what tests look like in this class.

namespace NUnitTestGameProject
{
[TestFixture]
public class CharacterSkillPointsTest
{
CharacterSkillPoints characterskillpoints;
        [SetUp]
public void TestSetup()
{
characterskillpoints = new CharacterSkillPoints();
            characterskillpoints.GiveOutSkillPointsForPaladin();
characterskillpoints.GiveOutSkillPointsForBarbarian();
characterskillpoints.GiveOutSkillPointsForMage();
characterskillpoints.GiveOutSkillPointsForThief();
}
        [Test]
public void skill_points_for_paladin_strength()
{
int StrengthSkillPoints = characterskillpoints.paladin.Strength;
            Assert.AreEqual(26, StrengthSkillPoints);
}
        [Test]
public void skill_points_for_paladin_stamina()
{
int StaminaSkillPoints = characterskillpoints.paladin.Stamina;
            Assert.AreEqual(22,StaminaSkillPoints);
}
        [Test]
public void skill_points_for_barbarian_strength()
{
int StrengthSkillPoints = characterskillpoints.barbarian.Strength;
            Assert.AreEqual(33, StrengthSkillPoints);
}
        [Test]
public void skill_points_for_barbarian_stamina()
{
int StaminaSkillPoints = characterskillpoints.barbarian.Stamina;
            Assert.AreEqual(26, StaminaSkillPoints);
}
        [Test]
public void skill_points_for_mage_strength()
{
int StrengthSkillPoints = characterskillpoints.mage.Strength;
            Assert.AreEqual(15, StrengthSkillPoints);
}
        [Test]
public void skill_points_for_mage_stamina()
{
int StaminaSkillPoints = characterskillpoints.mage.Stamina;
            Assert.AreEqual(12, StaminaSkillPoints);
}
        [Test]
public void skill_points_for_thief_strength()
{
int StrengthSkillPoints = characterskillpoints.thief.Strength;
            Assert.AreEqual(20, StrengthSkillPoints);
}
        [Test]
public void skill_points_for_thief_stamina()
{
int StaminaSkillPoints = characterskillpoints.thief.Stamina;
            Assert.AreEqual(22, StaminaSkillPoints);
}
}
}

We check skills after selecting a class, it is good to write tests for methods that accept some arguments and return some, but we have an exception because these methods operate on two variables and all code is organized, just call the methods distributing skill points and check the total amount skills in the character class we chose, so these methods are still easy to test.

However, there are a lot of these tests if there was only one method for distributing skill points, there would be only two tests. But calmly, we’ll do all this in the next article using dependency injection :)

The methods validating the password and login accept and return arguments, so in this way, as described above, we will write tests to them.

Without tests, you can sometimes be surprised why and what does not work after the change … 😐

Let’s see, what is the class for validating login and password?

Code for validating the password and login before the changes.

public void RegisterUser(string login, string password)
{
string LoginPattern = @"(?=.*[A-Za-z0-9]$)[A-Za-z][A-Za-z\d.-]{0,19}";
string PasswordPattern = @"(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}";
     // Define a test string.        
Match result1 = Regex.Match(login, LoginPattern);
Match result2 = Regex.Match(password, PasswordPattern);
     if (result1.Success && result2.Success)
{
Console.WriteLine("Register user");
}
else
{
Console.WriteLine("Login or password are incorrect!");
}
}

Here is also a tragedy 😥 everything thrown into one method, it’s hard to read, also fear to move here something because you can not write tests. 😐

Let’s see now how it looks after the changes. I divided it into two classes validating the password and login separately.

Password validating class:

namespace RefactoredGameProject
{
public class PasswordValidator
{
string PasswordPattern = @"(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}";
        public bool PasswordValidate(string password)
{
Match passwordmatchresult = Regex.Match(password, PasswordPattern);
            if (passwordmatchresult.Success)
{
return true;
}
else
{
return false;
}
}
}
}

And the validating class of the login:

namespace RefactoredGameProject
{
public class LoginValidator
{
string LoginPattern = @"(?=.*[A-Za-z0-9]$)[A-Za-z][A-Za-z\d.-]{0,19}";
        public bool LoginValidate(string login)
{
Match loginmatchresult = Regex.Match(login, LoginPattern);
            if (loginmatchresult.Success)
{
return true;
}
else
{
return false;
}
}
}
}

It already looks a lot better :), validation of the password and login have been separated into separate classes which is more readable and easier to develop and easy to test. Let’s see how tests look like to them.

Tests to the LoginValidator class

namespace NUnitTestGameProject
{
[TestFixture]
public class LoginValidatorTest
{
LoginValidator loginvalidator;
        [SetUp]
public void TestSetup()
{
loginvalidator = new LoginValidator();
}
        [Test]
public void test_if_login_is_valid()
{
bool ifloginvalid = loginvalidator.LoginValidate("asdasd123");
            Assert.IsTrue(ifloginvalid);
}
        [Test]
public void test_if_login_is_not_valid()
{
bool ifloginnotvalid = loginvalidator.LoginValidate("asdasd123@");
            Assert.IsFalse(ifloginnotvalid);
}
}
}

And tests to the PasswordValidator class

namespace NUnitTestGameProject
{
[TestFixture]
public class PasswordValidatorTest
{
PasswordValidator passwordvalidator;
        [SetUp]
public void TestSetup()
{
passwordvalidator = new PasswordValidator();
}
        [Test]
public void test_if_password_is_valid()
{
bool ifpasswordvalid = passwordvalidator.PasswordValidate("asdasd123@");
            Assert.IsTrue(ifpasswordvalid);
}
        [Test]
public void test_if_password_is_not_valid()
{
bool ifpasswordnotvalid = passwordvalidator.PasswordValidate("asdasd1");
            Assert.IsFalse(ifpasswordnotvalid);
}
}
}

And now we will see if all tests through to the validation of login and password as well the tests that check the correct distribution of skill points.

As you can see they pass :)

It’s better to ensure that after writing 100 lines of code everything works :)

At the end, let’s see the main GameServer class

namespace RefactoredGameProject
{
public class GameServer
{
LoginValidator loginvalidator;
PasswordValidator passwordvalidator;
public CharacterSkillPoints characterskillpoints;
        public GameServer()
{
loginvalidator = new LoginValidator();
passwordvalidator = new PasswordValidator();
characterskillpoints = new CharacterSkillPoints();
}
        public void RegisterUser(string login, string password)
{
bool ifloginvalidate = loginvalidator.LoginValidate(login);
bool ifpasswordvalidate = passwordvalidator.PasswordValidate(password);
            if(ifloginvalidate==true && ifpasswordvalidate==true)
{
Console.WriteLine("Register user");
}
else
{
Console.WriteLine("Login or password are incorrect!");
}
}
        public void CreateCharacter(ICharacter character)
{
if(character is Barbarian)
{
characterskillpoints.GiveOutSkillPointsForBarbarian();
}
else if(character is Paladin)
{
characterskillpoints.GiveOutSkillPointsForPaladin();
}
else if (character is Mage)
{
characterskillpoints.GiveOutSkillPointsForMage();
}
else if (character is Thief)
{
characterskillpoints.GiveOutSkillPointsForThief();
}
}
}
}

In the RegisterUser() method, if the methods validating the login and password return us the condition true, we register the user, if any of the methods returns false, we display the error.

In the CreateCharacter() method, depending on the passed class, we give out skill points. You could use the Strategy pattern in this method to get rid of these if-s, but there is another better way related with the dependency injection. This is also in the next article :)

In the constructor, we pass class objects that we need, but we pass them to specific class types that should not be done.

There is a way for these objects to depend from abstraction, but about this in the next article.

And the client who uses the GameServer class.

namespace RefactoredGameProject
{
class Program
{
static void Main(string[] args)
{
GameServer gameserver = new GameServer();
            gameserver.RegisterUser("assd12","adasd123@");
            Console.WriteLine("Skill points before the deal.");
            Console.WriteLine(gameserver.characterskillpoints.barbarian.Strength);
Console.WriteLine(gameserver.characterskillpoints.barbarian.Stamina);
            gameserver.CreateCharacter(new Barbarian());
            Console.WriteLine("Skill points after the deal.");
            Console.WriteLine(gameserver.characterskillpoints.barbarian.Strength);
Console.WriteLine(gameserver.characterskillpoints.barbarian.Stamina);
            Console.ReadKey();
}
}
}

I wanted to display skill points before and after the deal so that everything was understandable.

Result:

Summary

In the next article, we will already use dependency injection techniques and we will even more refactorise this code.

Link to github with the whole code from this article: https://github.com/Slaw145/Dependency-injection--refactored-game-project

This content also you can find on my blog devman.pl: http://devman.pl/programtech/dependency-injection-refactoring-of-the-project/

As a standard, I remind you about the newsletter, which I send notifications about new entries and additional information about the IT world in general.🙂

And NECESSERILY join the DevmanCommunity community on fb, part of the community is in one place 🙂

– site on fb: Devman.pl-Slawomir Kowalski

– group on fb: DevmanCommunity

Ask, comment underneath at the end of the post, share it, rate it, whatever you want🙂.