Notes from Daily Encounters with Technology RSS 2.0
 
# Monday, November 24, 2014

Encouraged by Scott Hanselman's Get Involved video, I started experimenting with DocPad. For someone, who's been mostly focused on .NET framework and Microsoft technologies, this turned out to be quite a step outside the comfort zone. Not in a bad way, though. Learning Node.js, CofeeScript, and Embedded CoffeeScript (ECO) templates is an interesting challenge and a great way to broaden my horizons.

Recently I wanted to perform a simple task of grouping a list of pages by the month, they were published in. In .NET I would use LINQ and be done with it in a matter of minutes. Doing this in my new technology stack took a bit more time. My first working solution was not something, I'd want to show to anyone. However, after some refactoring and cleaning up, I ended up with code, I decided to even blog about (mostly to serve as a reference while I continue to learn more).

My starting point in DocPad was the result of the following call:

posts = @getCollection("blogposts").toJSON()

Here's a sample of it:

var posts = 
[
  {
    "date": newDate("2014-11-10"),
    "url": "ScopeNinjectBindingsToIndividualTests.aspx",
    "title": "Scope Ninject Bindings to Individual Tests"
  },
  {
    "date": newDate("2014-11-03"),
    "url": "CustomizingComparisonsInFluentAssertionsWithOptions.aspx",
    "title": "Customizing Comparisons in FluentAssertions with Options"
  },
  {
    "date": newDate("2014-10-27"),
    "url": "LightMessageBusAStandaloneLightweightEventAggregator.aspx",
    "title": "LightMessageBus: A Standalone Lightweight Event Aggregator"
  }
];

It's a collection of page meta data, already sorted descendingly by date.

Surprisingly (at least to me) there's already a built-in reduce function available on JavaScript arrays, you just need to call it correctly:

postsByMonth = posts.reduce((previous, current, index, context) ->
  month = moment(current.date).format("MMMM YYYY")
  if previous[month]
    previous[month].push(current) 
  else
    previous[month] = [ current ]
  previous
{})

I started out with an empty object (passed in as context). For each month I added a property to it. Each property contains an array of posts for the month. The function returns the modified object so that it can be passed back in for the next call. I'm using moment.js library to convert the date into a string representing the month.

With just a little more effort this can be converted into a reusable grouping function:

arrayGroupBy = (array, aggregate) ->
  array.reduce((previous, current, index, context) ->
    group = aggregate(current)
    if previous[group]
      previous[group].push(current)
    else
      previous[group] = [ current ]
    previous
  {})

The aggregate function can be passed into it as a parameter:

postsByMonth = arrayGroupBy(posts, (post) -> 
  moment(current.date).format("MMMM YYYY"))

Since I was going to use the date formatting elsewhere, I extracted it into a function as well, and changed the call accordingly:

dateToMonthAndYear = (date) -> moment(date).format("MMMM YYYY")
postsByMonth = arrayGroupBy(posts, (post) -> dateToMonthAndYear(current.date))

Now I was ready to use my code in the page template:

<ul>
<% posts = @getCollection("blogposts").toJSON() %>
<% aggregate = (post) => @dateToMonthAndYear(post.date) %>
<% postsByMonth = @arrayGroupBy(posts, aggregate) %>
<% for month in Object.keys(postsByMonth): %>
  <li><%= month %></li>
  <ul>
  <% for post in postsByMonth[month]: %>
    <li>
      <a href="<%= post.url %>">
        <%= post.title %>
      </a>
    </li>
  <% end %>
  </ul>
<% end %>
</ul>

Notice, how I first iterated through properties of postsByMonth by calling Object.keys(), and then through the posts by accessing the object properties as items in an associative array.

For those familiar with DocPad, I should mention that I have declared dateToMonthAndYear and arrayGroupBy in docpad.coffee configuration file, that's why I'm accessing them using the @ shortcut syntax for "this" in CoffeeScript. Here's the relevant part of my configuration file:

