Abstract Classes Versus Interfaces

Probably the most C# common interview question in London is “What’s the difference between an abstract class and an interface?”.  Let’s be clear: if you use the word contract in your answer, you’re not getting a job if I’m asking the question.  Contracts are a concept that doesn’t directly correspond to any code construct in C#, abstract classes and interfaces are your fundamental building blocks of well written code.

Here’s the one word answer:  Code.  Abstract classes can have code, interfaces can’t.  C#, like Java, doesn’t allow you to pull code from two sources, so you can only inherit from one abstract class.  Equally, the very presence of code tends to form a template for inheritors, which can be good or bad depending on context.

Fragility

Most people think of abstract classes as producing more fragile solutions than interfaces, simply because more decisions are made up front.  That’s true in the case of a parameter that’s an abstract class, but actually the opposite is true when you choose to inherit from an interface.

Here’s the problem: what happens when add a method to the interface?  As Erich Gamma discusses here, every implementation breaks.  That’s not so bad if every class that implements the interface is actually loaded into Visual Studio: then you can just fix them.  On the other hand, if you’ve not got your entire code-base loaded or, worse, you’re writing a framework, you’ve got broken code on your hands.

If everyone inherits from an abstract, the new method can be added to the abstract class without breaking any inheritors.  Now you know why Microsoft are so fond of abstract classes.

The Third Way

The thing with abstraction is, you can to some extent have your cake and eat it too.  Just because you create an IGraph interface doesn’t mean you can’t also create a PureBaseGraph abstract class with no implementation of any of the methods.  You’ve got the ultimate flexibility of the interface, but you can choose convenience and stability of an abstract class.  Microsoft don’t favour this approach, because they know that when they change an interface they’ll be deluged with complaints from developers who don’t want to ever change their code.  NHibernate and Castle Windsor, on the other hand, do, as Oren outlines here.  Using EmptyInterceptor avoids being exposed to changes in the interface, but you can run with scissors if you really want.

So, do I get the job?

SOLID Principles: Types of Abstraction

When I first try to demonstrate the power of abstraction to developers, often they get confused.  Partly this is because they’re unused to thinking in these terms, but partly it’s because there’s so many different types.  Ultimately, it’s a case of tools for the job: you pull out a hammer when you’ve got a nail, a screwdriver when you’ve got a screw.  The problem is:  they can all be combined together in useful ways.  There’s no such thing as a hammer than you twist, but abstract classes and interfaces can be used together in the one solution.

Parameterization

Everyone understands parameterization.  If you’ve got a routine that writes to a file, the routine is more general if it takes the destination filename as a parameter than if it hard-codes it in.  What not everyone does is take it far enough: we’ve already talked out parameterizing dependencies (aka dependency injection), but even for primitive types, there’s more parameterization that can be done.  A classic case of this is the class that reads a string from a config file.  Just passing the string in as a parameter is more general and more flexible.

Inheritance and Abstract Classes

Abstract classes allow for generality with the re-use of code.  Unless you make absolutely every method virtual (which, typically, you should) they’re not as general as interfaces.  The single inheritance restriction somewhat limits their usefulness as well.

Interfaces

Operationally, interfaces are almost exactly like abstract classes with no code and every method declared virtual.  However, one class can implement multiple interfaces, which we’ll see later helps us support the Interface Segregation Principle.

Dynamic Types

Dynamic types aren’t in C# yet, but will be soon.  Here, you can call any method, but it’s resolved (or fails to resolve) at runtime.  That’s equivalent to having your own custom interface you don’t need to declare.  It might well be worth using code contracts to actually check that your dynamic object behaves the way you think it does.

Any old VB programmer knows this concept already: it’s late binding.  He’s probably also quite wary of it.  The average python or ruby programmer knows it already and is a lot more comfortable with it.  Us C# programmers are going to have a lot to learn from them over the next few years.

Generics

