Interfaces and design patterns - An Agile foundation - Adaptive Code via C# Agile coding with design patterns and SOLID principles (2014)

Adaptive Code via C# Agile coding with design patterns and SOLID principles (2014)

Part I: An Agile foundation

Chapter 3. Interfaces and design patterns

After completing this chapter, you will be able to

Image Define interfaces and identify the primary ways in which they differ from classes.

Image Apply design patterns, such as the Adapter and Strategy patterns, to interfaces.

Image Understand an interface’s versatility through duck-typing, mixins, and fluent interfaces.

Image Identify the limitations of interfaces and implement workarounds.

Image Spot common anti-patterns and abuses of interfaces.

The interface is a powerful construct in Microsoft .NET Framework development. Although the interface keyword is very simple, what it represents is a very powerful paradigm. When used correctly, interfaces provide your code with the extension points that make it extremely adaptive. However, some uses of interfaces are not so good; yet they remain in common use.

This chapter provides a reminder of the differences between classes and interfaces and describes how best to use the two together both to protect client code from implementation changes and to facilitate polymorphism.

It also covers the versatility of interfaces and how they are a ubiquitous tool in modern software solutions. This encompasses some powerful design patterns that, when applied correctly (in conjunction with other patterns in this book) yield code that is incredibly flexible and able to adapt to the changing requirements that Agile projects embrace.

Interfaces alone are, however, no panacea. A judicious sprinkling of interfaces can certainly help a project, but the interfaces must be applied in the correct manner. This chapter explores some of the common abuses of interfaces.

What is an interface?

An interface defines the behavior that a class has, but not how this behavior is implemented. Interfaces stand as separate constructs from classes, but they require a class to provide the working code to fulfill the interface.

Interfaces are defined by their syntax—that is, the language-construct side of the interface: the interface keyword and everything that this implies and entails. But they are also defined by their features: the concepts that they represent and enable.

Syntax

Interfaces are defined by using the interface keyword. They can contain properties, methods, and events, just as classes can. However, no element of an interface can be given an access modifier: the implementing class must implement an interface as public. Listing 3-1 shows the declaration of an interface, along with a possible implementation.

LISTING 3-1 Declaring and implementing an interface.


public interface ISimpleInterface
{
void ThisMethodRequiresImplementation();

string ThisStringPropertyNeedsImplementingToo
{
get;
set;
}

int ThisIntegerPropertyOnlyNeedsAGetter
{
get;
}

public event EventHandler<EventArgs> InterfacesCanContainEventsToo;
}
// . . .
public class SimpleInterfaceImplementation : ISimpleInterface
{

public void ThisMethodRequiresImplementation()
{

}

public string ThisStringPropertyNeedsImplementingToo
{
get;
set;
}

public int ThisIntegerPropertyOnlyNeedsAGetter
{
get
{
return this.encapsulatedInteger;
}
set
{
this.encapsulatedInteger = value;
}
}

event EventHandler<EventArgs> InterfacesCanContainEventsToo = delegate { };

private int encapsulatedInteger;
}


The .NET Framework does not support the concept of multiple inheritance of classes, but it does support multiple interface implementation for a single class.

There is no limit imposed on the number of interfaces that a class can implement; the number of interfaces that really make sense on one class is more of a practical concern. Listing 3-2 extends the prior example to implement a second interface on the implementing class.

LISTING 3-2 Multiple interfaces can be implemented by a single class.


public interface IInterfaceOne
{
void MethodOne();
}
// . . .
public interface IInterfaceTwo
{
void MethodTwo();
}
// . . .
public class ImplementingMultipleInterfaces : IInterfaceOne, IInterfaceTwo
{
public void MethodOne()
{
}

public void MethodTwo()
{
}
}


Though there can be multiple interfaces implemented on a single class, a single interface can similarly be implemented multiple times by different classes.


Multiple inheritance

Some languages support the concept of inheriting from multiple base classes. C++, in particular, allows this construct. However, .NET Framework languages do not permit it, and the compiler will generate a warning if a class attempts to inherit from two or more classes, as Figure 3-1 shows.

Image

FIGURE 3-1 Multiple inheritance is prevented by the compiler.

Diamond inheritance problem

One reason that multiple inheritance is disallowed is because of the diamond inheritance problem. This problem occurs when two or more classes are inherited by the same class. If each of those base classes contains identical methods, which one should be used? Figure 3-2 shows a Unified Modeling Language (UML) diagram of the problem.

Image

FIGURE 3-2 A UML diagram showing the diamond inheritance problem.

In this case, which version of RootMethod()should class AttemptedMultipleInheritance inherit—the one that BaseClassOne inherited from RootClass, or the one that BaseClass-Two inherited from RootClass? Because of this ambiguity, the .NET Framework does not allow multiple inheritance of classes.


Explicit implementation

Interfaces can also be implemented explicitly. Explicit interface implementation differs from implicit interface implementation, which is what was shown in the previous examples. Listing 3-3 shows the same example class from before, but this time it implements its interface explicitly.