moment = require('moment')

docpadConfig = {
  templateData:
    dateToMonthAndYear: (date) -> moment(date).format("MMMM YYYY")
    arrayGroupBy: (array, aggregate) ->
      array.reduce((previous, current, index, context) ->
        group = aggregate(current)
        if previous[group]
          previous[group].push(current)
        else
          previous[group] = [ current ]
        previous
      {})
}

It's also worth mentioning, that I've used the fat arrow when declaring the aggregate. This way "this" got bound to its value in the template, making dateToMonthAndYear function available.

Yes, I know there is a LINQ library available for JavaScript, which I could use instead, but I think it's a bit of an overkill in this case. And the end result wouldn't be all that different either, although a bit easier to understand for .NET developers:

moment = require('moment')
enumerable = require('linq')

docpadConfig = {
  templateData:
    dateToMonthAndYear: (date) -> moment(date).format("MMMM YYYY")
    arrayGroupBy: (array, aggregate) -> enumerable.
      from(array).
      groupBy((post) -> aggregate(post)).
      select((group) ->
        {
          key: group.key()
          values: group.toArray()
        }).
      toArray()
}

Since arrayGroupBy now returns an array instead of an object, the looping in the template would be implemented slightly differently as well:

<ul>
<% posts = @getCollection("blogposts").toJSON() %>
<% aggregate = (post) => @dateToMonthAndYear(post.date) %>
<% postsByMonth = @arrayGroupBy(posts, aggregate) %>
<% for month in postsByMonth: %>
  <li><%= month.key %></li>
  <ul>
  <% for post in month.values: %>
    <li>
      <a href="<%= post.url %>">
        <%= post.title %>
      </a>
    </li>
  <% end %>
  </ul>
<% end %>
</ul>

I don't know about you, but I decided not to use linq.js for now. I might change my mind if I'll need more complex array manipulation in my project.

Monday, November 24, 2014 6:48:20 AM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | CoffeeScript | JavaScript | Software | DocPad
# Monday, November 17, 2014

The more I use FluentAssertions, the more I like its flexibility and extensibility. In this post I'm going to focus on assertion rules which can be used to define custom equality comparisons for specific types.

To show how it works, we'll create two classes for representing information about a user. The first one will be used in the application back end, when the user is retrieved from the database:

public class User
{
  public string Name { get; set; }
  public string Surname { get; set; }
  public string Username { get; set; }
  public LoginType LoginType { get; set; }
}    

public enum LoginType
{
  UsernameAndPassword,
  DomainAccount
}

The second one will be used in the application service layer contract:

public class UserDto
{
  public string Name { get; set; }
  public string Surname { get; set; }
  public string Username { get; set; }
  public LoginTypeDto LoginType { get; set; }
}

public enum LoginTypeDto
{
  UsernameAndPassword,
  DomainAccount
}

In our tests we will want to compare the two classes: the first one will be used to setup the repository contents, and the second one will be returned by the service layer. Comparing whether they contain the same data, is a reasonable requirement:

public void DefaultAssert()
{
  var user = new User
  {
    Name = "Damir",
    Surname = "Arh",
    Username = "damir",
    LoginType = LoginType.UsernameAndPassword
  };
  var repository = new MockUserRepository();
  repository.Users.Add(user);
  var service = new UserService(repository);

  var userDto = service.GetUser("damir");

  userDto.ShouldBeEquivalentTo(user);
}

Although ShouldBeEquivalentTo method already does a property by property comparison, it will still throw, when it compares the two LoginType values:

Expected property LoginType to be UsernameAndPassword, but found UsernameAndPassword.

With configuration:
- Include only the declared properties
- Match property by name (or throw)


UsernameAndPassword values in both enums technically don't have anything in common, only we as developers know, that they represent the same business values. To provide this information to FluentAssertions, we can write a custom IAssertionRule:

