Mud Designer Code Analysis: Sealed Attributes

I had mentioned a couple of times that I would be refactoring the current engine re-write using the NDepend statically analysis tool. I performed an initial analysis last night and reviewed the report and started to dig in. Some of the issues that I ran in to were really simple to fix. Let's start with those first.

Avoid unsealed attributes

Microsoft's MSDN states that for performance reasons, you should avoid unsealed attributes whenever possible. Considering that the engine's built-in validation mechanics are built off of attributes, I want to make sure and grab any performance increase that I can get.

I started out with an existing Fixture model that I created during development. The model is really simple and includes three properties, each being validated in some manor.

public class ValidatableFixture : ValidatableBase
{
    private const string PasswordConfirmationDelegateName = "ConfirmPasswordsMatch";

    public ValidatableFixture()
    {
        this.Name = string.Empty;
        this.Password = string.Empty;
        this.PasswordConfirmation = string.Empty;
    }


    [ValidateValueIsNotNullOrEmpty(ValidationMessageType = typeof(MessageFixture), FailureMessage = "Name must be set.")]
    public string Name { get; set; }


    [ValidateValueIsNotNullOrEmpty(ValidationMessageType = typeof(MessageFixture), FailureMessage = "Password must be set.")]
    [ValidateStringIsGreaterThan(GreaterThanValue = 4, ValidationMessageType = typeof(MessageFixture), FailureMessage = "Password must be greater than 4 characters.")]
    public string Password { get; set; }


    [ValidateWithCustomHandler(DelegateName = PasswordConfirmationDelegateName, ValidationMessageType = typeof(MessageFixture), FailureMessage = "Passwords do not match.")]
    public string PasswordConfirmation { get; set; }


    [ValidationCustomHandlerDelegate(DelegateName = PasswordConfirmationDelegateName)]
    public IMessage PasswordConfirmationValidation(IMessage message, PropertyInfo property)
    {
        return this.PasswordConfirmation.Equals(this.Password) ?
            null :
            message;
    }
}

This fixture contains four total validation rules that will be invoked during the test. The most expensive of the four would be the ValidateWithCustomHandler attribute. Unlike the rest of the validation system, the ValidateWithCustomHandler attribute does not cache the MethodInfo object it fetches via reflection. At the moment, the engine just caches the attributes themselves and their associated properties for re-use across multiple instances. Caching of method delegates will make its way in to the system at some point in the future.

To get started, I wrote a simple unit test that creates 1,000 instances of the ValidatableFixture class and invokes ValidateAll(). The unit test ran through all 1,000 instances, invoking Validate() on each attribute, which caused the attribute validation to take place. In theory, there shouldn't be a huge penalty here due to the caching I am doing. Once the first ValidatableFixture is instanced, the rest of them use the PropertyInfo and ValidationAttribute cache that the first one generated. There won't be any deep walking of the attribute heirarchy.

// Arrange
var fixtures = new List< ValidatableFixture>();
var watch = new Stopwatch();
for (int index = 0; index < 1000; index++)
{
    var model = new ValidatableFixture();
    model.Name = "TestName";
    model.Password = "pass";
    model.PasswordConfirmation = "pass";
    fixtures.Add(model);
}

// Act
watch.Start();
foreach(ValidatableFixture fixture in fixtures)
{
    fixture.ValidateAll();
}

watch.Stop();
Debug.WriteLine(watch.Elapsed.TotalMilliseconds);

I ran the test 5 times and ended with an average of 41.3199ms.

Finally, I went through and sealed all of the validation classes and re-ran the test. To my surprise, the results ended up be slower than leaving them unsealed, with an average time of 43.3881ms.

I found this to be really interesting, as sealing the class should technically make it faster.

I did some research online and discovered there is actually a debate over whether you should or should not seal your classes. I'm in the camp that they should be sealed unless there is an explicit need to override them. In the case of the validation attributes, they should remain sealed, even at the cost of a bit of performance. The idea behind the validation rules is that developers should write their own implementation of IValidationRule rather than inheriting from an existing one and trying to change one step out of many within the classes validaiton process. It opens up the possibility of unexpected results happening behind the scenes during the validation process.