Generics are the most powerful tool in the C# armoury, and due to become more powerful with C#4.  Here, you’ve got the benefits of abstraction, but you can specialize later.  So, your original base class can require a TWebRequest, which is declared to be a type that inherits from WebRequest, but you can inherit from this and then restrict TWebRequest to be an HttpWebRequest.

Generics in C# were created to support Nullable<T> and Collection<T>, but they’re much, much, more powerful than that.  Any parameter can be made generic, even if it’s an interface already.

The major problem with generics right now is the type system is cussed.  For instance, IEnumerable<int> can’t be case to IEnumerable<object>, despite the fact that this would cause no problems.  On the other hand, List<int> will never be castable to List<object>, because that would imply that you could add a float to a List<int>.

If there’s a lesson to be learned here, it’s that you still need to think about abstraction when actually instantiating a generic.  A list of an interface type is still more general than a list of abstract type.  A list of abstract type is still more general than a list of a concrete type.

Delegates

In my experience, people are quite uncomfortable with delegate abstraction.  Coming from a maths background, the idea of parameterizing one function with another is second nature.  A functional programmer won’t show many problems with the concept either.

However, there’s actually a very easy way to think of it.  Imagine you had an interface that had exactly one method and passed in the interface instead.  It behaves exactly the same.

There’s one other problem with delegates as dependencies: Castle Windsor doesn’t really inject them naturally.

Technorati Tags: ,

SOLID Principles: D is for Dependency Inversion

When we looked at the Open / Closed principle, we saw that certain practices produced fragile code.

  • Static Methods
  • Non-virtual methods
  • Creating objects
  • Too-specific variable declarations
  • Hard-coded values
  • Using the wrong object (the Law of Demeter)

Of these, the first three are all things you can’t override.  If you can’t override things, they’re inflexible and, inevitably, will cause problems.  In the cases we’ve seen, the Law of Demeter problem could be solved just by using the right object in the first place.  Equally, a non-virtual method is easy to fix, you just make it virtual.  You might be thinking “but that line of reasoning would make every method virtual” and you’d be right.  Private methods are pretty much the only case in which you don’t need this: because you can’t change a private method anyway.

Making Dependencies Explicit

Just for a second, let’s go back to the example of the code that printed some lines to the console.  For the code to run, it needed two things: the lines themselves, and the console object.  If we’d had some formatting rules, we could have obtained them from some instance variables.  If we were writing to a file, we’d have need the StreamWriter object we created.  These are all examples of direct dependencies:

  • Static methods (and by extension, singletons)
  • Objects we create
  • Method Parameters
  • Instance variables

Now, to go all Animal Farm on you for a second, two of these are good and two of these are bad.  Static methods and objects we create can’t be changed, so they’re bad.  Method parameters are easy to change: just change what you pass into the function.  Instance variables are typically set either by the constructor, a property set or another method call.  In each case, it comes down to a parameter you can change, so it’s good.  Obviously, static methods and object creation has to happen somewhere, but there’s ways around that.  You can wrap static methods with an object.  You can then pass the wrapping object as a parameter.  You can do the same thing with object creation.  Then, of course, you’re using an abstract factory.  You’ll have noticed that the discussion of SOLID principles is heading towards the same territory as the discussion of GoF design patterns.  This is no accident, but I’ll cover that in more detail later on.

By doing this, we see something really interesting.  All of a sudden, everything we depend on in an object is being passed in to the object.  If we return to my naive interpretation of OOD, it’s completely the other way around.  There, if a an object/actor needed another actor, he just got it, created it, whatever you like.  Now, my object is always being told what to do.  The actor is finally actually reading his lines.

It gets even better: hard coded values are dependencies as well.  Again, the same solution presents itself: pass it in.  Most people get the value of this when they start building tests for it, but it’s not about testing, it’s about flexibility.  You can filter and buffer function calls by introducing decorators, you can change your real time data feed to a batch CSV import without altering any code.

Using Abstractions