public class LoginTypeRule : IAssertionRule
{
  public bool AssertEquality(IEquivalencyValidationContext context)
  {
    var subjectValue = GetValueForEnum(context.Subject);
    var expectationValue = GetValueForEnum(context.Expectation);

    if (subjectValue == null || expectationValue == null)
    {
      // rule not applicable
      return false;
    }

    Execute.Assertion
      .BecauseOf(context.Reason, context.ReasonArgs)
      .ForCondition(subjectValue == expectationValue)
      .FailWith("Expected {context:string} to be {0}{reason}, but found {1}",
        context.Subject, context.Expectation);

    // equality assertion handled
    return true;
  }

  private int? GetValueForEnum(object enumValue)
  {
    if (enumValue is LoginType || enumValue is LoginTypeDto)
    {
      return (int) enumValue;
    }
    return null;
  }
}

We only need to implement a single method: AssertEquality. By taking a closer look at our implementation, we can see which requirements we need to fulfill:

  • We start by checking the type of both values: Expectation and Subject. They both need to be one of our enum types, otherwise the method returns false. This indicates to FluentAssertions that the rule isn't applicable and further assertion rules need to be run.
  • In the next step we compare the two enums by their ordinal value. The comparison is wrapped in some boilerplate code which provides FluentAssertions with all the necessary information to report the assertion failure, when the values don't match.
  • We conclude by returning true, which indicates that the values have been compared and no further assertion rules need to be run.

To use the new assertion rule, we just need to include it in the assertion call at the end of our test:

userDto.ShouldBeEquivalentTo(user, options => options
  .Using(new LoginTypeRule()));

This will insert the rule at the beginning of the assertion rules collection, making the test pass.

Although the above example is a bit contrived, I am sure, you will encounter actual business cases, when creating a new assertion rule could make your tests easier to write and understand. Just keep in mind, that you have this extensibility point at your disposal.

Monday, November 17, 2014 6:44:57 AM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | Testing
# Monday, November 10, 2014

One of the main reasons for using an IoC container like Ninject is to be able to inject different dependencies into your classes in production code and in test code. Usually you don't even need to use an IoC container in your tests; it's easier to explicitly instantiate the dependencies in your tests and pass them to the class you're testing. This gives you much more control over them.

Nevertheless, there are cases when the IoC container can be useful in test code as well:

  • You might need to use it because of the service locator (anti)pattern imposed on you by your own code base or a framework you're using.
  • You might want to use it when your classes have lots of dependencies and you don't want to manually instantiate them all.

Unlike in production code, where your bindings usually don't change for the lifetime of the application, in test code the bindings often change from test to test. Not only that; you also want the tests to be isolated from each other, therefore the bindings need to be scoped appropriately. Transient scopes of course will work fine, but longer lived scopes, such as singleton, will quickly create problems.

In this post I'll demonstrate that objects, which are singleton scoped in production code, should usually be scoped to individual tests in test code. I'll be using Ninject as the IoC container and NUnit as the testing framework, but it shouldn't be difficult to modify the code for other frameworks, since the concepts are very similar in all of them.

The sample is based around a simplified repository interface:

public interface IRepository<T>
{
    IQueryable<T> Get();
    void Insert(T item);
}

For the purpose of the tests, we're going to use an in-memory implementation:

public class InMemoryRepository<T> : IRepository<T>
{
    private readonly List<T> _items = new List<T>();

    public IQueryable<T> Get()
    {
        return _items.AsQueryable();
    }

    public void Insert(T item)
    {
        _items.Add(item);
    }
}

Of course, we'll want to use the same instance of the class within a single test, and a different instance of the class for each test. This way we will achieve persistence within the test and isolation between tests. We could achieve that by manually creating the instance inside the test and pass it to any objects that depend on it. To make the scenario more complicated, we will assume that the object being tested is using the service locator pattern.

We're going to store users in our repository:

public class User
{
    public Guid Id { get; set; }
    public string Username { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }

    public User(string name, string surname)
    {
        Id = Guid.NewGuid();
        Name = name;
        Surname = surname;
    }

