The adapter pattern and presenter-first UIs


The adapter pattern provides a technique to bind together the presenter logic and the UI screens of a presenter-first model (and other loosely coupled models) of user interface implementation. An example follows.

An application to double an integer requires a presenter function to perform the doubling on request:

public class Presenter
{
    public IInput Input { get; set; }
    public IOutput Output { get; set; }

    public void PerformDoubling()
    {
        int inputValue = Input.GetValue();
        int result = inputValue * 2;
        Output.SetValue(result);
    }
}

The presenter is using interfaces to access the user interface – which allows presenter unit tests to be built via mocks independent to the development of the UI:

public interface IInput
{
    int GetValue();
}

public interface IOutput
{
    void SetValue(int newValue);
}

Binding the interfaces to a deployed UI can then take the form of UI-specific adapters to the IInput and IOutput interfaces. I.e. for a Window1 class containing input and output accessors (implemented using explicit marshalling as described in this blog entry):

public partial class Window1 : Window
{
    ...

    public String ThreadSafeGetInputValue() ...
    public void ThreadSafeSetOutputValue(String newValue) ...
}

The following two adapters can be used to convert the UI to match the required interface without the need to modify the UI code. (Null and error checking has been excluded below to reduce code footprint:)

public class AdapterIInputWindow1 : IInput
{
    private Window1 Window1 { get; set; }

    public AdapterIInputWindow1(Window1 wrappedWindow)
    {
        Window1 = wrappedWindow;
    }

    // IInput implementation - excluding error checking
    public int GetValue()
    {
        int result = Convert.ToInt32(Window1.ThreadSafeGetInputValue());
        return result;
    }
}


public class AdapterIOutputWindow1 : IOutput
{
    private Window1 Window1 { get; set; }

    public AdapterIOutputWindow1(Window1 wrappedWindow)
    {
        Window1 = wrappedWindow;
    }

    // IOutput implementation - excluding error checking
    public void SetValue(int newValue)
    {
        Window1.ThreadSafeSetOutputValue(newValue.ToString());
    }
}

Finally the UI can initialize its presenter and set the IInput and IOutput interfaces to the appropriate adapters:

public partial class Window1 : Window
{
    private Presenter Presenter { get; set; }

    public Window1()
    {
        InitializeComponent();

        Presenter = new Presenter();
        Presenter.Input = new AdapterIInputWindow1(this);
        Presenter.Output = new AdapterIOutputWindow1(this);
    }


    public void XamlAction(object sender, EventArgs args)
    {
        Presenter.PerformDoubling();
    }
    ...
}
Advertisement

Covariance and contravariance in C# 4.0


C# 4.0 introduces covariance and contravariance (together variance) for generic type parameters. These two concepts are similar and allow the use of derived or base classes in a class hierarchy.

An easy way to understand the difference between the two concepts is to consider the activity of the user of the variables passed to / from the generic method (interface or delegate.)

Contravariance

If the method implementation is only using variables passed with the parameter for read activity – then the generic parameter is a candidate for contravariance. The ‘read only’ role of the parameter type can be formalised by marking it with the in keyword – i.e. it is an input to the implementation.

Any generic type parameter marked with the in keyword will be able to match to types that are derived from the named type. In this case the implementation is the user of the variables. It makes sense to allow derived classes as any read activities that are available on the base class will also be available on any derived class.

// C# 3.0 does not recognise the in keyword for parameterized types
public delegate void TypeReporter<in T>(T input);

public void DoSomeGenericContravariance(RoutedEventArgs args)
{
    TypeReporter<EventArgs> ReportMethod
                  = new TypeReporter<EventArgs>(this.SimpleReportMethod);

    // C# 3.0 and earlier will not compile the following
    TypeReporter<RoutedEventArgs> RoutedReportMethod = ReportMethod;

    RoutedReportMethod(args);
}

public void SimpleReportMethod(EventArgs args)
{
    Console.WriteLine(args.GetType().ToString());
}

Covariance

