Designing Clean Interfaces With Abstract Types, Explained

UPI Study › Courses › C++ Programming › Free lessons › Lesson 9.6 Free with a free account

Designing Clean Interfaces With Abstract Types

C++ Programming · Chapter 9: Structs, Classes, and File StreamsSection 9.6 · lesson 64 of 84video coming soon

← Defining Multiple Constructor VersionsNext lesson: Tracking Down Class Bugs →

Earn 3 college credits for C++ ProgrammingRecommended for credit by NCCRS · Computer Science 242 · self-paced · one payment, lifetime access

In this lesson

Simplifying complexity.

  • Separate what a type does from how it does it
  • Design the interface before the representation
  • Specify Stack and Queue by their operations
  • Read a header as a contract; keep accessors minimal

Transcript

Everything said in the video, section by section (1,422 words). Read along, or search it.

Opening Question

Using std::string Without Knowing Its Storage

You use std::string constantly, and have no idea how it stores its characters. That isn't a gap in your education. It's the point. A well-designed type lets you work entirely in terms of what it does, and leaves how it does it as somebody else's business. This lesson is about designing types that behave that way.

Core Concept

Abstraction Draws a Line Between What and How

Abstraction is drawing a line between what something does and how it does it, and letting users work on one side of that line only. You already rely on several. With a string, you assign, compare, ask its length, and print it. You've never needed to know whether short text is kept inside the object, or how growth is arranged. With a vector, you add, index, and ask the size, never knowing the growth factor. With sort, you give a range and a comparator, never knowing which algorithm runs. In every case, the ignorance is deliberate and beneficial. You can't write a wrong assumption about the growth factor into your program, if you were never told it. And the library authors can change it without breaking a line of your code. Abstraction isn't about hiding things from you. It's about deciding what you need to depend on, and making everything else unavailable to depend on by accident.

Design Method

Interface First, Representation Last

The natural instinct when writing a new type is to start with its data: what fields does it need? That order produces working code, and poor designs. You pick the fields, add a getter for each, and callers work with the fields. The representation is now public, and changing it means changing every caller. Interface first means listing what callers do, naming the operations, and only then choosing a representation, which you can change freely later. It's four questions, in order. What will callers need to do? Written as verbs, in the problem's words, like add a reading, or report the average. What does each operation need, and what does it hand back? What's guaranteed, before and after? And only now: how will this be stored? Storage comes last on purpose: it's the part you most want to keep free to change.

Definition

The Stack as an Abstract Data Type

An abstract data type is a type defined entirely by the operations it supports, and the rules they obey, with no reference at all to how the data is held. The stack is a classic example. Push makes a value the most recently added item. Pop removes the most recent item, and requires the stack not to be empty. Top reports the most recent item without removing it, and also requires a non-empty stack. Empty reports whether anything is held, and size reports how many. The governing rule is last in, first out. Push 1, 2, 3, 4, and top reports 4, with size 4. Pop, and top is 3, with size 3.

Comparison

Queue Rules Versus Stack Rules

A queue is specified the same way. Enqueue adds a value to the back of the line. Dequeue removes the item that's waited longest. Front reports it. And empty and size, as before. Its rule is first in, first out. Push A, B, C and D onto a stack, and pop removes D, the newest. Items are added and removed at the same end. Enqueue the same four, and dequeue removes A, the oldest: here, 1 is at the front. After one dequeue, 2 is at the front, with 3 left. Items are added at one end, and removed at the other. And notice what's missing from both specifications: no mention of arrays, vectors, indexes, or memory.

Worked Example

Same Stack on a Fixed Array

A stack could be built on a vector, on a fixed array with a counter, or on structures you haven't met yet. Every one of those would satisfy the specification exactly. Here is the same stack on a fixed array and a counter. The private part is different. The caller, main, is untouched. A caller written against the specification works with all of them, unchanged. The output is identical: 4 and size 4, then 3 and size 3. That's the strongest possible statement that the representation was never part of the type's meaning.

Header Contract

What the Header Promises Callers

The header file is where an abstract data type stops being an idea, and becomes something a caller can read. Everything a caller is entitled to rely on appears there, and everything else stays out. The stack header lists push, pop, top, empty and size, each with a comment. The private vector is today's choice, and no caller depends on it. The operation names, what each takes and returns, what must be true before, and what's true afterward, are part of the contract. The bodies and the container are behind it. And those comments carry real weight. Pop requiring a non-empty stack isn't an implementation detail. It's part of what the caller is agreeing to. A contract that only lists names is half a contract.

Stability

Which Changes Break Callers and Which Don't

Implementations change constantly. Something's too slow, a better container appears, or a bug forces a rethink. Interfaces should change rarely, because a change there ripples out to every caller. Replace the stack's internal vector with a fixed array and a counter, and no caller is affected. None ever mentioned the vector. Rename top to peek, and every caller is affected. The name was part of the contract. Make push reserve space in advance, and nobody's affected: only timing differs. But change what top reports, say the oldest item instead of the newest, and every caller is affected. Existing calls still compile, which makes it more dangerous than an outright break. So here's the test. If I changed my mind about how this is stored, how much code outside the type would need editing? The answer should be none.

Common Mistake

Getters and Setters for Every Field

Here is the habit worth resisting: writing a getter and a setter for every field, mechanically, before knowing whether anyone needs them. It feels like good practice, and it isn't. It publishes the field list under slightly longer names, so callers end up depending on exactly what you meant to keep free to change. This reading offers get and set for celsius, hour, and sensor ID. It's already committed to three fields of specific types.