LISTING 3-3 Implementing an interface explicitly.


public class ExplicitInterfaceImplementation : ISimpleInterface
{
public ExplicitInterfaceImplementation()
{
this.encapsulatedInteger = 4;
}

void ISimpleInterface.ThisMethodRequiresImplementation()
{
encapsulatedEvent(this, EventArgs.Empty);
}

string ISimpleInterface.ThisStringPropertyNeedsImplementingToo
{
get;
set;
}

int ISimpleInterface.ThisIntegerPropertyOnlyNeedsAGetter
{
get
{
return encapsulatedInteger;
}
}

event EventHandler<EventArgs> ISimpleInterface.InterfacesCanContainEventsToo
{
add { encapsulatedEvent += value; }
remove { encapsulatedEvent -= value; }
}

private int encapsulatedInteger;
private event EventHandler<EventArgs> encapsulatedEvent;
}


To use an explicitly implemented interface, clients must have a reference to an instance of the interface—a reference to an implementation of the interface will not suffice. Listing 3-4 explores this in further detail.

LISTING 3-4 When implemented explicitly, the interface methods are not visible on class instances.


public class ExplicitInterfaceClient
{
public ExplicitInterfaceClient(ExplicitInterfaceImplementation
implementationReference, ISimpleInterface interfaceReference)
{
// Uncommenting this will cause compilation errors.
//var instancePropertyValue =
//implementationReference.ThisIntegerPropertyOnlyNeedsAGetter;
//implementationReference.ThisMethodRequiresImplementation();
//implementationReference.ThisStringPropertyNeedsImplementingToo = "Hello";
//implementationReference.InterfacesCanContainEventsToo += EventHandler;

var interfacePropertyValue =
interfaceReference.ThisIntegerPropertyOnlyNeedsAGetter;
interfaceReference.ThisMethodRequiresImplementation();
interfaceReference.ThisStringPropertyNeedsImplementingToo = "Hello";
interfaceReference.InterfacesCanContainEventsToo += EventHandler;
}

void EventHandler(object sender, EventArgs e)
{

}
}


Explicit implementation is only really useful when you want to avoid a signature clash, when the class already possesses a method signature that must be implemented by an interface.

Every method that can be defined in the .NET Framework has a specific method signature. This signature helps to distinguish methods as unique and to differentiate methods that have been overridden. A method’s signature consists of its name and its parameter list. Note that a method’s access level, return value, abstract, or sealed status all affect the method signature. Listing 3-5 shows a variety of method signatures, some of which clash. Method signatures clash if they are equal in all aforementioned criteria. No class, interface, or struct can contain methods with clashing signatures.

LISTING 3-5 Some of these methods have the same signature.


public class ClashingMethodSignatures
{
public void MethodA()
{

}

// This would cause a clash with the method above:
//public void MethodA()
//{

//}

// As would this: return values are not considered
//public int MethodA()
//{
// return 0;
//}

public int MethodB(int x)
{
return x;
}

// There is no clash here: the parameters differ.
// This is an overload of the previous MethodB.
public int MethodB(int x, int y)
{
return x + y;
}
}


Properties, because they don’t have parameter lists, are differentiated solely on their name. Thus, two properties’ signatures clash if they possess the same name.

Imagine the class shown in Listing 3-6, which is needed to implement the aforementioned interface, InterfaceOne.

LISTING 3-6 The interface that this class needs to implement will cause method signature collisions.


public class ClassWithMethodSignatureClash
{
public void MethodOne()
{
}
}


First, because the method signatures are the same, you only need to add the interface implementation notation to the class declaration, as shown in Listing 3-7.

LISTING 3-7 Implicitly implementing the interface will allow reuse of the existing methods.


public class ClassWithMethodSignatureClash : IInterfaceOne
{
public void MethodOne()
{
}
}


Whenever a client calls the interface methods on this class, the same methods that are already in place will be used. An example of where this can be useful is when you are implementing the Model-View-Presenter (MVP) pattern in Windows Forms and adding an IView interface that requires a Close method to be implemented on a Form. Listing 3-8 shows this in practice.

LISTING 3-8 Sometimes the presence of a clashing method can be neatly capitalized on.


public interface IView
{
void Close();
}
// . . .
public partial class Form1 : Form, IView
{
public Form1()
{
InitializeComponent();
}
}


However, if the class needs to provide different method bodies for the interface implementation, the class should implement the interface explicitly, avoiding the method signature clash. Listing 3-9 shows a class with clashing methods that provides different implementations of those methods.

LISTING 3-9 Explicitly implementing an interface to avoid clashing method signatures.


public class ClassAvoidingMethodSignatureClash : IInterfaceOne
{
public void MethodOne()
{
// original implementation
}

void IInterfaceOne.MethodOne()
{
// new implementation
}
}


