Question

“Exposing the rep” of an object can be caused by which of the following:

  1. Not declaring variables in its class as private.
  2. Allowing a variable in the object to refer to an object passed to it as an argument to its constructor.
  3. Allowing a reference stored in a variable in the object to be passed as the return value of a method called on that object.
  4. All of the above.

I know the answer is 4 but i'm not sure why, my understanding of what exposing the rep is not completely clear.

Edit: for clear answers

Was it helpful?

Solution

I had never heard that term, so I googled it. This article explained it quite clearly:

http://www.cs.newpaltz.edu/~pletcha/oop_chap5_1.html

Exposing the rep means to violate the rule that an object's methods control its state. For example, if an object has instance variables and mutators to change their values, then the object has a means of controlling its state. If I call foo.setBar(5), then foo.getBar() better return 5 if the documentation says getBar returns the value set by setBar.

I will explain why each of the three descriptions you gave exposes the representation of an object (or put more generally, breaks encapsulation):

Not declaring variables in its class as private.

This one is the easiest. If the instance variables are public, anything in the JVM can change their values from outside of code within the same object/class. If we call foo.setBar(5) and then foo.getBar(), we may get something other than 5 because bar is public scope so another area of code could have mutated it.

Allowing a variable in the object to refer to an object passed to it as an argument to its constructor.

This one took me a minute to understand, but it makes sense if you think about an object and its dependencies as a single unit.

If a Foo has a Bar and Bar has int called x, then Foo can see and control the x property on bar because it has a reference. If I create an instance of Foo and pass a reference to an instance of Bar in Foo's constructor, it gives the appearance that Foo has complete control. But it does not. Example:

public class Foo {
    private Bar bar;

    public Foo(Bar bar) {
        this.bar = bar;
    }

    // immutable property - can only be read once this object is instantiated
    public Bar getBar() {
        return this.bar;
    }
}

public class Bar {
    private int x;

    public Bar(int x) {
        this.x = x;
    }

    public int getX() {
        return this.x;
    }

    public void setX(int x) {
        this.x = x;
    }
}

// some other java class
Bar bar = new Bar(10);
Foo foo = new Foo(bar);
bar.setX(5);

This code exposes the rep because foo has made a critical assumption that it controls bar. Notice its reference to bar is immutable. But it isn't really immutable. Only the reference itself is immutable. The code that created foo still has a reference to bar and can mutate it without foo knowing about it.

Put more simply, foo has a dependency on bar and considers it part of itself. But bar can actually change independently and therefore foo's state changed indirectly without it knowing.

Allowing a reference stored in a variable in the object to be passed as the return value of a method called on that object.

This is easiest to explain through a collection.

public class Foo {
    private Collection<Bar> bars = new ArrayList<Bar>();

    // immutable property - can only be read once this object is instantiated
    public Collection<Bar> getBars() {
        return this.bars;
    }

    public void addBar(Bar bar) {
        this.bars.add(bar);
    }

    public int getBarCount() {
        return this.bars.size();
    }
}

Foo foo = new Foo();
foo.getBars().add(new Bar(someUnexpectedBar));
System.out.println(foo.getBarCount());  // -> 1

This violates the contract. To add a bar, you are supposed to call addBar. That is why the method is exposed. By returning a reference to the collection in getBars, the underlying collection can be manipulated.

This seems trivial at first. But if Foo makes this rule and the above usage violates it, then what would happen if I want to refactor Foo to this (let's say for performance):

public class Foo {
    private Collection<Bar> bars = new LinkedList<Bar>();
    private int barCount;  // for faster inserts, use a linked list and to maintain fast counts, track the count ourselves by tracking the adds.

    // immutable property - can only be read once this object is instantiated
    public Collection<Bar> getBars() {
        return this.bars;
    }

    public void addBar(Bar bar) {
        this.bars.add(bar);
        this.barCount++;
    }

    public int getBarCount() {
        return this.barCount;
    }
}

Foo foo = new Foo();
foo.getBars().add(new Bar(someUnexpectedBar));
System.out.println(foo.getBarCount());  // -> 0

Functionally, it's identical. If you use the methods in it appropriately, there would be no difference. But we cheated. We grabbed the underlying collection and mutated it. Now the getBarCount() method returns the wrong answer (0).

The way around this is to return a new collection with a clone of the original.

    public Collection<Bar> getBars() {
        return new ArrayList<Bar>(this.bars);
    }

or even

    public Collection<Bar> getBars() {
        return Collections.unmodifiableCollection(this.bars);
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top