6

I'm referring to some architectural styles here and under Object Oriented architectural style there are couple of invariants mentioned.

  • Objects are responsible for preserving the integrity (e.g.,some invariant) of the data representation
  • The data representation is hidden from other objects

But no clear explanation provided. Really appreciate if someone can help me to understand these, maybe with couple of examples.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
prime
  • 219
  • 1
  • 4
  • 1
    I recommend books or papers by Barbara Liskov if you are interested in the theory behind correctness in object oriented design. – Frank Hileman Aug 14 '17 at 17:06

1 Answers1

8
  • Objects are responsible for preserving the integrity (e.g.,some invariant) of the data representation.

An invariant is just something that needs to stay true at all times. For example, if you have an object that represents a range of some sort, and stores a lower limit (let's call it a) and an upper limit (b), an invariant would be the requirement that a <= b.
The object would maintain this invariant by preventing client code (the code that uses it) to change it in a way that results in a > b. For example, let's say that the user can set these two values; in the setter method/property, before accepting the user provided value, you would check if a > b, and throw an exception if it is, or use some other mechanism to maintain the invariant.

  • The data representation is hidden from other objects

This is known as encapsulation; basically, you provide restricted set of public methods (the public interface) that other objects can use to interact with this object. This is like a public "contract" of sorts: your object basically declares "I provide these services, in this way and in this format". However, the internal state can be represented and manipulated in various ways (you can have various implementations) as long as the object confirms to the interface defined. This internal state is "invisible" to other objects, which is a good thing: this theoretically enables you to change the implementation details of the object without affecting (and thus having to change) the code in any other part of the system.

For example, consider a simple game where you have level that is an NxN grid with some items on it.

// (pseudocode)
class Grid 
{
    // represent items locations as an NxN 2D array of Booleans
    // (omitted)

    bool IsItemOn(x, y) 
    {
        // check the 2D Boolean array
    }
}

But, then, for one reason or another, you decide change the internal representation from the 2D Boolean array to a 1D Boolean array:

class Grid 
{
    // represented as 1D array of NxN Booleans
    // (omitted)

    bool IsItemOn(x, y) 
    {
        // calculate the index into the 1D array from x and y
        // check the value at the calculated index
    }
}

However, because of some other considerations, you later decide to change the scheme, and just store the list of item locations. Maybe there are just 3 items in the level, and it seemed wasteful to use an array of NxN elements. This way, you'll just store 3 two-dimensional points.

class Grid 
{
    // just store item locations as (x, y)
    // (omitted)

    bool IsItemOn(x, y) 
    {
        // check if this location matches any of the ones stored
    }
}

In all cases, only the internal representation changes, and any external code remains unaware of that, as it's only the IsItemOn method that's visible from the outside. No other code needs to change, everything works as is.

Filip Milovanović
  • 8,428
  • 1
  • 13
  • 20
  • 6
    Regarding invariants: each object is responsible for its own correctness. It should not be possible to have an object in an invalid state. A few guidelines follow from this: (1) The constructor must validate the initial state. (2) If there is no valid “empty” state, then there should not be a default constructor. (3) After the constructor has validated the initial state, the easiest way to prevent integrity violations is to prevent any state modification → immutability. (4) It is hard to ensure all invariants if you expose setters or writable properties. Instead, offer higher-level commands. – amon Aug 14 '17 at 16:00
  • Thing got more clear now. Thank you for the detailed answer. – prime Aug 14 '17 at 16:12
  • @amon In fact invariant violations are typically allowed temporarily in the body of a method. But the invariant must be true when control leaves the method, even if making a call to another object, or an exception occurs. – Frank Hileman Aug 14 '17 at 17:04