Concepts / Encapsulation and Public Interfaces

Encapsulation and Public Interfaces

The read_and_sum() function is a client of the Vector class: it creates a Vector, reads and writes its elements, and asks for its size, all through public members.

  • Programming

A Function That Only Talks to an Object

Writing a class is only half of the work. The other half is using it. This article looks at a short function, read_and_sum(), that makes use of a Vector class. Its job is to read a number of values, store them, and return their total. The function is simple enough to follow line by line, which makes it a good place to see how a program talks to an object.

As you read, pay attention to one question: which parts of the Vector object does the function actually need to know about? The answer shapes the whole idea of encapsulation, which this article explains.

What do you think happens?

Before reading the function, guess which members of Vector the function must name directly in its code. Choose from: the constructor, operator[], size(), elem, and sz.

  • Only the constructor, operator[], and size()

  • Only elem and sz

  • All five of them

Reveal answer
Answer:

Only the constructor, operator[], and size()

The function creates the object with Vector v(s), reads and writes elements with v[i], and asks for the count with v.size(). It never names elem or sz. Those two members are the hidden representation of the class, and the function works only through the public members.

The Function Being Traced

The source describes read_and_sum() in terms of what it does and how it uses the Vector: it creates one with Vector v(s), it reads and writes its elements with v[i], and it asks how many elements it holds with v.size(). The source does not print the full function, so the listing below is a reconstruction written to match those uses exactly. Treat it as a teaching sketch, not as the original text. The return type double is an assumption made for this sketch.

cpp
Notes on the code
  • Vector v(s) creates the object with s elements, using the constructor in the public interface.
  • The first loop reads values from input into v[i], using operator[] for access.
  • The loop test i != v.size() uses size() as the stopping point.
  • The second loop uses the same test and the same access form v[i] to add the elements into sum.
  • Nothing in the function names elem or sz.

Notice that the two loops are nearly identical. They use the same stopping test, i != v.size(), and the same access form, v[i]. The source points out that this repetition is not an accident of style. Both loops depend on the same public interface.

Tracing the Calls Step by Step

The diagram below shows the same function as a sequence of messages between the function and the Vector object. Read it from top to bottom. Each arrow is one interaction, and each one uses a public member.

read_and_sum()Vector v1. Vector v(s)2. size() loop test3. v[i] write from cin4. size() loop test5. v[i] read into sum
In what order does read_and_sum() create the Vector, write and read its elements through operator[], and call size(), and which calls go to which object?

Step 1 creates the object. The declaration Vector v(s) uses the constructor, and the object is created with s elements. Steps 2 and 3 belong to the first loop: each pass checks the stopping test with size() and then stores a value through v[i]. Steps 4 and 5 belong to the second loop, which checks size() again and reads each element through v[i] to build the total. At no point does the function touch the object's storage by name.

Worked example

Tracing read_and_sum(3) with inputs 4, 5, and 6

The function is called with s equal to 3, and the user types the values 4, 5, and 6. What does the function return?

  1. 1
    Create the object

    Vector v(3) creates a Vector with 3 elements. The loop test will compare i against v.size(), which is 3.

  2. 2
    First loop, reading

    When i is 0, cin >> v[0] stores 4. When i is 1, cin >> v[1] stores 5. When i is 2, cin >> v[2] stores 6. Then i becomes 3, and the test i != v.size() fails, so the loop stops.

  3. 3
    Second loop, summing

    sum starts at 0. The loop adds v[0] (4), then v[1] (5), then v[2] (6), giving 0 + 4 + 5 + 6.

  4. 4
    Return

    The function returns the total, which is 15.

Result

The function returns 15. Each access went through v[i] and each stopping test went through v.size(), so the function never needed to know how the values are stored.

The Public Interface and the Hidden Representation

Definition

The class interface is the set of public members that clients may use. For Vector, these are Vector(), operator[](), and size(). Encapsulation is the idea that the representation, the members elem and sz, is hidden from the outside, so that a Vector can only be worked with through its interface.