Similarly – if a method implementation is only using variables passed with the parameter for write activity – then the generic parameter is a candidate for covariance. The ‘write only’ role of the parameter type can be formalised by marking it with the out keyword – i.e. it is an output from the implementation.

Any generic type parameter marked with the out keyword will be able to match to types that are base classes of the named type. In this case the calling / client code can be considered the user of the variables – so again it makes sense. Any operation (read or write) that is available on the base class that the client code has requested will also be available on the actual (derived) type that the implementation instantiates / sends as output.

public void DoSomeGenericCovariance()
{
    // The generic IEnumerable interface is defined with the out keyword
    // on the parameter type:
    //
    // public interface IEnumerable<out T> : IEnumerable

    List<String> strings = new List<String> { "one", "two" };
    IEnumerable<String> myStrings = strings.AsEnumerable();

    // C#3.0 and earlier will not compile the following
    IEnumerable<object> myObjects = myStrings;

    foreach (object myObject in myObjects)
    {
        Console.WriteLine(myObject.ToString());
    }
}

Issues to consider

Once type parameters have been marked with the in or out keyword the compiler will validate the interface / delegate compliance with the assigned variance. E.g. if the first example is changed to return the parameter passed, then the compiler will report an error – as the parameter is not being used for input only.

// Compiler will report a variance validation error
public delegate T TypeReporter<in T>(T input);

Variance uses reference type conversion – so it will not work with value types. Even though within the type system int inherits from object, the following will not compile.

List<int> ints = new List<int> { 1, 2 };
IEnumerable<int> myInts = ints.AsEnumerable();

// Reference type conversion not available
IEnumerable<object> myIntObjects = myInts;

C# and NUnit – testing interface implementations


A common development strategy is to generate interface definitions and then code against these – allowing the implementations to be varied easily.

When writing NUnit test cases for interfaces and implementing classes – it is possible to generate a set of test cases for the interface and then apply these test cases to each concrete implementation. A strategy is outlined below.

Within the class library containing the test cases generate a test fixture for the interface itself. A class member, m_fileManager in this case, is used to hold the interface instance to test and a second member m_runTests is used to track whether the fixture holds an interface realisation.

A virtual function CreateConcrete is defined to create the concrete interface implementation – it is called in the SetUp method.

Add the interface test cases to this class – checking whether tests should by run using the m_runTests member.

[TestFixture]
public class Test_IFileManager
{

    /// Interface to use for testing
    protected IFileManager m_fileManager;

    /// Indicator that we are in a realisation test fixture
    protected bool m_runTests = false;

    /// Create a concrete instance
    virtual public void CreateConcrete()
    {
    }
        
    [SetUp]
    /// Test set up
    public void Setup()
    {
        CreateConcrete();
    }

    [TearDown]
    /// Test tear down
    public void TearDown()
    {
        m_fileManager = null;
        m_runTests = false;
    }

    [Test]
    /// A simple test
    public void NonExistentDirectoryThrowsException()
    {
        if (m_runTests)
        {
            Console.WriteLine("Running test: NonExistentDirectoryThrowsException");

            Assert.Throws<DirectoryNotFoundException>
                ( delegate
                  {
                    m_fileManager.GetFreeFileName(@"ZZ:\BadDrive\", "stem", "txt");
                  });
        }
    }
}

To attach the set of test cases to a concrete implementation, generate a class that inherits this base interface testing class. Override the CreateConcrete method to assign the interface instance and to indicate that the tests should be run.

[TestFixture]
public class Test_IFileManager_SimpleFileManager : Test_IFileManager
{

    /// Create a concrete instance
    override public void CreateConcrete()
    {
        // Using static method
        m_fileManager = (IFileManager) SimpleFileManager.GetFileManager();
        m_runTests = true;
    }

}

Each implementing class can be tested by creating a separate concrete implementation testing class. When NUnit runs it finds all of the interface tests and runs them for each concrete implementation.

If any implementation-specific test cases exist, then they can be added to the concrete testing class. If a class implements multiple interfaces, then each interface can be tested in a similar manner by generating a concrete implementation testing class for each interface / implementer pair.