    public User(string name, string surname, string username)
        : this(name, surname)
    {
        Username = username;
    }
}

We will test a class which can generate a unique username for a new user:

public class UsernameGenerator
{
    public string GenerateUsername(User user)
    {
        var userRepository = NinjectKernel.Instance.Get<IRepository<User>>();
        for (int surnameChars = 1; surnameChars <= user.Surname.Length; surnameChars++)
        {
            var username = user.Name.ToLower() 
                + user.Surname.Substring(0, surnameChars).ToLower();
            if (!userRepository.Get().Any(u => u.Username == username))
            {
                return username;
            }
        }
            
        throw new Exception("Couldn't generate username.");
    }
}

As you can see; the repository is not passed into the class through the constructor. Instead, the method requests it from the global kernel instance. NinjectKernel is a static class, containing the singleton instance of the kernel:

public static class NinjectKernel
{
    public static readonly IKernel Instance = new StandardKernel();
}

We only need two tests to demonstrate, how inappropriate scoping can break isolation between tests:

[Test]
public void AvoidDuplicates()
{
    var userRepository = NinjectKernel.Instance.Get<IRepository<User>>();
    userRepository.Insert(new User("Peter", "Patterson", "peterp"));
    var user = new User("Peter", "Parker");
    var usernameGenerator = new UsernameGenerator();

    var username = usernameGenerator.GenerateUsername(user);

    Assert.AreEqual("peterpa", username);
}

[Test]
public void DefaultUsernameWhenEmptyRepository()
{
    var user = new User("Peter", "Parker");
    var usernameGenerator = new UsernameGenerator();

    var username = usernameGenerator.GenerateUsername(user);

    Assert.AreEqual("peterp", username);
}

For the tests to pass, we need a binding for IRepository<User>. Transient binding is not going to work because in the first test we get the instance from the kernel both in the test and in the method which we are testing. The instance needs to be the same in both cases. By using singleton scope we can ensure this same instance. But now the second test will fail if they are run in the same order as they are defined above, because in the second test the repository will already contain the user from the first test and cause the username generator to give a different result.

We could avoid that by clearing the repository or by releasing the singleton before each test:

[SetUp]
public void Setup()
{
    var instance = NinjectKernel.Instance.Get<IRepository<User>>();
    NinjectKernel.Instance.Release(instance);
}

This is quite error prone since it needs to be done in every tests class for each applicable dependency.

A much better solution is to avoid the singleton scoping altogether and bind the objects to the scope of individual tests in the first place. Ninject supports binding to custom scopes; it only needs a function which returns the same value within the desired scope and a different one for any call outside of that scope. To implement such a method we can take advantage of NUnit's TestContext class which provides information about the currently running test. By combining these two together we can bind the repository as follows:

Bind<IRepository<User>>().To<InMemoryRepository<User>>()
    .InScope(ctx => TestContext.CurrentContext.Test.FullName);

In my opinion it's still better to avoid using IoC containers in tests, but when you really need to use them, this makes it at least more manageable than manually reinitializing all dependencies before each test and hunting down the cause for randomly failing interdependent tests, when you forget to do it.

Monday, November 10, 2014 6:56:27 AM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | Testing
# Monday, November 3, 2014

Unit tests best serve their purpose when they are brief and easy to understand. There are cases when it can be difficult to achieve this; nevertheless it's still worth putting in the effort, as it may pay off. Here's a test similar to the one I've recently come up with:

