Object-Oriented Programming

Encapsulation

Michael L. Collard, Ph.D.

Department of Computer Science, The University of Akron

Credits

Adapted from: Encapsulation is not information hiding

Definitions

  • Encapsulation

    The bundling of data with the methods that operate on that data

  • Information Hiding

    Hide the internal representation, or state, of an object from the outside

  • Note: Some definitions of encapsulation include the definition of information hiding

Language Features

  • C language support:
  • struct with fields only and no access specifiers
  • free functions
  • C++ language support:
  • class Integrate data with methods
  • Access specifiers: public, private, protected

Access & Visibility for Class A

Access Type Visibility
public All code
protected class A methods
friend functions of class A
class derived from class A
private class A methods
friend functions s of class A

First Rule: Limit the Interface

  • Limit the methods available
  • Limit access and visibility
  • I.e., private whenever possible
  • Remove any unneeded parameters
  • E.g., code where a local variable could be used instead of a parameter
  • Use const whenever possible for reference and pointer parameters and methods

Minimum Essential Interface

  • Unless required, more is not better
  • Developers tend to overdesign and provide more than is needed (sometimes missing what is needed)
  • E.g., A study at Microsoft showed that 30% of methods identified in the design of software were never implemented
  • Start with the minimum functionality and configurability, and add as needed

Position v0

Position v0 Analysis

  • Data, latitude and longitude, in the Position class
  • Operations, distance() and heading(), are free functions in a separate file
  • The client program has to include two files
  • Easy to get the order of arguments backward heading() and calculate the opposite of what is needed

Other Position v0 Issues

  • Fields of struct Position are public and directly accessible. It is possible to store invalid latitude and longitude values, producing garbage distance and headings (GIGO)
  • Changes to the field/data member names, types, or even storage in Position require changes in the implementation of distance() and heading() (∆ Position.hpp ⟶ ∆ PathCalculations.cpp)

Encapsulation Rule

Place data and the operations that perform on that data in the same class

  • Don't make the client tie data and operations together
  • Provide it in one class (or the fewest number of classes needed)
  • Improves class cohesion

Position v1

Position v1 Analysis

  • Only need to include a single include file, Position.hpp
  • Clear direction of operations heading()

Information Hiding Rules

  1. Don't expose data items
  2. Don't expose the difference between stored data and derived data
  3. Don't expose the implementation details of a class
  4. Don't expose a class's internal structure

Information Hiding Rule 1

Don't expose data items

  • Make all data members private
  • If access is needed externally to the class, use get and set methods for access
  • Isolates client from changes to data members (if the same external functionality is needed)

Position v2

What Not To Do

  • Non-const reference takes const off of the method
  • It's pretty much the same as making the field public
  • Very few applications where this makes sense
  • Specially a problem in Java, as Objects are not passed by value but by Object Reference, and there isn't a const
  • Instead: Always return data by value or const reference

As Far As A Mutator

Information Hiding Rule 2

Don't expose the difference between stored data and derived data

  • Derived data is data calculated from stored data
  • Don't reveal whether an attribute is stored or derived
  • Use get method names for property methods
  • get method names: speed(), getSpeed()
  • property method names: calculateSspeed(), determineSspeed()

Position v2 Position::distance()

  • Changing to radians internally would make everything much simpler
  • Do not want to change the current interface (i.e., in degrees), as clients expect it
  • However, we could add radians to the interface while preserving what we have

Position v2 → v3

Position v2 & v3 Interface

Position v2 & v3 Client

Position::distance() v2 → v3

Position:getLatitude() v2 → v3

New Requirement

  • Need to handle a multi-position route

Route v0

Information Hiding Rules

  1. Don't expose data items
  2. Don't expose the difference between stored data and derived data
  3. Don't expose implementation details of a class
  4. Don't expose a class's internal structure

Route v0 Issues

  • Can directly access elements of an internal container
  • Can we change the container?
  • Can we change the container values?

Route v0 & v1 Interface

Information Hiding Rules

  1. Don't expose data items
  2. Don't expose the difference between stored data and derived data
  3. Don't expose implementation details of a class
  4. Don't expose a class's internal structure

Route v1 Issues

  • Exposes direct design detail that we are using a std::vector
  • If we change to a different container, the type returned by the getPositions() has to change, and the client code has to change
  • Use of auto in the client code, and perhaps a typedef can help, but not entirely prevent this
  • Which container we use is an internal structure and implementation detail that should be hidden in the class
  • Do we need this type of access?

Route v1 & v2 Interface

Route v2 Issues

  • The setPosition() exposes direct design detail that we are using an indexable container, e.g., std::array, std::vector, and std::deque, with an indexable iterator
  • If we change to a container that does not allow indexing, the client code would have to change, e.g., std::list, std::forward_list, with a bidirectional iterator
  • The characteristics of which container we use is an internal structure and implementation detail that should be hidden in the class
  • Always ask yourself Do we need this type of access?

Route v2 & v3 Interface

Route v3 Issues

  • The getPosition() is expected to be a O(1) or constant-time algorithm
  • However, with a non-indexable container, e.g., std::list, this is not true
  • If we change a container that does not allow indexing, the client code would have to change
  • The characteristics of which container we use is an internal structure and implementation detail that should be hidden in the class
  • Always ask yourself Do we need this type of access?

Route v3 & v4 Interface

Route v4 Improvements

  • Use standard C++ iterators

Route v4 & v5 Interface

Route v5 Additional Improvements

  • A separate iterator object would allow multiple, simultaneous access
  • A begin() and end() would allow use in range-for statement
  • The segment is problematic. Perhaps allow iteration through segments and not positions