Better Design

A Reading Log With Meaningful Operations

Compare a reading log. It offers record, average, highest, and count above. It's smaller, says more, and is far harder to misuse. Run it: three readings, an average of 21.5, a highest of 24, and two above 20. Its readings could be held in one vector, in several, or summarized as they arrive, and no caller would notice.

Decision Rules

Four Questions Before Adding an Accessor

Ask four questions before an accessor earns its place. Does a caller genuinely need this value, for a purpose I can name? If not, leave it out. It can be added later. Removing one is far harder. Is it a value in the problem's terms, rather than a stored field? If not, offer the operation the caller actually wanted. Would I still offer it if the field were stored some other way? If not, it accesses the representation, not the type. And does the matching setter make sense on its own? If not, provide the getter only. Every element of an interface is something you've promised to keep working. So a smaller interface is a smaller promise.

Checklist

Six Marks of a Good Interface

Here's a checklist for an interface. Small: every operation has a caller who needs it. Complete: anything a caller reasonably needs can be done without reaching around the type. Hard to misuse: the obvious way to call it is the correct way, and requirements like not empty are stated where they'll be read. Spoken in the problem's words: operations named for what they mean, like record and average, not for how they work. Stable: a change of representation forces no edits outside the type. And honest: what's guaranteed is written down, and anything not written down isn't promised.

Quick Check

Redesigning the Thermostat Interface

Quick check. All over a program, callers of a thermostat class get its target, add one, and set it back. What should the interface offer instead, and why? Pause and think. An operation named for what callers mean, such as raise, by one degree. Each call then states its intent in the problem's words. And any rule, like a safe maximum, lives in one place inside the type, while the stored field stays free to change.

Key Takeaways

Operations First, Storage Last

To summarize. Abstraction lets callers work on the what side of the line only. Design the operations first, and choose the storage last. Specify an abstract data type by its operations and rules, and state requirements in the header, as part of the contract. And a few meaningful operations beat a getter and setter for every field.

About C++ Programming at UPI Study

C++ Programming (Computer Science 242) is a self-paced online course of 84 video lessons in 11 chapters, from your first program to decisions, loops, arrays, functions, classes, files, pointers and inheritance. It carries a NCCRS credit recommendation for 3 semester credits.

Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Why take it here

Why students take C++ Programming with UPI Study

Your first real programming languageC++ teaches how programs really work: variables, control flow, functions, memory and objects. It is the base for data structures, algorithms, systems and game programming, and for most computer-science degrees.
Credit at a fraction of the priceThe same 3 credits can cost $750 or more at a US college. Here it is one payment, self-paced, with lifetime access.
Finish your degree soonerTransfer credits reduce the courses you still owe, so you can graduate earlier or lighten a heavy semester.
Strengthen an applicationA completed college-level course on an official transcript shows admissions and scholarship committees you can do the work.
Study on your own scheduleNo start dates, no deadlines. Watch the lessons, practice, and take the final when you are ready.
130,000+students helped with college creditsACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Where the credit goes

Universities that work with NCCRS

C++ Programming carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.

FAQ

Questions students ask us

About Designing Clean Interfaces With Abstract Types

What is abstraction in C++ class design?
Abstraction means drawing a line between what a type does and how it does it, so callers depend only on the "what" side. You use std::string this way without knowing how it stores characters. Because callers never learn the storage details, they can't depend on them, and the library authors can change them without breaking your code.
What does it mean to design the interface before the representation?
It means listing what callers need to do before choosing any fields. Ask four questions in order: what operations do callers need, what does each take and return, what is guaranteed before and after, and only then how the data will be stored. Storage comes last because it is the part you most want free to change.
How do you specify a stack and a queue without saying how they store data?
List the operations and the rules they obey. A stack has push, pop, top, empty, and size, with last in, first out. A queue has enqueue, dequeue, front, empty, and size, with first in, first out. Push 1, 2, 3, 4 and top gives 4. Neither specification mentions arrays or vectors.
Should I write a getter and setter for every private field?
No, because that publishes your field list under longer names and callers end up depending on your representation. Offer a few meaningful operations instead, like record, average, and highest for a reading log, or raise for a thermostat. Add an accessor only if a caller needs it for a purpose you can name, since a smaller interface is a smaller promise.

About the C++ Programming course

How many college credits is C++ Programming?
C++ Programming (Computer Science 242) carries a NCCRS credit recommendation for 3 semester credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
How much does C++ Programming cost and what is included?
$250, paid once, with lifetime access: all 84 video lessons, a practice quiz after each, the graded work and final exam, and an official transcript when you finish. Students who want several courses choose UPI Study PRO instead.
Which universities accept UPI Study credits?
UPI Study has signed partner agreements with universities such as Thomas Edison State University, University of the People and Charter Oak State College, and works with universities such as Penn State, SNHU and SUNY. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities. Use "Find my college" on this page to check yours.
How long does C++ Programming take?
It is self-paced with no deadlines. The 84 lessons total about 0 hours of video; most students add reading and practice time and finish in a few weeks to a few months, on their own schedule.
How are the exams done?
Quizzes and assignments are online in the course. The final exam is taken online with proctoring; a person on the UPI Study team checks assignments, usually within two days.
How do my credits reach my college?
When the course shows 100% complete, request the official transcript from your dashboard. It is sent to the college you name, and you can send it to more than one. Check your college first with "Find my college" on this page, or ask Athena.

Ready for the full course? Earn 3 credits in C++ Programming.

84 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.

C++ Programming
3 credits · NCCRS
Enroll →