[Test]
public void InitialTest()
{
    // add properties which should not have their values copied
    var ignoredPropertyNames = new[] { "LastChanged" };

    var activityType = typeof(ActivityWithSettings);
    var properties = activityType
        .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
        .Where(property => !ignoredPropertyNames.Contains(property.Name)).ToList();

    var random = new Random();
    var source = new ActivityWithSettings();
    foreach (var property in properties)
    {
        if (property.PropertyType == typeof(string))
        {
            property.SetValue(source, random.Next().ToString(CultureInfo.InvariantCulture), null);
        }
        else if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
        {
            property.SetValue(source, random.Next(), null);
        }
        else
        {
            throw new NotImplementedException(
                String.Format("Test does not yet support properties of type {0} (property {1}).",
                property.PropertyType.FullName, property.Name));
        }
    }

    var destination = new ActivityWithSettings();
    destination.CopySettingsFrom(source);

    foreach (var property in properties)
    {
        Assert.AreEqual(property.GetValue(source, null), property.GetValue(destination, null),
            property.Name + " should have matching values.");
    }
}

It's far from obvious, what I was trying to achieve: test whether CopySettingsFrom method copies the right subset of properties from one object to another one. Since I mostly wanted the test to catch changes of the class which didn't include a corresponding modification of the method, I used a lot of reflection to define the set of properties and initialize them.

A couple of hours later I managed to write an equivalent test, which looked like this:

[Test]
public void FinalTest()
{
    var source = Builder<ActivityWithSettings>.CreateNew().Build();

    var destination = new ActivityWithSettings();
    destination.CopySettingsFrom(source);

    destination.ShouldBeEquivalentTo(source, options => options
        .Using(new NonInheritedPublicPropertiesSelectionRule())
        .Excluding(activity => activity.LastChanged));
}

Much better, isn't it? We'll take a look at the steps I took; but first, let's look at the tested code (a simplification of my actual scenario):

public class CoreActivity
{
    public int Id { get; set; }
    public string DisplayName { get; set; }
}

public class ActivityWithSettings : CoreActivity
{
    public int DeadlinePeriod { get; set; }
    public string Category { get; set; }
    public DateTime LastChanged { get; set; }

    public void CopySettingsFrom(ActivityWithSettings sourceActivity)
    {
        DeadlinePeriod = sourceActivity.DeadlinePeriod;
        Category = sourceActivity.Category;
        LastChanged = DateTime.Now;
    }
}

Essentially, CopySettingsFrom should copy the values of properties declared on the derived class, skipping the predefined exceptions. Any additional properties, added to the derived class in the future, should by default be copied as well.

This explains the steps in my initial test:

  • A list of properties to ignore.
  • A list of properties to check, consisting of the ones declared directly on the derived class, without the ones in the ignore list.
  • A block of code initializing all the properties of the source object to non-default values.
  • Test method invocation.
  • Validation of selected property values on the destination object.

I first replaced my object initialization with NBuilder. This library can be used for preparing test data with a minimum amount of effort. In my case it was enough to use it in its simplest form; it actually offers a lot more flexibility:

[Test]
public void TestWithBuilder()
{
    // add properties which should not have their values copied
    var ignoredPropertyNames = new[] { "LastChanged" };

    var activityType = typeof(ActivityWithSettings);
    var properties = activityType
        .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
        .Where(property => !ignoredPropertyNames.Contains(property.Name)).ToList();

    var source = Builder<ActivityWithSettings>.CreateNew().Build();

    var destination = new ActivityWithSettings();
    destination.CopySettingsFrom(source);

    foreach (var property in properties)
    {
        Assert.AreEqual(property.GetValue(source, null), property.GetValue(destination, null),
            property.Name + " should have matching values.");
    }
}

With NBuilder I already replaced a lot of complex code. The key to remaining changes were FluentAssertions. I already wrote about this library, but this time I had to use more of its features. ShouldBeEquivalentTo is the method for loosely comparing objects and its second parameter can be used to further customize the comparison behavior. This was the result of my first attempt at using its capabilities:

[Test]
public void TestWithFluentAssertions()
{
    var activityType = typeof(ActivityWithSettings);
    var properties = activityType
        .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).ToList();

    var source = Builder<ActivityWithSettings>.CreateNew().Build();

    var destination = new ActivityWithSettings();
    destination.CopySettingsFrom(source);

    destination.ShouldBeEquivalentTo(source, options => options
        .Including(ctx => properties.Select(property => property.Name).Contains(ctx.PropertyPath))
        .Excluding(activity => activity.LastChanged));
}