The diagram separates the two sides of the class. On one side is the interface, the three members a client may call. On the other side is the representation, the members elem and sz, which the source says are accessible only through that interface. The read_and_sum() function sits entirely on the interface side.

Vector classPublic interfaceVector()Hidden representationoperator[]()size()elemszexposeshidescontainscontainscontainscontainscontains
Which members of Vector are visible to clients, which are hidden inside the class, and what contains what?

The consequence is that the client depends on what the object does, not on how it stores its data. The source makes this the point of the design: if the object's internals changed while the interface stayed the same, the two loops in read_and_sum() would not need to change.

Why the Client Stays on the Outside

It is tempting to reach into an object and touch its members directly, because the members are right there. The source's design says not to. Consider what would change for the client if it touched elem and sz directly instead of going through operator[] and size(). The table compares the two approaches.

QuestionClient uses operator[] and size()Client touches elem and sz directly
Which members does the client name?Vector(), operator[](), size()elem and sz, the hidden representation
What can the class change without breaking the client?Its internal storage, as long as the interface stays the sameVery little, because the client now depends on the storage layout
Where does the client's code belong?On the interface side of the boundaryAcross the boundary, inside the object's private details
Does the loop test still come from the object?Yes, through size()Only if the client recomputes it from sz itself

Client access through the public interface compared with direct access to the hidden representation

Where a Wrong Result Comes From

When a function that uses a class returns a wrong total, the fault is usually in one of the places where the client meets the object. The diagram follows a value from input through the Vector's storage to the returned sum. Each arrow is a place where a mistake can enter.

cin inputVector elements viav[i]sum accumulatorreturned totalsize s passed toVector(s)written by v[i]read by v[i]returnedsets the number of elements
How does a value flow from input through Vector's storage and back out as the sum, and at which step could a wrong result enter?
  • Common mistake

    Creating the Vector with the wrong size

    What it looks like

    Calling read_and_sum(2) when the user is going to type three values.

    Why it fails

    Vector(s) sets how many elements the object holds, and the loops stop at size(). The extra input is never stored.

    Do this instead

    Make sure the size passed to the constructor matches the number of values the function is meant to read.

  • Common mistake

    Indexing outside the valid range

    What it looks like

    Writing a loop that goes one step past the last element, so that v[i] is used with i equal to the size.

    Why it fails

    The client is responsible for indexing the object within range. An out-of-range index is a client error, even though the Vector itself is written correctly.

    Do this instead

    Keep the loop test as i != v.size() so that the index stops before leaving the valid range.

  • Common mistake

    Looking inside the object to debug it

    What it looks like

    Printing elem or sz to check the result before checking the function's own calls.

    Why it fails

    These members are the hidden representation. Inspecting them skips the interface, which is where the client's mistake is most likely to be.

    Do this instead

    Check the client's use of the public interface first, then check the class itself.

Best practice

When a class-using function misbehaves, separate the client code from the object. First ask whether the client calls the public interface correctly: does it create the object with the right size, index it within range, and use size() to control its loops? Only after that should you consider whether the class itself is at fault.

Practice Tracing a Call

MEDIUM

Suppose read_and_sum(2) is called and the user types 7, 8, and 9 in that order. Trace the two loops and state the value the function returns. Then say which line is the first place a client mistake would show up.

Hints
  • Vector v(2) creates an object with 2 elements, so the loop test stops after two reads.
  • The extra value 9 is never read into an element of v.
  • Work out the sum using only the two values that were stored.

Key Takeaways

  • read_and_sum() is a client of Vector: it creates the object with Vector v(s), reads and writes elements with v[i], and asks for the count with v.size().
  • The public interface, Vector(), operator[](), and size(), is what clients may use. The representation, elem and sz, is hidden by encapsulation.
  • Because the client depends only on the interface, the class's internals could change without requiring changes to the loops in the client.
  • When a class-using function returns a wrong result, check the client's use of the public interface first, including the size passed to the constructor, the indexing range, and the loop test, and only then check the class itself.

Related concepts

More concepts starting with E