In a similar regard, if a class needs to implement two different interfaces that are unrelated but that both contain a method with the same signature, you can implement them both implicitly and share the same method implementation. Or, as shown in Listing 3-10, you can implement them both explicitly—for clarity—to demarcate which implementation belongs to which interface.

LISTING 3-10 When implementing two interfaces with a common method signature, only explicit implementation is sufficient.


public class ClassImplementingClashingInterfaces : IInterfaceOne, IAnotherInterfaceOne
{
void IInterfaceOne.MethodOne()
{

}

void IAnotherInterfaceOne.MethodOne()
{

}
}


Polymorphism

The ability to use an object of one type and have it implicitly act as if it were of a different type is called polymorphism. Client code can interact with an object as if it is one type when it is actually another. This programmatic sleight of hand is one of the most important tenets of object-oriented programming and underpins some of the most elegant, adaptive solutions to programming problems.

Figure 3-3 shows an interface representing the behavior of vehicles, and some possible implementing classes for cars, motorcycles, and speedboats. Note that each of these three vehicle types is quite distinct, but they all exhibit the same behavior due to their unifying interface.

Image

FIGURE 3-3 Interfaces pass on their behavior to implementing classes, enabling polymorphism.

In this example, vehicles are assumed to have an engine that can be started and stopped, some provision for steering, and the ability to accelerate. Polymorphism allows client code to hold a reference to an IVehicle interface and treat all concrete types as if they were the same. The details of how a car steers or accelerates compared to a motorcycle, or how a speedboat engine is started and stopped compared to a train, is irrelevant to the client. And this is a very good thing. In reality, we are all clients to this interface whenever we use a vehicle. Sure, a real interface for a vehicle is more nuanced than that which is shown here, but the principle is the same. Do you need to know how the engine of your car works in order to start and stop the engine? No, not at all. Those are all implementation details that can have no bearing on your knowledge of driving. That is good interface design.

The design patterns and interface features that make up the rest of this chapter all facilitate the creation of adaptive code. Polymorphism enables each one to be useful for any class that fulfils an expected interface, whether it has already been written or is yet to be conceived.

Adaptive design patterns

Design patterns were popularized by the Gang of Four1 book, Design Patterns (Addison-Wesley Professional, 1994). Despite the fact that this book is almost 20 years old (which is at least four ice ages in software development terms), it is still extremely relevant today. Some of the patterns have crossed over to be reclassified as anti-patterns, but others are used constantly and enhance the adaptability of code.

1 So called because of its four authors: Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.

Good design patterns are reusable collaborations between interfaces and classes that can be applied repeatedly in many different scenarios, across projects, platforms, languages, and frameworks. As with most notable best practices, design patterns are another theoretical tool that it is better to know than not know. They can be overused, and they are not always applicable, sometimes overcomplicating an otherwise simple solution with an explosion of classes, interfaces, indirection, and abstraction.

In my experience, design patterns tend to be either underused or overused. In some projects, there are not enough design patterns and the code suffers from a lack of discernable structure. Other projects apply design patterns too liberally, adding indirection and abstraction where the benefit is negligible. The balance is in finding the right places to apply the right patterns.

The Null Object pattern

The Null Object pattern is one of the most common design patterns—and it is one of the easiest to comprehend and implement. Its purpose is to allow you to avoid accidentally throwing a Null-ReferenceException and a plethora of null object checking code. The UML class diagram in Figure 3-4 shows how the Null Object pattern is applied.

Image

FIGURE 3-4 The Null Object pattern demonstrated as a UML class diagram.

Listing 3-11 shows some typical code that can throw a NullReferenceException.

LISTING 3-11 If you don’t check return values for null, there is a chance of throwing a NullReference-Exception.


class Program
{
static IUserRepository userRepository = new UserRepository();

static void Main(string[] args)
{
var user = userRepository.GetByID(Guid.NewGuid());
// Without the Null Object pattern, this line could throw an exception
user.IncrementSessionTicket();
}
}


Every client that calls IUserRepository.Get(Guid uniqueID) is in danger of throwing a null reference. In practice, this means that every client must check that the return value is not null, to avoid attempting to dereference null, causing a NullReferenceExceptionto be thrown. This would make the client shown previously look more like the code in Listing 3-12.

LISTING 3-12 Is checking against null really the responsibility of all clients?


class Program
{
static IUserRepository userRepository = new UserRepository();

static void Main(string[] args)
{
var user = userRepository.GetByID(Guid.NewGuid());
if(user != null)
{
user.IncrementSessionTicket();
}
}
}


The Null Object pattern indicates that you are placing too much unnecessary burden on all of the clients of IRepository. The more clients that use this method, the greater the probability of forgetting a null reference check. Instead, you should change the source of the problem to perform the check for you, as shown in Listing 3-13.

LISTING 3-13 The service code should implement the Null Object pattern.


