At this point in the development of the WordTutor, we need to properly incorporate speech generation into the application. We could hack and glue it into place on top of the existing architecture, or we can integrate it into the existing structure in a clean way.

There are pros and cons either way - but my experience is that nasty code inevitably becomes a curse on future developers. Let’s do it the right way.

On other platforms, Redux typically handles asynchronous and long-running tasks through a middleware extension point. This approach has some nice characteristics worth noting.

Consistency - other parts of the application interact with these tasks by sending messages, just as they do for other application events. This consistency makes it easier for other developers to read, comprehend and modify the application.

Encapsulation - other parts of the application need not even be aware of the asynchronous nature of the action. A message is sent requesting an action, and the application state changes when the action is complete. Having those two events separated in time is transparent to the consumer.

To create this middleware extension point in our existing ReduxStore, we need to define a couple of interfaces. The first of these is IReduxMiddleware and defines a single method:

public interface IReduxMiddleware
{
    void Dispatch(IReduxMessage message, IReduxDispatcher next);
}

The Dispatch() method allows the middleware to act upon the supplied message, and the next parameter gives implementers a great level of flexibility. The message might be passed through unchanged, or it might be suppressed completely. The message might be replaced with a new message, or it might be supplemented with additional methods that are dispatched earlier or later.

The IReduxDispatcher interface simply allows for the message to be passed on:

public interface IReduxDispatcher
{
    void Dispatch(IReduxMessage message);
}

To allow registration of middleware, we add a registration method into IReduxStore:

void AddMiddleware(IReduxMiddleware middleware);

An alternative to this design would be for the ReduxStore constructor to accept all the defined middleware from the dependency injection container. This would introduce some difficulties around the order of middleware execution. Either we’d need to give up control of this (posing a problem for logging and exception management) or we’d need to extend our interface with some kind of priority or weighting (which is pretty nasty). Using an explicit Add() method is cleaner.

Next time, we’ll implement the changes to ReduxStore allowing the middleware to be used.

Prior post in this series:
Speech API
Next post in this series:
Redux Middleware Implementation

Comments

blog comments powered by Disqus