We’ve already touched on how making variable declarations too specific can cause problems.  In general terms, you want to declare variables with the most general type you can.  In the file reading example, the correct parameter declaration for the input was IEnumerable<string>, rather than string array or “ILineProvider”.  Abstraction is at the heart of the SOLID principles.  If you declare something as IEnumerable<string>, more people can use your code easily than string array or “ILineProvider”.  Another way of thinking about it is that you’ve made as few decisions as you can and still get work done.  This directly analogous to the Agile principle of the Last Responsible Moment.

There are challenges associated with abstract code.  The first is to get your own head round it.  If you’re a mathematician, this is a lot easier: you’ve got years of training in it.  The second is team acceptance.  Because abstract code is harder to immediately grasp, it’s often derided as “unreadable”.  In fact, well written abstract code can actually be significantly more readable than concrete code.  If you think about sorting, would you rather have to implement a sorting algorithm every time you needed to sort something, or use a well-defined abstract algorithm and simply tell it how you wanted the comparison to be done?  If you think about LINQ’s OrderBy method, there’s actually two layers of abstraction:

  • It takes the fields you specify and turns them into a Comparison delegate.
  • It applies a sort algorithm to the list using the Comparison delegate.

Depend on Abstractions, not Concretions

I’ve split dependency inversion into two parts: the injection of dependencies and the abstraction of dependencies.  Although both are of use separately, it’s when we bring it together that it really starts to pay off.  Using dependency injection without abstraction gives you some flexibility (e.g. the ability to read from a different file) but often not as much as you’d get from adding abstraction (e.g. the ability to read from a URL as well).  Abstraction without dependency injection is, in many ways, worse, because you often get the illusion of flexibility without the benefits.  You’re still performing wiring by soldering the lamp into the power socket.  The fact that the power socket could power a toaster isn’t much use: the lamp can’t be pulled out without damage.

Understanding the power of abstraction and how to use it is key to all serious development.  There are many things that don’t benefit: administration scripting (when you’ve got no re-use cases), project planning, end-user testing, work prioritisation… but if you want to be a better coder, you’ve got to learn abstraction.

Coding Without Resharper

Davy Brion asked if his readers could go back to not using Resharper.  Sadly, I don’t have to imagine this.  I deal with this on a regular basis.

You see, although my firm has enough Resharper licences to go around, many developers just plain don’t like it.  The objection is simple: it slows up the dev environment.  This isn’t an idle objection either: the slowdown is real.  I think we’ll probably have to wait for VS2010’s new extension model to see a proper fix for this.

However, personally I think the performance cost is incredibly worth it, because it saves my cycles even if it doesn’t save the processor’s cycles.  Little things that I use all the time:

  • Generate a field from a constructor parameter.  (Alt-Enter Enter)
  • Generate a property from a field.  (Alt-Enter Enter)
  • Automatically add a new interface method to an implementation.  (Alt-Enter Enter)
  • Find Usages (Shift-F12)
  • Find current file in solution explorer (Alt-Shift-L)
  • Implement Interface (Alt-Enter Enter)
  • Generate Method Stub (Alt-Enter Enter)
  • Add parameter to function when you changed a call to it.  (You guessed it: Alt-Enter Enter)

That’s without mentioning the “Automatically Add Namespace” feature.  (Alt-Enter Enter or just click on the tool-tip.)  This single feature probably saves a good five to ten minutes when dealing with an unfamiliar code base.  Just try grabbing most of the code samples from blogs and getting them to compile without this feature.

Before Resharper, one of my big secrets as a developer was that I could remember a lot of the mechanical edits I needed to make to effect a change.  Now, I spend more of my time thinking about what my code actually does, rather than how to wire it up correctly.

So, what’s it like without Resharper?  In a word, mechanical.  Code just gets stuck together slower.  You find yourself constantly writing bottom up code to help out intellisense.  Until you can’t remember what namespace a type is in.  At which point you spend your time guessing and googling something that would take would have taken under a second to correct with R#.

Now, if only they could fix that really annoying hang when you first delete a file…

Technorati Tags: