28

Do not declare interfaces for immutable objects

[EDIT] Where the objects in question represent Data Transfer Objects (DTOs) or Plain Old Data (PODs)

Is that a reasonable guideline?

Up to now, I've often created interfaces for sealed classes that are immutable (data cannot be changed). I've tried to be careful myself to not use the interface anywhere where I care about immutability.

Unfortunately, the interface begins to pervade the code (and it's not just my code I'm worried about). You wind up being passed an interface, and then wanting to pass it to some code that really wants to assume that the thing being passed to it is immutable.

Because of this problem, I'm considering never declaring interfaces for immutable objects.

This might have ramifications with respect to Unit Testing, but other than that, does this seem a reasonable guideline?

Or is there another pattern I should be using to avoid the "spreading-interface" problem I'm seeing?

(I'm using these immutable objects for several reasons: Mainly for thread safety since I write a lot of multi-threaded code; but also because it means I can avoid making defensive copies of objects passed to methods. Code becomes a lot simpler in many cases when you know something is immutable - which you don't if you've been handed an interface. In fact, often you can't even make a defensive copy of an object referenced via an interface if it doesn't provide a clone operation or any way of serialising it...)

[EDIT]

To provide a lot more context for my reasons for wanting to make objects immutable, see this blog post from Eric Lippert:

Link

I should also point out that I'm working with some lower-level concepts here, such as items that are being manipulated/passed around in multi-threaded job queues. These are essentially DTOs.

Also Joshua Bloch recommends the use of immutable objects in his book Effective Java.


Follow Up

Thanks for the feedback, all. I've decided to go ahead and use this guideline for DTOs and their ilk. It's working well so far, but it's only been a week... Still, it's looking good.

There are some other issues relating to this that I want to ask about; notably something I'm calling "Deep or Shallow Immutability" (nomenclature I stole from Deep and Shallow cloning) - but that's a question for another time.

16
  • 3
    +1 great question. Please explain why it is so important to you that the object is of that particular immutable class rather than something that merely implements the same interface. It's possible that your problems are rooted elsewhere. Dependency Injection is extremely important for unit testing, but it has many other important benefits which all go away if you force your code to require a particular immutable class. Mar 6, 2013 at 13:14
  • 2
    I'm slightly confused by your use of the term immutable. Just to be clear, do you mean a sealed class, which cannot be inherited and overridden, or do you mean that the data it exposes is read-only and cannot be changed once the object is created. In other words, are you wanting to guarantee that the object is of a particular type, or that its value will never change. In my first comment, I assumed to meant the former, but now with your edit, it sounds more like the latter. Or are you concerned with both? Mar 6, 2013 at 13:33
  • 3
    I'm confused, why not just leave setters off the interface? But on the other hand, I do tire of seeing interfaces for objects that are really domain objects and/or DTOs requiring factories for those objects...causes cognitive dissonance for me. Mar 6, 2013 at 15:28
  • 2
    What about interfaces for classes that are all immutable? For example, Java's Number class allows one to define a List<Number> which can hold Integer, Float, Long, BigDecimal, etc... All of which are immutable themselves.
    – user40980
    Mar 6, 2013 at 15:28
  • 1
    @MikeBrown I guess that just because the interface implies immutability doesn't mean the implementation enforces it. You could just leave it up to convention (i.e. document the interface that immutability is a requirement) but you could end up with some really nasty issues if someone violated the convention. Mar 6, 2013 at 15:37

5 Answers 5

18

In my opinion, your rule is a good one (or at least it's not a bad one), but only because of the situation you are describing. I wouldn't say that I agree with it in all situations, so, from the standpoint of my inner pedant, I'd have to say your rule is technically too broad.

Typically you wouldn't define immutable objects unless they are essentially being used as data transfer objects (DTO), which means that they contain data properties but very little logic and no dependencies. If that is the case, as it seems it is here, I'd say you are safe to use the concrete types directly rather than interfaces.

I'm sure there will be some unit-testing purists who will disagree, but in my opinion, DTO classes can be safely excluded from unit-testing and dependency-injection requirements. There is no need to use a factory to create a DTO, since it has no dependencies. If everything creates the DTOs directly as needed, then there's really no way to inject a different type anyway, so there's no need for an interface. And since they contain no logic, there's nothing to unit-test. Even if they do contain some logic, as long as they have no dependencies, then it should be trivial to unit-test the logic, if necessary.

As such, I think that making a rule that all DTO classes shall not implement an interface, while potentially unnecessary, is not going to hurt your software design. Since you have this requirement that the data needs to be immutable, and you cannot enforce that via an interface, then I would say it's totally legitimate to establish that rule as a coding standard.

The larger issue, though, is the need to strictly enforce a clean DTO layer. As long as your interface-less immutable classes only exist in the DTO layer, and your DTO layer remains free of logic and dependencies, then you will be safe. If you start mixing your layers, though, and you have interface-less classes that double as business layer classes, then I think you will start to run into much trouble.

5
  • Code which expects specifically to deal with a DTO or immutable value object should use that type's features rather than those of an interface, but that doesn't mean there wouldn't be usage cases for an interface which is implemented by such a class and also by other classes, with methods that promise to return values that will be valid for some minimum time (e.g. if the interface is passed to a function, the methods should return valid values until that function returns). Such an interface can be helpful if there are a number of types which encapsulate similar data and one wishes...
    – supercat
    Feb 22, 2014 at 20:00
  • ...to have a means of copying data from one to the other. If all types which include certain data implement the same interface to read it, then such data may be copied easily among all the types, without requiring each to know how to import from each of the others.
    – supercat
    Feb 22, 2014 at 20:01
  • At that point, you may as well eliminate your getters (and setters, if your DTOs are mutable for some stupid reason) and make the fields public. You're never going to put any logic there, right?
    – Kevin
    Feb 12, 2016 at 7:02
  • @Kevin I suppose, but with the modern convenience of auto properties, it's so easy to make them properties, why not? I suppose if performance and efficiency are of utmost concern then perhaps that matters. In any case, the question is, what level of "logic" do you allow in a DTO? Even if you put some validation logic in its properties, it would be no trouble to unit test it as long as it had no dependencies that would require mocking. As long as the DTO doesn't use any dependency business objects, then it's safe to have a little logic built into them because they will still be testable. Feb 12, 2016 at 10:59
  • Personally, I prefer to keep them as clean of that kind of thing as possible, but there's always times when bending the rules is necessary and so it's better to leave the door open to it just in case. Feb 12, 2016 at 11:00
7

I used to make a big fuss about making my code invulnerable to misuse. I made read-only interfaces for hiding mutating members, added lots of constraints to my generic signatures, etc., etc. It turned out that most of the time I made design decisions because I didn't trust my imaginary co-workers. "Perhaps someday they'll hire a new entry-level guy and he won't know that class XYZ can't update DTO ABC. Oh no!" Other times I was focusing on the wrong problem - ignoring the obvious solution - not seeing the forest through the trees.

I never create interfaces for my DTOs anymore. I work under the assumption that the people touching my code (primarily myself) know what is allowed and what makes sense. If I keep making the same stupid mistake, I usually don't try to harden my interfaces. Now I spend most of my time trying to understand why I keep making the same mistake. It's usually because I'm over-analyzing something or missing a key concept. My code has been much easier to work with since I gave up being paranoid. I also end up with fewer "frameworks" that required an insider's knowledge to work on the system.

My conclusion has been to find the simplest thing that works. The added complexity of making safe interfaces just wastes development time and complicates otherwise simple code. Worry about things like this when you have 10,000 developers using your libraries. Believe me, it will save you from a lot of unnecessary tension.

1
  • I usually save interfaces for when I need dependency injection for unit testing. Mar 6, 2013 at 21:53
5

It seems like an okay guideline, but for odd reasons. I've had a number of places where an interface (or abstract base class) provides uniform access to a series of immutable objects. Strategies tend to fall here. State objects tend to fall here. I don't think it's too unreasonable to shape an interface to seem immutable and document it as such in your API.

That said, people do tend to over-interface Plain Old Data (hereafter, POD) objects, and even simple (often immutable) structures. If your code has no sane alternatives to some fundamental structure, it doesn't need an interface. No, unit testing is not sufficient reason to change your design (mocking database access isn't the reason you're providing an interface to that, it is flexibility for future change) - it isn't the end of the world if your tests use that basic fundamental structure as-is.

2

Code becomes a lot simpler in many cases when you know something is immutable - which you don't if you've been handed an interface.

I don't think that's a concern that the accessor implementer has to worry about. If interface X is meant to be immutable, then isn't it the responsibility of the interface implementer to insure that they implement the interface in an immutable fashion?

However, in my mind, there's no such thing as an immutable interface - the code contract specified by an interface applies only to the exposed methods of an object, and nothing about the internals of an object.

It's much more common to see immutability implemented as a decorator rather than an interface, but the feasibility of that solution really depends on the structure of your object and the complexity of your implementation.

7
  • 1
    Could you provide an example of implementing immutability as a decorator? Mar 6, 2013 at 15:27
  • Not trivially, I don't think, but it's a relatively simple thought exercise - if MutableObject has n methods that change state and m methods that return state, ImmutableDecorator can continue to expose the methods that return state (m), and, depending on the environment, assert or throw an exception when one of the mutable methods is called. Mar 6, 2013 at 15:33
  • I really don't like converting compile-time certainty into the possibility of run-time exceptions... Mar 6, 2013 at 15:39
  • OK, but how can ImmutableDecorator know if a given method changes state or not? Mar 6, 2013 at 15:41
  • You can't be certain in any instance if a class which implements your interface is immutable with regards to the methods defined by your interface. @Baqueta The decorator has to have knowledge of the implementation of the base class. Mar 6, 2013 at 15:41
0

You wind up being passed an interface, and then wanting to pass it to some code that really wants to assume that the thing being passed to it is immutable.

In C#, a method that expects an object of an "immutable" type should not have a parameter of an interface type because C# interfaces cannot specify the immutability contract. Therefore, the guideline you are proposing itself is meaningless in C# because you cannot do it in the first place (and future versions of the languages are unlikely to enable you to do it).

Your question stems from a subtle misunderstanding of Eric Lippert's articles on immutability. Eric did not define the interfaces IStack<T> and IQueue<T> to specify contracts for immutable stacks and queues. They don't. He defined them for convenience. These interfaces enabled him to define different types for empty stacks and queues. We can propose a different design and implementation of an immutable stack using a single type without requiring an interface or a separate type to represent the empty stack, but the resulting code will not look as clean and would be a little less efficient.

Now let's stick to Eric's design. A method that requires an immutable stack must have parameter of type Stack<T> rather than the general interface IStack<T> which represents the abstract data type of a stack in the general sense. It's not obvious how to do this when using Eric's immutable stack and he didn't discuss this in his articles, but it's possible. The problem is with the type of the empty stack. You can solve this problem by making sure that you never get an empty stack. This can be ensured by pushing a dummy value as the first value on the stack and never popping it. This way, you can safely cast the results of Push and Pop to Stack<T>.

Having Stack<T> implements IStack<T> can be useful. You can define methods that require a stack, any stack, not necessarily an immutable stack. These methods can have a parameter of type IStack<T>. This enables you to pass immutable stacks to it. Ideally, IStack<T> would be part of the standard library itself. In .NET, there is no IStack<T>, but there are other standard interfaces that the immutable stack can implement, making the type more useful.

The alternative design and implementation of the immutable stack I referred to earlier does use an interface called IImmutableStack<T>. Of course, inserting "Immutable" in the interface name doesn't make every type that implements it immutable. However, in this interface, the contract of immutability is just verbal. A good developer should respect it.

If you are developing a small, internal library, then you can agree with everybody on the team to honor this contract and you can use IImmutableStack<T> as the type of parameters. Otherwise, you should not use the interface type.

I would like to add, since you tagged the question C#, that in the C# specification, there is no such thing as DTOs and PODs. Therefore, discarding them or precisely defining them improves the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.