public class UserRepository : IUserRepository
{
public UserRepository()
{
users = new List<User>
{
new User(Guid.NewGuid()),
new User(Guid.NewGuid()),
new User(Guid.NewGuid()),
new User(Guid.NewGuid())
};
}

public IUser GetByID(Guid userID)
{
IUser userFound = users.SingleOrDefault(user => user.ID == userID);
if(userFound == null)
{
userFound = new NullUser();
}
return userFound;
}

private ICollection<User> users;
}


First, this code attempts to retrieve the User from the in-memory collection, which is no change from the previous implementation. Now, though, you check whether the User instance returned is actually a null reference. If it is, you return a special subclass of the IUser type: theNullUser. This subclass overrides the IncrementSessionTicket method to do precisely nothing, as shown in Listing 3-14. In fact, a proper NullUser implementation overrides all methods to do as close to nothing as possible.

LISTING 3-14 The NullUser method implementations all do nothing.


public class NullUser : IUser
{
public void IncrementSessionTicket()
{
// do nothing
}
}


Additionally, whenever a method or property of the NullUser object is expected to return a reference to another object, it should return a special Null Object implementation of those types, too. In other words, all Null Object implementations should return recursive Null Object implementations. This obviates the need for any null reference checking in clients.

This also has the added benefit of reducing the number of unit tests that you need to write. Previously, when each client had to implement the check, there would also be concomitant unit tests to confirm that the check was in place. Instead, the repository implementation is unit tested to ensure that it returns the NullUser implementation.

The IsNull property anti-pattern

Sometimes the Null Object pattern involves adding a Boolean IsNull property to the interface. All real implementations of this interface return the value false for this property. The Null Object implementation of the interface returns true. Listing 3-15 shows how this might work, given the previous example.

LISTING 3-15 The IsNull property is only true for Null Object implementations.


public interface IUser
{
void IncrementSessionTicket();

bool IsNull
{
get;
}
}
// . . .
public class User : IUser
{
// . . .
public bool IsNull
{
get
{
return false;
}
}

private DateTime sessionExpiry;
}
// . . .
public class NullUser : IUser
{
public void IncrementSessionTicket()
{
// do nothing
}

public bool IsNull
{
get
{
return true;
}
}
}


The problem with this property is that it causes logic to spill out of the objects whose purpose is to encapsulate behavior. For example, if statements will start to creep into client code to differentiate between real implementations and the Null Object implementation. This obviates the whole purpose of the pattern, which is to avoid proliferating this logic to its various clients. Listing 3-16 is a typical example of this problem.

LISTING 3-16 Logic based on the IsNull property makes this an anti-pattern.


static void Main(string[] args)
{
var user = userRepository.GetByID(Guid.NewGuid());
// Without the Null Object pattern, this line would throw an exception
user.IncrementSessionTicket();

string userName;
if(!user.IsNull)
{
userName = user.Name;
}
else
{
userName = "unknown";
}

Console.WriteLine("The user's name is {0}", userName);

Console.ReadKey();
}


This can easily be fixed by encapsulating the name of a null user inside the NullUser class, as in Listing 3-17.

LISTING 3-17 With proper encapsulation, the IsNull property is obsolete.


public class NullUser : IUser
{
public void IncrementSessionTicket()
{
// do nothing
}

public string Name
{
get
{
return "unknown";
}
}
}
// . . .
static void Main(string[] args)
{
var user = userRepository.GetByID(Guid.NewGuid());
// Without the Null Object pattern, this line would throw an exception
user.IncrementSessionTicket();

Console.WriteLine("The user's name is {0}", user.Name);

Console.ReadKey();
}



Cascading Nulls

A requested feature of the C# language is that it mimic Groovy2 and include a “Cascading Nulls” operator. Consider the following code snippet.