Mud Designer Alpha 3 Analysis with NDepend

I was finally able to get the Mud Designer in to a usable state over the weekend. It has an improved Telnet server that is completely decoupled from the IGame interface. The only coupling the server has with the engine at the moment is a reference to IPlayer, which I might be able to get rid of at some point down the road.

I thought this would be a good time to run some code analysis on the project and see where it stands. Unfortuantely, the report didn't bring the good news I was hoping to see. Let's take a look at the report.

The project contains 1,270 lines of code (compared to Alpha 2 which had 3,852) along with 89 Types (compared to 152). The Alpha 2 release contained 8 critical rule violations and 828 standard violations. Looking at the numbers for Alpha 3, they're roughly cut in half. I was hoping for the number to be lower, but it's early enough in the re-write that I can address this and lower these numbers. The goal of the re-write is to produce a more reliable code-base that provides greater flexibility and is easier to maintain. Proper patterning, architecture, design and code quality is critical for this. NDepend does an excellent job of identifying the areas that I need to address, and allows me to research and apply code designer philosophies.

One of the things that has greatly improved with the current code base is the Cyclomatic Complexity of the source. The overall average is about the same, but the maximum is down from 74 paths to 23.

Over the course of this week as I address some of these code violations, I will post a blog entry for the rule(s) I am currently working on addressing. The problematic source will be posted along with the end result of the refactor to satisfy the NDepend analyzer

Using NDepend to analyze Mud Designer Alpha

Why analyze?

Since the latest build of the Mud Designer re-write isn't ready to ship yet, I thought I'd go back and run a code analyizer against the original Mud Designer Alpha, so that I can compare the quality of the old engine to the new (once it is ready). The goal of the re-write was to improve the code base and make it more abstract and not as tightly coupled.

I was provided a copy of the NDepend toolchain and used it to run the first report against the Alpha version of the Mud Designer and thought I'd share the results of it.

The stats

After the analysis was completed, it provided me with some basic stats.

  1. The project contained 3,852 lines of code, of which 47% was commented. Pretty good coverage, almost to much. Half of my code base shouldn't be covered in comments if the code was wrote clearly. The project
  2. The project contained 152 Types across 5 assemblies with 1,091 methods.
  3. I did not have any rules with errors, but ended up with 61 rule violations in total. I'm not sure if that is a significant number for the number of lines/methods/types the project has or if it is minor.

The violations

I had two violations for Types being to large and one for a method that was to complex. These two violations were anticipated before hand, since all three of them take place in the WinForms code-behind. Code-behind on a WinForms project tend to be ugly and cumbersome, so this was expected. It's also a large part of why I chose to build the next version in WPF with the MVVM pattern.

The report also told me that I had a total of 8 methods that needed to be refactored. With 4 of them being engine methods and 4 of them being WinForms project methods. Once again, the amount of code in violation within the engine was minor next to the WinForm project. The WinForm project just over double the number of IL instructions (117 vs 242) than the rest of the engine.

Something that I had not expected was that I had 9 Types with to many methods. With all but one of those Types living within the Engine assemblies. The bulk of the bloated Types lived under the GameObjects namespace, which was responsible for all of the player, item, world and npc code. Reviewing the code and the analysis showed that the objects could really use a good refactor. Something that I was already planning on the next version of the application. The BaseMob Type contained 81 methods by itself. It made me cringe and not want to even go back and look at the source.

I'm not going to go over every rule violation that took place in this report with this post, but there are a few interesting things to point out.

The report showed that I had a problem with cohesive methods, potential issues due to invoking virtual members within my constructors and that I was not properly disposing of Types that implemented the IDisposable interface. I'm really anxious to get the dust to settle on the new version of the Mud Engine source so that I can run the tool against it. I am planning on a series of posts as I build out the new engine and run the analysis tool against it. The goal of the series would be to demonstrate rule violations and how to address and fix them.

There's really no question that using NDepend will help make my code base much better. It is fairly complex at first when you go to use it, but their documentation seems pretty good. Using their documentation with the analysis and a bit of googling, you can quickly pick up what patterns can be applied through-out your app. It's really nice.

Stay tuned for additional posts coming soon.