Interface Overloading - TDD Against UI

Interface Overloading - TDD Against UI

WTF...

... is interface overloading. Or Overriding. I kinda toggle between the terms. Right now I'm favoring Interface Overloading; so that's what the title is.

Interface Overloading

Interface overloading is the process I've found in C# and JAVA and Swift to allow TDD when using Libraries and OS Systems. I figure it'll work in most (if not all) languages with interfaces. Except GoLang; this is kinda how GoLang works.

It's a mechanism I stumbled across while TDDing up an android app.
I wrote this tdd-against-android-widgets post while I first found it. It was part of a long and twisted path... Which I think resulted in an excellent tool.

Simple Definition

Extending a class and implementing an interface of a base class method allowing passing around as an interface.

The driving forces behind this discovery is TDD, Extreme Encapsulation, and Clean Architecture.

Mix-In

The basic idea really comes into a mix-in style that gives functionality as required to classes you don't control.

Where I discovered this, and I'm confident it will have significant impact is the UI. It can be utilized against Library and OS classes as easily, but I haven't encountered any cases yet that scream "WIN" as loudly as UI does.

Example

Let's go for a simple example that was TDD'd. The code link is available at the end of the post.

The base class
public class BaseClass 
{
     public string TheMethod(int intVal, bool boolVal) => $"StringVal {intVal} {boolVal}";
}

A simple example with just one method. TheMethod. It builds a string and returns it.

TheMethod is not a virtual method so it's unable to be mocked.
We need a way to pass this around. Really; this example is overly simplified. A MAJOR reason it works so well for UI, is that those often tie themselves to the UI thread, which makes calling textBox.Text = "example"; in a unit test will throw an exception. In Android, it's due to the OS not actually being there.
For C#, I'm working in UWP; and it's a "Ui Changed on non-ui thread" exception.
I suspect very similar if working in WinForms. In general; the UI is thread restricted which forces what I've seen from some frameworks; the ability to force a specific thread.

Now that we have a method we want to use in a method; we need to create the interface to hook into it.

public interface ITheInterface {
   string TheMethod(int intVal, bool boolVal);
}

Yep; same signature. Not a lot to go over here. It's an interface with the same definition... yeah.

OK; next up!

public class WrapperClass : BaseClass, ITheInterface { }

And here we have our Wrapper class. It's the object we'll use in our UI.
In android it'll look like

<com.quantityandconversion.widget.WrapperClass 
  android:id="@+id/tv_score_value"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

that can be seen here
and in XAML it'll look like

<userControls:WrapperClass x:Name="TxtTitle" Canvas.Top="1" Canvas.Left="1" HorizontalAlignment="Left" HorizontalContentAlignment="Left"/>

and can be seen here
The base class of these are have been TextBox in actual implementation; and did the Interface Overload for setting text.

Both of the above repo's can be explored to see how I've done used it.

Use this WrapperClass control

Using Android; we'll look at the TopItemsAdapter from my HackerNews Reader experiment project.
We retrieve the object like so in our TopItemsAdapter .ViewHolder

/* package */ static class ViewHolder extends RecyclerView.ViewHolder{
    private QacTextView points;
    private QacTextView comments;
    private QacTextView time;
...
    /* package */ ViewHolder(final View itemView) {
        super(itemView);
...
        points = (QacTextView)itemView.findViewById(R.id.tv_score_value);
        comments = (QacTextView)itemView.findViewById(R.id.tv_comments);
        time = (QacTextView)itemView.findViewById(R.id.tv_posted_time);
    }
}

And we'll use these control in the Item class; which I won't reproduce the entirety of here; just some relevant methods

public class Item {
...
   public void postTimeInto(final SetText item) {
        postTime.postTimeInto(item);
    }

    public void commentCountInto(final SetText item) {
        itemComments.commentCountInto(item);
    }

    public void scoreInto(final SetText item) {
        itemScore.scoreInto(item);
    }
...
}

We can see the use of the SetText interface in these methods.
The way this is used by the `TopItemsAdapter' is by passing the correct control into each method

 @Override
    public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
        final Item story = topItemsActivityMediator.itemAt(position);
...
        story.commentCountInto(viewHolder.comments);
        story.scoreInto(viewHolder.points);
        story.postTimeInto(viewHolder.time);
...
    }

and with this pattern in place; you maintain encapsulation, and can still produce the data to be displayed.

Examples

The available simple examples are available in my InterfaceOverride git repo.

Summary

This isn't a complex way to abstract classes you don't control. It's just a really useful one... at least very useful in the UI... for me. I hope it can help others TDD more of the application. Other tricks may be required for other things; but that's a post for another day.

This was a really quick write up; happy to improve and answer questions about the process.

UPDATE

This is a major component in my Hotel Pattern to maintain Clean Architecture.

Show Comments