Most importantly, I managed to replace string based exclusion list with a strongly typed one using lambdas. I still wasn't satisfied with the approach, I had to use to only include the properties declared directly on the derived type. It's not terrible, but it was really a step back from using IncludeAllDeclaredProperties method which promised to do exactly that before I tried it out. Unfortunately its understanding of declared properties is different from BindingFlags.DecaledOnly. While the latter does exactly what I needed, the former includes the inherited properties as well.

Although the library doesn't include such a method, the same can be achieved in a much more elegant manner than I did it in my attempt above. Instead of including the property filtering logic directly in the test an ISelectionRule can be written and then reused in any test:

public class NonInheritedPublicPropertiesSelectionRule : ISelectionRule
{
    public IEnumerable<PropertyInfo> SelectProperties(IEnumerable<PropertyInfo> selectedProperties,
        ISubjectInfo context)
    {
        return context.CompileTimeType
            .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
    }

    public override string ToString()
    {
        return "Include only non-inherited declared properties";
    }
}

As you can see, all the logic is now wrapped in the rule, along with a description which will show up whenever the assertion using it, fails. Instead of the Including method, Using can now be used:

[Test]
public void FinalTest()
{
    var source = Builder<ActivityWithSettings>.CreateNew().Build();

    var destination = new ActivityWithSettings();
    destination.CopySettingsFrom(source);

    destination.ShouldBeEquivalentTo(source, options => options
        .Using(new NonInheritedPublicPropertiesSelectionRule())
        .Excluding(activity => activity.LastChanged));
}

And here we are: this is how my test ended up looking. Well worth the time I put into it, if you ask me.

Monday, November 3, 2014 6:14:33 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | Testing
# Monday, October 27, 2014

In the field of software architecture the terms publish-subscribe pattern, event aggregator, and message bus are closely related to each other. All of them revolve around a loosely coupled messaging paradigm, in which message publishers and message receivers mutually independent. Their only common point is the communication contract in the form of messages, which are being published and received.

Publish-subscribe denotes the name of the architectural pattern. Event aggregator is a common name for its in-process implementation, i.e. a class which receives all the published messages and propagates them to the subscribed receivers. In contrast, message bus is usually a larger software component enabling a similar messaging pattern between multiple applications. The term enterprise service bus (ESB) is also often used in this context. The meaning of terms is not that strictly defined and they are often used interchangeably in different sources.

This post focuses on the concept, most often named event aggregator, i.e. in-process pub-sub communication between components. Lately, event aggregators are often mentioned in connection with MVVM (model - view - view model) UI architectural pattern. Most MVVM frameworks today include their own event aggregator implementation: Messenger in MVVM Light, MvxMessenger in Mvvm Cross, EventAggregator in Prism for the Windows Runtime, etc.

As long as you're actually using the event aggregator within the frames of the UI, developed using the MVVM pattern, you're of course also going to use the event aggregator distributed with your MVVM framework of choice. The APIs and functionalities of different event aggregator implementations are similar enough, therefore it shouldn't really matter which one you are using.

But what if you want to use an event aggregator in non-MVVM applications? You probably don't want or even can't reference a MVVM framework just to get to the event aggregator implementation. In this case you suddenly have a much smaller choice. My coworker and friend Goran Šiška recently published the first version of LightMessageBus, his own minimalistic event aggregator. I decided to spread some word about it by giving it a proper introduction.

To do that, we need a feasible use case. After all, samples usually don't go past composite UIs, where event aggregators are used for communication between different view models. I'm sure you've already encountered a shopping cart example:

  • There's a shopping cart with a number of items displayed in the corner of the application.
  • Other parts of the UI publish messages whenever an item is added to the cart.
  • The shopping cart is subscribed to this messages.
  • This way other view models don't have to know about the shopping cart directly, but can still notify it of new items added to it.

