Preventing the destruction of encapsulation

I have this class:

class Phone {
private:
    string producer, color;
    int weight, dimension;
public:
    Phone(string &producer, string &color, int &weight, int &dimension):
        producer(producer), color(color), weight(weight), dimension(dimension) {};
    Phone():
        producer(""), color(""), weight(0), dimension(0) {};
    virtual ~Phone() {};
    string getProducer(void) const;
    string getColor(void) const;
    int getWeight(void) const;
    int getDimension(void) const;
    virtual void displayInfo(void) const;
};

      

The problem here stems from the fact that I am exposing the internal implementation of the object via getters.

But how can I prevent this?

Because generally, in my code, I need to know some private data from my object (this is one example for comparison) and therefore I use getters.

So, I rewrite the class something like this:

class Phone {
    private:
        string producer, color;
        int weight, dimension;
    public:
        Phone(string &producer, string &color, int &weight, int &dimension):
            producer(producer), color(color), weight(weight), dimension(dimension) {};
        Phone():
            producer(""), color(""), weight(0), dimension(0) {};
        virtual ~Phone() {};
        bool isTheProducer(string& producer) const { return this->producer == producer };
        bool hasWeight(int& weight) const { return this->weight == weight };
        bool hasDimension(int& dimension) const { return this->dimension == dimension };
        virtual void displayInfo(void) const;
    };

      

Is this the best design (due to not getting the actual personal value)?

+3


source to share


3 answers


As you could see from other answers and comments, the answer is: it depends.

In fact, it mainly depends on where your classes are being used. Let's first insert the example given in the question, object comparison. Since it is not clear from the question whether we want to compare two telephone objects or just a specific data item, I will discuss two situations here.

Comparing an item with data outside the class

Take this usecase, where we search for all phones with a weight greater than x (just pseudocode):

for (Phone& p in phoneList) {
    if (p.getWeight() > x) {
        cout << "Found";
    }
}

      

Then the first class example is perfectly fine as it is not an inherent feature of the phone and therefore the phone class is not responsible for handling it. Moreover, the result does not reveal more than is absolutely necessary for the task.

Comparing two phone objects

In this case, both code examples are equally good (or, in this case, equally bad). In both cases, the user needs to know a lot of details about how the phones are presented to compare all required members. If a new member is added to the class in a later version, each code segment that compares the two phones must be adapted. To overcome this, you can add a function to the class that does exactly the comparison.

class Phone {
private:
    string producer, color;
    int weight, dimension;
public:
    bool IsEqualTo(const Phone& other)
    {
        return (producer == other.producer && color == other.color &&....);
    }

      

Non-competitive usecase



But open up a more advanced example. Let's assume the following task: the user enters the PIN code into the phone, and if it is correct, the phone should unlock. Let's take a very naive approach:

class Phone
{
private:
    int pin;
    bool unlocked;

public:
    int getPin() { return pin; }
    void unlock() { unlocked = true; }
};

      

and the corresponding call

if (phone.getPin() == enteredPin)
    phone.unlock();

      

In this case, we have a completely different situation. Here we have to look at the "tell, not ask" rule , which basically says that it is a bad construct to ask the state of an object first, make a decision, and then tell the object what to do. Instead, we should only tell the object what we want and let it do the work for us. In this case, it is obvious, since unlocking the phone only when the contact is correct is the responsibility of the phone and not the user who uses the phone class. But in a more complex scenario, many programmers will do what I described here.

Back to the problem: A good solution here would be, for example,

class Phone
    {
    private:
        int pin;
        bool unlocked;

    public:
        void CheckPin(int enteredPin) {
            if (pin == enteredPin)
               unlocked = true;
        }
    };

      

with code

phone.CheckPin(enteredPin);

      

Hope this helps, and thanks to @KonradRudolph for the "say, don't ask rules" point. Feel free to help me improve the answer to the comment :)

+1


source


The first, even with a getter, is encapsulated. Consider a method color()

that returns a string. Even if you change the implementation Phone

to preserve the color as an enum rather than a string, your method can still return a string if you do some conversion first. The important part is that you can change the implementation color()

and underlying storage without having to change the class of the class.



Compare with a class that stores color as a public string. If you later change the item to be an enumeration, you need to change each location using the color. It is less of an encapsulation property and more of a property to separate the interface from the implementation.

+1


source


Encapsulation allows you to manipulate attributes solely using methods within a class. Both examples are encapsulated.

0


source







All Articles