4

I asked a question on StackOverflow on how to properly implement an ImmutableMap that abides by the SOLID principles.

Due to Java's Map interface containing put and putAll, it violates the interface segregation princple by forcing us to implement the put and putAll methods for an ImmutableMap. If we were to define our own interface, it would fall under the code smell category "Alternative Classes with Different Interfaces".

This got me thinking on how would one properly declare an interface that abides by the Liskov Subsitution principle, while still abiding by the interface segregation principle.

The only way I can think of achieving this would to declare the base interface with only accessors, then supply a subinterface with mutators:

interface Map {
    //accessor methods
}

interface MutableMap extends Map {
    //mutator methods
}

class HashMap implements MutableMap {

}

class ImmutableMap implements Map {

}

Is this considered "good practice"? It seems to be the only way to implement immutability while still abiding by the SOLID principles, but I've never heard of such a practice.

7

Original interface design in the question suggested that Map interface mandated immutability, but this violates Liskov substitution principle. Consider its definition:

Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.

Consider immutability as the provable property about immutable interface Map. This property (immutability) is clearly not provable in MutableMap interface.

Interface design in the question was clarified and now it doesn't violate Liskov's. Absence of mutate methods alone doesn't mandate immutability.

This more explicit design doesn't violate Liskov's substitution principle:

/** Contract says nothing about (im)mutability, only that it provides read access */
interface Map {
    // accessor methods
}

/** This specialization explicitly allows mutation */
interface MutableMap extends Map {
    // mutator methods
}

/** This specialization adds an immutability constraint */
interface ImmutableMap extends Map {
    // could contain nothing (just marker interface)
}

The key here is that immutable interface is not an ancestor of mutable interface, they are both "siblings" who clarify (specialize) constraints/features of the common generic ancestor.

Map interface says nothing about (im)mutability, that's why it's possible to specify this in its descendants without violating Liskov's substitution principle.

  • I'm not understanding; the Map I defined doesn't mention anything about immutability; it's just a contract stating "I contain key/value pairs which you can access". Why is your second any different from what I described? It seems as if ImmutableMap would simply be a marker interface in that case – Vince Emigh Apr 26 '15 at 7:47
  • 2
    In your original post you wrote The only way I can think of achieving this would to declare the base interface as immutable. Declaring the class immutable is not the same thing as implementing only read methods. Either you declare the Map to be immutable (and all its descendants must satisfy it) or you don't declare Map immutable, but then you can't treat Map instances as immutable. Yes, ImmutableMap is a marker interface, it only adds to the contract "immutability" constraint. Remember that interface's contract is not just code ... – qbd Apr 26 '15 at 7:53
  • I edited my title soon after realizing I shouldn't have said immutable, guess I missed the actual post :s Immutable was a bad term to use, I guess; I was assuming an interface that defined "read only" behavior was considered immutable. Fixing that up now.. – Vince Emigh Apr 26 '15 at 7:56
  • I have edited my question to better fit what I'm wondering. I'm not sure what an ImmutableMap interface would do if the Map interface has no mutator methods – Vince Emigh Apr 26 '15 at 8:00
  • ImmutableMap is a marker interface, you use it to mark your class that it is immutable :) Take for example a situation where you need some good immutable map implementation - how can you find all immutable maps? Easy, just display all classes implementing ImmutableMap interface. Another possibility is that your code might work with maps. It might for some reason want to work with immutable maps differently (e.g. because immutable maps are inherently threads safe, you don't need to synchronize access on them), while you need to on ordinary Map instances. – qbd Apr 26 '15 at 8:12
0

No, that is a violation of the Liskov Substitution Principle since you are expanding the implied contract (from immutable to mutable). You could have a Readable foo as the base class, but you need to make extra special care that the code does not imply immutability.

  • Expansion doesn't violate the principle, does it? If so, wouldn't any means of adding behavior through inheritance violate the principle, making inheritance only good for overriding methods and adding new state? If so, how would you properly implement immutability? Or should I ask a different question for that – Vince Emigh Apr 26 '15 at 3:44
  • @vinceemigh - it is debatable if adding functionality violates it - personnally I think it does. But taking something that already exists "this property foo is an int and cannot be changed" and breaking that contract certainly is. You properly implement immutability by making things immutable (not supplying mutators for the object's data). I don't see how that is a question... – Telastyn Apr 26 '15 at 3:54
  • It's not stating that state can't be changed; it's simply stating "This has elements in key/value pairs. You can receive these values after the instance has been constructed", while the MutableMap states you can also add values". Do you have source of this being debated? I'm having troubles finding it (probably due to bad googling skills :s). If you take out the mutators in Java's Map, and didn't extend upon it to make a MutableMap, wouldn't that lead to the code smell "alternative classes with different interfaces"? – Vince Emigh Apr 26 '15 at 4:17
  • @vinceemigh - I don't have a link off hand. And I'm not paying attention to Java at all. But if I see an interface with only getters, I'm going to assume that it represents an immutable object unless it's name indicates otherwise. – Telastyn Apr 26 '15 at 17:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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