Of course, you'll usually have a MVVM framework available in such a scenario. So, let's venture into the game development for a change. You're typically not going to use MVVM or XAML for game development, so no MVVM framework, as well. This should be a good opportunity for LightMessageBus to prove itself. Here's the scenario:

  • We want to implement achievements in our game.
  • The achievement tracker needs to know about many different in-game events to track their statistics. It's going to subscribe to all messages it needs to track.
  • Once conditions are met for an achievement to unlock, it will publish a corresponding message.
  • The notification center will listen to this message (as well as others) and show a pop-up notifying the player about the unlocked achievement.

The first step in implementing such a system should be the definition of the messaging contract: the messages that are going to be published. In LightMessageBus all messages must implement Imessage interface. Since there's no base class implementing it in the library, you'll probably want to write one yourself to avoid reimplementing the same required property in every message:

public abstract class MessageBase : IMessage
{
    public object Source { get; set; }

    protected MessageBase(object source)
    {
        Source = source;
    }
}

Both our messages will derive from this base class:

public class LevelEndedMessage : MessageBase
{
    public bool Success { get; set; }

    public LevelEndedMessage(object source, bool success)
        : base(source)
    {
        Success = success;
    }
}

public class AchievementUnlockedMessage : MessageBase
{
    public AchievementType AchievementType { get; set; }

    public AchievementUnlockedMessage(object source, AchievementType achievementType) 
        : base(source)
    {
        AchievementType = achievementType;
    }
}

The AchievementTracker class is subscribed to the LevelEndedMessage from all publishers:

public AchievementTracker()
{
    MessageBus.Default.FromAny().Where<LevelEndedMessage>().Notify(this);
}

To be able to handle it, it must implement IMessageHandler<LevelEndedMessage>:

public class AchievementTracker : IMessageHandler<LevelEndedMessage>
{
    void IMessageHandler<LevelEndedMessage>.Handle(LevelEndedMessage message)
    {
        if (message.Success)
        {
            Achievements[AchievementType.LevelsWonInARow].IncrementCount();
        }
        else
        {
            Achievements[AchievementType.LevelsWonInARow].ResetCount();
        }
    }
}

It makes sense to implement the interface explicitly. We don't want to pollute its public interface with a bunch of Handle methods. The message handler processes the message and updates the corresponding AchievementStatus instance. Once the achievement is unlocked, the class publishes a new AchievementUnlockedMessage:

public class AchievementStatus
{
    public AchievementType AchievementType { get; set; }
    public bool Completed { get; set; }
    public int CurrentCount { get; set; }
    public int RequiredCount { get; private set; }

    private void CheckCompletion()
    {
        if (CurrentCount >= RequiredCount && !Completed)
        {
            MessageBus.Default.Publish(new AchievementUnlockedMessage(this, AchievementType));
            Completed = true;
        }
    }

    public void IncrementCount(int delta = 1)
    {
        CurrentCount += delta;
        CheckCompletion();
    }

    public void ResetCount()
    {
        CurrentCount = 0;
    }
}

Other classes follow the same pattern. When the player completes the level or fails to complete it, the game level will publish the LevelEndedMessage, which will be delivered to the AchievementTracker. Similarly, the notification center is subscribed to AchievementUnlockedMessage to notify the user, whenever it is published. With such architecture, it is easy to extend the game with additional messages, publishers and subscribers, without affecting the existing code.

If you have never done it before, try using an EventAggregator in your code, when an opportunity arises: either in an MVVM application or elsewhere. Publish-subscribe is an architectural pattern, worth learning.

Monday, October 27, 2014 12:44:22 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET
My Book

NuGet 2 Essentials

About Me
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

All Content © 2014, Damir Arh, M. Sc. Send mail to the author(s) - Privacy Policy - Sign In
Based on DasBlog theme 'Business' created by Christoph De Baene (delarou)
Social Network Icon Pack by Komodo Media, Rogie King is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.