Encapsulation C++

A common question among newer Object-Oriented Programmers is “Why should I use and prefer to use getter and setter methods for my objects variables when I can simply set them as public?” For example, let’s consider the trivial object below:

class myObject
{
    int myVar;
public:
    myObject() : myVar(0)
    {
    }

    int getMyVar()
    {
        return myVar;
    }

    void setMyVar(int x)
    {
        myVar = x;
    }
};

It would appear to a casual observer, and could be pretty well argued, that there’s no need to have two methods just to set one variable and get one variable inside of an object, aside from the knee-jerk reaction to encapsulate everything. A public variable would accomplish the same goal, and would be far easier to read and write, like so:

class myObject
{
public:
    int myVar;
    myObject() : myVar(0)
    {
    }
};

To someone who hasn’t experienced any problems with this way of doing things, it may seem odd to want to write more unnecessary code when all this time we’ve been trying to find ways to avoid writing more code. The aforementioned way looks more computationally expensive, harder to understand, and completely unnecessary.

Well… no, it isn’t. Encapsulation is the basic principle that teaches us to always do this, but we’re not writing all of this just to appease the Encapsulation Police, we’re doing it for a very good reason, and that’s flexibility with our objects. I’m going to venture a proposition… never in the history of programming has there ever once been someone who completely wrote a non-trivial object and didn’t need to change it ever again. What I am saying is that I guarantee to you you’ll find yourself in situations where you’re going to need to change some internal mechanisms of an object to add functionality, or make it more efficient, or for any other assorted reason. I’ll now prove this to you.

Let’s continue to utilize the object above, but let’s say, for some reason, you need a new function that “rewinds” and “fast forwards” the returned value from getMyVar(), so you’ll need to keep some kind of internal mechanism as to how to rewind/FF it. Let’s also say that you need to keep count of how many times myVar was changed, as well as add a new overloaded constructor to allow the user to set any value as the initial value (instead of just 0).

class myObject
{
    unsigned long tChanged;
    bool looped;
    std::vector<int> myVars;
    std::vector<int>::iterator retNum, endPoint;

    void setup(int x)
    {
        myVars.reserve(20);
        myVars.push_back(x);
        retNum = myVars.begin();
        endPoint = myVars.end();
    }

public:
    myObject() : tChanged(0)
    {
        setup(0);
    }
    myObject(int x) : tChanged(0)
    {
        setup(x);
    }

    int getMyVar()
    {
        return *retNum;
    }

    /* I wanted to ensure I didn't have to do any additional
       dynamic memory allocation for this method in the case
       of setMyVar being called more than 20 times, so I made
       it so after the 20th call, setMyVar simply wrapped 
       around the vector and treated the first number as if
       it were the 21st, but that means rewind can't go further
       back than 20 numbers. That's okay. 🙂                    */

    void setMyVar(int x)
    {
        if (myVars.size() >= 20)
        {
            if (endPoint >= myVars.end())
            {
                endPoint = myVars.begin();
                looped = true;
            }

            *endPoint = x;
            ++endPoint;
        }
        else
        {
            myVars.push_back(x);
            endPoint = myVars.end();
        }

        retNum = endPoint - 1;
        ++tChanged;
    }

    void rewind()
    {
        if (retNum >= endPoint)
        {
            if (--retNum < endPoint)
                retNum = endPoint;
        }
        else
        {
            if (--retNum < myVars.begin())
            {
                if (looped)
                    retNum = myVars.end() - 1;
                else
                    retNum = myVars.begin();
            }
        }
    }
    void rewind(int x)
    {
        for (int iii = 0; iii < x; ++iii)
        {
            rewind();
        }
    }

    void fastForward()
    {
        if (retNum < endPoint)
        {
            if (++retNum >= endPoint)
                retNum = endPoint - 1;
        }
        else
        {
            if (++retNum >= myVars.end())
            {
                retNum = myVars.begin();
            }
        }
    }
    void fastForward(int x)
    {
        for (int iii = 0; iii < x; ++iii)
        {
            fastForward();
        }
    }

    unsigned long getTimesChanged()
    {
        return tChanged;
    }
};

As you can tell, this object has become much less trivial! But here’s the real advantage to using your getter/setter methods: Any other code you made that utilized the myClass object doesn’t have to change at all and it is still fully functional! While your getter and setter methods have wildly changed in implementation, and new methods have been added to the object, your older code that still used the old getter/setter methods will still be able to use this object exactly as if nothing had changed at all, and you won’t need to worry about updating your entire code base to reflect what could be only one or two changes in a single object! This is absolutely vital in any project with more than a few source code files, since tracking down individual changes can be a difficult and time-consuming task, as well as could produce many difficult to find, and fix, bugs!

When people talk about flexibility with code, and a need for abstraction, this is a great example of what they mean. While the ideas of encapsulation and abstraction extend far beyond this simple example, this is a very much real code example as to what’s going on, and why it is so important to do. Imagine if you had chose to instead go with a public variable that could be changed by any object, by doing so you’ve suddenly lost any flexibility to count the number of times that public variable has changed, or to extend the functionality of the object with that variable. You’ve lost a significant amount of versatility in your code, and through experience many developers have learned that this is the best way to go about things. That is why we use getter and setter methods!

To close, I want to address some of the challenges to getter/setter methods, and emphasize when using these methods is appropriate. Remember that divulging implementation details, such as what certain internal “has-a” objects contain, or what those objects are doing and what their states are, is a completely inappropriate use of getter/setter methods. Objects in themselves should be doing that kind of legwork, not the other objects using them. The article writer asserts that all getter/setter methods break encapsulation and provides no evidence of this supposedly obvious truth, but it doesn’t. The reason these methods exist is to maintain encapsulation, as I’ve just shown. The entire implementation can be changed in the object, and so long as there’s something to capture those get/set methods, then things will continue to work. Getter/Setter methods are intended to be part of the interface of an object, and part of it’s functionality, not an exposure to implementation details, and this distinction is important.

Hope this all helped!


Look at it this way. You have two objects and you need a kind of medium for them to communicate with each other.A can go and ask B what he ate for breakfast.

A can do this by saying B.WhatDidYouEatForBreakfast() or shorter B.Breakfast(). The nice thing about it is that it doesn’t need to go and check it’s plates and dumpster (those are private) but the question is allowed (it’s public). B, on the other hand, can say what he wants (will probably say fruits and vegetables, when in fact was chips and cola), but it better be the info that you’re looking for (makes sure of this in the method body).

So, basically, you’ll just have to have a public method in one of the classes with a return type of the private variable. This method will return the value of the private variable. Anybody can now call that object and find out what is going on.


It is not clear what you are trying to do. If you have something like this…

class A
{
    private:
    int X;//ignoring any thing about encapsulation for now
};

class B
{
//...
    public:
    void DoSomething()
    {
        A objectA; 
        //makes objectA another A each time we get here, with X uninitialised
        //use objectA.X which has random numbers in
    }

};

this will behave similarly to the problem you describe. If you make objectA a member instead and initialise it properly it might do what you want. With class A as before,

class B
{
//...
    private:
    A objectA;
    public:
    B() 
    {
        objectA.X = 42;
    }
    void DoSomething()
    {
        //use member objectA.X 
    }

};

EDIT Clearly in my example I have pulled 42 out of nowhere. If you have a sensible value from the gui, send it to the constructor of B. If it changes you’ll need a reference to it instead.

explicit B(int gui_value)
{
    objectA.X = gui_value;
}

EDIT2 If you have an existing objectA someplace else, then pass that to the constructor

explicit B(const A & otherA)
    : objectA(otherA)
{
}

If that’s not possible, your design needs improving. Without any code to look at I cannot help any more.


Sources:

Advertisements