2 Groovy is a dynamically typed Java variant (http://groovy.codehaus.org).

if(person != null && person.Address != null && person.Address.Country == "England")
{
// . . .
}

The theory is that it could be replaced with the following.

if(person?.Address?.Country == "England")
{
// . . .
}

The ?. operator thus becomes a way of safely dereferencing any object and, at worst, being handed a default(T) of the property type. I am not against any progression in the syntax of the language that others might find useful, but I would be reluctant to use this as an alternative to a proper Null Object implementation, for three reasons.

First, there are too many instances in which a default value of a type is simply not going to suffice. The previous example of using a sensible user name to avoid throwing a Null-ReferenceException illustrates that the alternative is not a default value but something more meaningful to the application.

Second, this would require all clients of this code to be programmed with the possibility of null in mind. Part of the reason to use the Null Object pattern is to obviate null checking and give you the freedom to dereference with impunity. If you reject the Null Object pattern in favor of the Cascading Nulls syntax, you open yourself up to forgetting to dereference again.

A third, more subjective reason is that such syntax would likely become ubiquitous. The occasional int? to represent an int with optional/reference semantics is fair enough, but littering the code with ?. for every dereference? No, thanks.

Given a proper Null Object implementation, this example could be written succinctly as shown here.

if(person.Address.Country == "England")
{
// . . .
}

This, surely, offers the most benefit: client code that is less obfuscated but that is safe from the perils of a NullReferenceException.


The Adapter pattern

The Adapter pattern allows you to provide an object instance to a client that has a dependency on an interface that your instance does not implement. An Adapter class is created that fulfills the expected interface of the client but that implements the methods of the interface by delegating to different methods of another object. It is typically used when the target class cannot be altered to fit the desired interface. This could be because it is sealed or because it belongs to an assembly for which you do not have the source. You can implement the Adapter pattern in two ways: by using the Class Adapter pattern or by using the Object Adapter pattern.

The Class Adapter pattern

Figure 3-5 shows the collaborating classes and interfaces used in the Class Adapter pattern.

Image

FIGURE 3-5 The UML class diagram for the Class Adapter pattern.

The Class Adapter pattern makes use of inheritance for the adapter—a subclass of the target class is what needs to be adapted to fit the expected interface of clients. Listing 3-18 shows how this works in practice.

LISTING 3-18 The Class Adapter pattern uses implementation inheritance.


public class Adaptee
{
public void MethodB()
{

}
}
// . . .
public class Adapter : Adaptee
{
public void MethodA()
{
MethodB();
}
}
// . . .
class Program
{
static Adapter dependency = new Adapter();
static void Main(string[] args)
{
dependency.MethodA();
}
}


This is the less common of the two types of Adapter pattern, mostly because developers are told to favor composition over inheritance. This is because inheritance, which is whitebox reuse, makes the subclass dependent on the implementation of a class, rather than merely on its interface. Composition, which is blackbox reuse, limits the dependency to the interface, so that the implementation can vary without adversely affecting clients.

The Object Adapter pattern

The Object Adapter pattern uses composition to delegate from the methods of the interface to that of a contained, encapsulated object. Figure 3-6 shows the collaborating classes and interfaces used in the Object Adapter pattern.

Image

FIGURE 3-6 The UML class diagram for the Object Adapter pattern.

Listing 3-19 shows how this works in practice.

LISTING 3-19 The adaptor accepts the target class as a constructor parameter and delegates to it.


public interface IExpectedInterface
{
void MethodA();
}
// . . .
public class Adapter : IExpectedInterface
{
public Adapter(TargetClass target)
{
this.target = target;
}

public void MethodA()
{
target.MethodB();
}

private TargetClass target;
}
//
public class TargetClass
{
public void MethodB()
{

}
}
// . . .
class Program
{
static IExpectedInterface dependency = new Adapter(new TargetClass());
static void Main(string[] args)
{
dependency.MethodA();
}
}


The Strategy pattern

The Strategy pattern allows you to change the desired behavior of a class without requiring recompilation, potentially even during run-time execution. Figure 3-7 shows the UML class diagram for the Strategy pattern.

Image

FIGURE 3-7 The UML class diagram of the Strategy pattern.

The Strategy pattern is used whenever a class needs to exhibit variant behavior depending on the state of an object. If this behavior can change at run time depending on the current state of the class, the Strategy pattern is a perfect fit for encapsulating this variant behavior. Listing 3-20shows how to create the Strategy pattern and use it in a class.

LISTING 3-20 The Strategy pattern in action.


public interface IStrategy
{
void Execute();
}
// . . .
public class ConcreteStrategyA : IStrategy
{
public void Execute()
{
Console.WriteLine("ConcreteStrategyA.Execute()");
}
}
// . . .
public class ConcreteStrategyB : IStrategy
{
public void Execute()
{
Console.WriteLine("ConcreteStrategyB.Execute()");
}
}
// . . .
public class Context
{
public Context()
{
currentStrategy = strategyA;
}

public void DoSomething()
{
currentStrategy.Execute();

// swap strategy with each call
currentStrategy = (currentStrategy == strategyA) ? strategyB : strategyA;
}

private readonly IStrategy strategyA = new ConcreteStrategyA();
private readonly IStrategy strategyB = new ConcreteStrategyB();

private IStrategy currentStrategy;
}


With every call that is made to Context.DoSomething(), the method first delegates to the current strategy and then swaps between strategy A and strategy B. The next call delegates to the newly selected strategy before again swapping back to the original strategy.

The way the strategies are selected is an implementation detail—it does not alter the net effect of the pattern: that the behavior of the class is hidden behind an interface whose implementations are used to perform the real work.

Further versatility

The utility of interfaces is not limited to design patterns. There are some other, more specialized uses of interfaces that are worth investigation. Though these features are not generally applicable, there are situations in which they are the right tool for the job.

Just as with design patterns, their overuse can be detrimental to the readability and maintainability of code. It is a difficult balancing act to find the correct number of collaborating patterns and techniques to solve a problem elegantly, so exercise some caution when using interfaces in the following ways.

Duck-typing

C# is a statically typed language, whereas duck-typing is more commonly found in dynamically typed languages. Duck-typing uses the duck test:

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

—James Whitcomb Riley

Applied to types in a programming language, the duck test suggests that, as long as an object exhibits the behavior of a certain interface, it should be treated as that interface. Unfortunately, this is not true by default in C#. Observe the example in Listing 3-21.

LISTING 3-21 Although the object Swan fulfills all of the methods of an IDuck, it is, in fact, not an IDuck.


public interface IDuck
{
void Walk();

void Swim();

void Quack();
}
// . . .
public class Swan
{
public void Walk()
{
Console.WriteLine("The swan is walking.");
}

public void Swim()
{
Console.WriteLine("The swan can swim like a duck.");
}

public void Quack()
{
Console.WriteLine("The swan is quacking.");
}
}
// . . .
class Program
{
static void Main(string[] args)
{
var swan = new Swan();

var swanAsDuck = swan as IDuck;

if(swan is IDuck || swanAsDuck != null)
{
swanAsDuck.Walk();
swanAsDuck.Swim();
swanAsDuck.Quack();
}
}
}


The is predicate and the as cast return false and null, respectively. The Common Language Runtime (CLR) does not consider a Swan as being an IDuck, even though it actually fulfils that interface. A type must implement the interface via interface inheritance.

There are a couple of tricks that you can employ to enable the Swan class to be usable as an instance of the IDuck interface without having to implement it. Either you can take advantage of the dynamic typing extensions in newer versions of the CLR, or you can make use of a third-party library called Impromptu Interface.

Using the Dynamic Language Runtime

As of version 4, the .NET Framework was no longer strictly statically typed. With the introduction of the dynamic keyword, and some supporting types, you can avoid the CLR’s static typing and switch to the dynamic typing of the Dynamic Language Runtime (DLR). An example of dynamic typing in C# is shown in Listing 3-22.

LISTING 3-22 The DLR can be used for duck typing.


class Program
{
static void Main(string[] args)
{
var swan = new Swan();

DoDuckLikeThings(swan);

Console.ReadKey();
}

static void DoDuckLikeThings(dynamic duckish)
{
if (duckish != null)
{
duckish.Walk();
duckish.Swim();
duckish.Quack();
}
}
}


Notice, however, that the method parameter is of type dynamic. Not only do you need to target the .NET Framework 4, dynamic typing needs to be designed into clients specifically. On both counts, this is sometimes infeasible. You can’t, for example, simply start creating all methods to take dynamic parameters everywhere, or else you would be better off using a dynamically typed .NET Framework language, such as IronPython3.

3 http://ironpython.net/

Using Impromptu Interface

Impromptu Interface is a .NET Framework library that can be installed via NuGet. After it is installed, you can use the ActLike<T>() method to pass in your Swan and receive an IDuck instance that delegates to your instance. Listing 3-23 shows how this works.

LISTING 3-23 Impromptu Interface allows duck-typing in C#.


class Program
{
static void Main(string[] args)
{
var swan = new Swan();

var swanAsDuck = Impromptu.ActLike<IDuck>(swan);

if(swanAsDuck != null)
{
swanAsDuck.Walk();
swanAsDuck.Swim();
swanAsDuck.Quack();
}

Console.ReadKey();
}
}


What the ActLike method is doing is creating a new type at run time by using Reflection Emit. This is a powerful part of the .NET Framework Reflection API that allows the creation of new types at run time. This new type fulfills the IDuck interface, but it also contains the Swaninstance as an encapsulated field. Whenever one of the IDuck interface methods is called, the new type trivially delegates to the Swan instance. It is, in effect, a run-time version of the Adapter pattern. Impromptu Interface is an automatic way of applying the Object Adapter pattern.

CLR duck-typing support

Interestingly, the CLR already supports duck-typing. Unfortunately, this is only for one uncommon case: implementing something that is enumerable. A type that is the target of the foreach loop must conform to a certain interface, but that interface is not formalized and can be implemented ad hoc on the target class. In the example in Listing 3-24, the Duck class is the target of a foreach loop.

LISTING 3-24 The CLR implicitly supports duck-typing for enumerable types.


class Program
{
static void Main(string[] args)
{
var duck = new Duck();

foreach (var duckling in duck)
{
Console.WriteLine("Quack {0}", duckling);
}

Console.ReadKey();
}
}


The Duck does not implement any interface, but the GetEnumerator() method is required by the foreach loop, as the compiler instructs, as shown in Figure 3-8.

Image

FIGURE 3-8 Without requiring a specific interface, the compiler complains that a public method is missing from the class.

When you implement this method with a void return type, you receive a new error stating that this type does not support some other required methods and properties, as shown in Figure 3-9.

Image

FIGURE 3-9 The return type of the GetEnumerator method must also match an implicit contract.

These properties can then be implemented as shown in Listing 3-25.

LISTING 3-25 Completing the implied interface of the DuckEnumerator class.


public class DuckEnumerator
{
int i = 0;

public bool MoveNext()
{
return i++ < 10;
}

public int Current
{
get
{
return i;
}
}
}


At this point, you have successfully implemented the implicit interface that the foreach requires—duck-typing in action!

Mixins

An extension of duck-typing is the concept of the mixin. A mixin is a class that contains the implementations from multiple other classes, without using implementation inheritance. As you have already learned, multiple implementation inheritance is not supported by C#, so you must look at other solutions for implementing mixins.

One trivial but limited way of implementing mixins is to use extension methods. This allows you to add methods to a type that has already been defined, which can be very useful. An alternative is to use a third-party library such as Re-motion Re-mix, which operates much like Impromptu Interface in that it generates a new type at run time that contains all of the interfaces you specify and acts as a multifaceted adapter.

Using extension methods

Since the .NET Framework 3.5, extension methods have allowed the addition of new functionality to already existing types. Without needing to access the source of a type, nor requiring the type to be a partial definition, you can extend a type. Listing 3-26 shows the interface that will be enhanced in this section, along with a pair of trivial extension methods.

LISTING 3-26 Extension methods can enhance an existing interface.


public interface ITargetInterface
{
void DoSomething();
}
// . . .
public static class MixinExtensions
{
public static void FirstExtensionMethod(this ITargetInterface target)
{
Console.WriteLine("The first extension method was called.");
}

public static void SecondExtensionMethod(this ITargetInterface target)
{
Console.WriteLine("The second extension method was called.");
}
}


Now, whenever a client has access to an ITargetInterface instance, and it also references the MixinExtensions class, these two extension methods will be available. Any number of extension methods can be created, spread across multiple static classes. Listing 3-27 shows another pair of extension methods; this time they take extra parameters.

LISTING 3-27 Extension methods can take parameters.


public static class MoreMixinExtensions
{
public static void FurtherExtensionMethodA(this ITargetInterface target, int
extraParameter)
{
Console.WriteLine("Further extension method A was called with argument {0}",
extraParameter);
}

public static void FurtherExtensionMethodB(this ITargetInterface target, string
stringParameter)
{
Console.WriteLine("Further extension method B was called with argument {0}",
stringParameter);
}
}


These extension methods can be called just like any other by any client, as in Listing 3-28.

LISTING 3-28 Clients have access to extension methods as if they were declared directly on the target interface.


public class MixinClient
{
public MixinClient(ITargetInterface target)
{
this.target = target;
}

public void Run()
{
target.DoSomething();
target.FirstExtensionMethod();
target.SecondExtensionMethod();
target.FurtherExtensionMethodA(30);
target.FurtherExtensionMethodB("Hello!");
}

private readonly ITargetInterface target;
}


There are a few notable limitations to this approach to mixins. The first is the testability of the client. As Chapter 4, “Unit testing and refactoring,” will show, static classes do not lend themselves to be easily mocked. This makes all clients of these extension methods more difficult to properly unit test.

Worse still, also due to extension methods being static classes, they cannot hold any extra per-instance state related to the object. Sure, there are workarounds, such as storing a static dictionary that maps instances to some extra values, but this is not ideal.

Notice also that the extension methods are all targeting the same interface, and that all instances to be enhanced must implement this interface. True mixins, on the other hand, implement multiple different interfaces and act as aggregate adapters.

Using Re-motion Re-mix

An alternative way of implementing mixins is by using a third-party library such as Re-motion Re-mix. Re-mix allows you to specify, via run-time configuration, which classes to combine when creating a new instance of a certain target class. As with Impromptu Interface, it generates a new type on the fly that fulfills all of the interfaces present on the mixins requested, with each instance of this type delegating to an instance of the mixin whenever an interface method is called. The interfaces, and some sample implementations, are shown in Listing 3-29.

LISTING 3-29 The disparate interfaces to be combined as a mixin.


public interface ITargetInterface
{
void DoSomething();
}
// . . .
public class TargetImplementation : ITargetInterface
{
public void DoSomething()
{
Console.WriteLine("ITargetInterface.DoSomething()");
}
}
// . . .
public interface IMixinInterfaceA
{
void MethodA();
}
// . . .
public class MixinImplementationA : IMixinInterfaceA
{
public void MethodA()
{
Console.WriteLine("IMixinInterfaceA.MethodA()");
}
}
// . . .
public interface IMixinInterfaceB
{
void MethodB(int parameter);
}
// . . .
public class MixinImplementationB : IMixinInterfaceB
{
public void MethodB(int parameter)
{
Console.WriteLine("IMixinInterfaceB.MethodB({0})", parameter);
}
}
// . . .
public interface IMixinInterfaceC
{
void MethodC(string parameter);
}
// . . .
public class MixinImplementationC : IMixinInterfaceC
{
public void MethodC(string parameter)
{
Console.WriteLine("IMixinInterfaceC.MethodC(\"{0}\")", parameter);
}
}


Note that there is no single class that implements all of these interfaces. Instead, the next step is to configure Re-mix so that, when it is asked for an instance of TargetImplementation, it will return a mixin containing all of the interfaces and classes combined. Listing 3-30 shows such a configuration.

LISTING 3-30 Instructing Re-mix how to construct TargetImplementation instances.


var config = MixinConfiguration.BuildFromActive()
.ForClass<TargetImplementation>()
.AddMixin<MixinImplementationA>()
.AddMixin<MixinImplementationB>()
.AddMixin<MixinImplementationC>()
.BuildConfiguration();

MixinConfiguration.SetActiveConfiguration(config);


Unfortunately, you cannot simply call new on the TargetImplementation and expect a mixin. Instead, you have to ask Re-mix to create a TargetImplementation instance so that it can build a new type to your specification and instantiate it. Listing 3-31 shows how it can do that—and luckily, it is trivial.

LISTING 3-31 Re-mix is in charge of creating mixins.


ITargetInterface target = ObjectFactory.Create<TargetImplementation>(ParamList.Empty)


One of the limitations of Re-mix is that you do not—and cannot—know the exact type of the instance returned by ObjectFactory.Create. All you know is that it is an instance of a subclass TargetImplementation. This is sort of bad news for clients, because the only interface you can guarantee that TargetImplementation fulfills at compile time is ITargetInterface. Clients of the mixin, therefore, must type-sniff by using is and as to cast the mixin to the desired interface. Listing 3-32 highlights this problem.

LISTING 3-32 Type-sniffing is bad practice but necessary for using mixins.


public class MixinClient
{
public MixinClient(ITargetInterface target)
{
this.target = target;
}

public void Run()
{
target.DoSomething();

var targetAsMixinA = target as IMixinInterfaceA;
if(targetAsMixinA != null)
{
targetAsMixinA.MethodA();
}

var targetAsMixinB = target as IMixinInterfaceB;
if(targetAsMixinB != null)
{
targetAsMixinB.MethodB(30);
}

var targetAsMixinC = target as IMixinInterfaceC;
if(targetAsMixinC != null)
{
targetAsMixinC.MethodC("Hello!");
}
}

private readonly ITargetInterface target;
}


Applying mixins to a solution works best when type-sniffing is already present or necessary. This is true with some libraries and frameworks. For example, Prism (the Windows Presentation Foundation/Model-View-Viewmodel library) makes use of type-sniffing, and the functionality required of client classes can be segregated into different implementations and recombined via mixins.

Fluent interfaces

An interface is said to be fluent if it returns itself from one or more of its methods. This allows clients to chain calls together, as shown in Listing 3-33.

LISTING 3-33 Fluent interfaces allow method chaining.


public class FluentClient
{
public FluentClient(IFluentInterface fluent)
{
this.fluent = fluent;
}

public void Run()
{
// without using fluency
fluent.DoSomething();
fluent.DoSomethingElse();
fluent.DoSomethingElse();
fluent.DoSomething();
fluent.ThisMethodIsNotFluent();

// using fluency
fluent.DoSomething()
.DoSomethingElse()
.DoSomethingElse()
.DoSomething()
.ThisMethodIsNotFluent();
}

private readonly IFluentInterface fluent;
}


This improves readability because it avoids repeated references to the instance of the interface. It is an increasingly popular way to implement configuration or finite state machines, as described in Chapter 8, “Interface segregation.”

Fluent interfaces are easy to implement, too. All the class has to do is return this from the method. Because the class is already an implementation of the interface, by returning this, the class returns only the interface portion of itself, thus hiding the rest of the implementation. Listing 3-34 shows the definition of the IFluentInterface and the implementation used in this example.

LISTING 3-34 Implementing a simple fluent interface is easy.


public interface IFluentInterface
{
IFluentInterface DoSomething();

IFluentInterface DoSomethingElse();

void ThisMethodIsNotFluent();
}
// . . .
public class FluentImplementation : IFluentInterface
{
public IFluentInterface DoSomething()
{
return this;
}

public IFluentInterface DoSomethingElse()
{
return this;
}

public void ThisMethodIsNotFluent()
{

}
}


Note that one of the methods of the interface is not fluent—it returns void. If a method returns anything but an interface, it is not fluent. Any chaining of methods that a client does will be halted by a call to a method that is not fluent.

Conclusion

In this chapter, you have learned what interfaces are and why they are such a key facet of writing adaptive code. They are catalysts for polymorphism, which allows you to encapsulate variation in families of classes. They are the root around which design patterns grow. And yet they actuallydo nothing.

Remember, interfaces are useless without accompanying implementations. But without interfaces, implementations—and their associated dependencies—would infiltrate and pollute your code, making it hard to maintain and extend. A well-placed interface acts as a firewall between the dirty implementation details of service code and a clean, well-organized client.

Interfaces also have other, more specialized features, such as duck-typing and mixins. These are seldom used, but when applied in the right context, they can simplify otherwise convoluted code and add an extra dimension of adaptability.

The groundwork laid in this chapter will be of great importance as you experience the ubiquity of the interface throughout the rest of the book.