Having a lot of difficulty in understanding encapsulation

I'm just ... not quite sure I understand encapsulation. Maybe this may be due to the fact that I am still studying programming in the classroom and have not done anything like that. REAL world programs that will be used by other people, but I just don’t understand what it is trying to execute, I understand that it restricts access to some members of the class and its functions. But, how ... a limitation from whom? I saw a few examples when they have a private data member, but they have public get or set methods that let you manipulate a data item. So, how was something limited or hidden?

My book says the following:

"Encapsulation provides two important benefits:

  • User code cannot inadvertently ruin the state of an encapsulated object.
  • The implementation of an encapsulated class can change over time without requiring changes to the user code. "

I guess I'm confused by what they use. How, or can someone give me an example of how user code can damage the state of an object?

I know that my question is everything, but it’s my mind when it comes to thinking about encapsulation, so I have difficulty encapsulating all my thoughts about it (..lol)

+6
source share
5 answers

My favorite example of encapsulation is driving a car.

A typical driver knows how to make the car go ahead by turning on the ignition and pressing the gas pedal. They don’t need to know anything about internal engine burns to work every day.

The gas pedal provides a very simple interface for working with a very complex machine. Meaning, really complex internal details are encapsulated from the driver.

Now, from a code point of view, let's say you want to use a Map some type, but you don’t know how to write common hash functions for your keys or how to implement any of the other base data.

In Java, you can simply use HashMap without worrying about what the standard library does. This data is encapsulated by the user.

+14
source

Let's look at both points separately.

1. Class invariants

In programming, reasoning is simplified when you have invariants. For example, you may have already heard about loop invariants:

 for (size_t i = 0; i < vec.size(); ++i) { // something that does not alter i } 

In this loop, 0 <= i < vec.size() is an invariant that ensures that vec[i] always a valid expression.

A class can also have invariants, for example, if you consider std::string , its size() method returns the number of characters in its buffer. Always.

Now suppose you write your own string class:

 // Invariant: size represents the number of characters in data. struct String { size_t size; char* data; }; 

Well documenting what you want was invariant, but I can do it perfectly:

 void reset(String& str) { delete str.data; str.data = 0; } 

and forget to reset str.size , thereby violating the invariant.

However, if you remove the members of the class:

 // Invariant: size() returns the number of characters accessible via data() class String { public: size_t size() const { return _size; } char const* data() const { return _data; } // methods which maintain the invariant private: size_t _size; char* _data; }; 

now only you can break the invariant. Thus, in the event of an error, you have less code to audit.

2. Insulation with replacement performance

The idea is that you should be able to switch the internal presentation of information without adapting class users. For instance:

 class Employee { public: std::string const& name() const { return _name; } // Bad private: std::string _name; }; // class Employee 

Now, if I understand that std::string not a suitable representation for the name (for example, I would need wide characters):

 class Employee { public: std::string const& name() const { return _name; } // error! private: std::basic_string<char32_t> _name; }; // class Employee 

I am stuck. I can no longer return std::string const& (I no longer have an internal std::string ). I can change the return name() to make a copy:

 std::string Employee::name() const { return encodeUtf8(_name); } 

Unfortunately, it can still break customers:

 std::string const& name(Employee const& e) { std::string const& n = e.name(); // Bind temporary variable to const& return n; // Returns reference to local variable!! } 

If Employee was designed from the very beginning using std::string name() const , we could make the change without a problem.

Note: when used in the real world, you need to make an external API with isolation, but the internal API can perfectly display the data representation ... due to more changes on your plate when making changes.

+3
source

Great explanation from @ Kepani. I just wanted to explain the new statement http://www.tutorialspoint.com/cplusplus/cpp_data_encapsulation.htm

Encapsulation is an object-oriented programming concept that connects data and functions that control data, and which protects against external interference and misuse.

Therefore, if any external object tries to access or change something in the class variables, they can unknowingly harm the data contained in it. Thus, we simply create a group and restrict its use and access.

Just like the inside of a car can only work with a car, you are only asked to drive, not to go inside and make the wheels work or use some external element to move the wheels, as they may not be synchronized and cause harm.

+2
source

The point of encapsulation is not so much that "you cannot change personal data in any way," but that the personal data store is hidden. For instance:

 class Employee { ... }; class Company { public: std::vector<Employee> employees; ... }; 

Now, the vector of employees can work very well for a company with a small number of employees, but if the company continues to grow, it can cause problems because it is slow to find [IN REALITY, maybe not!]. However, since we set std::vector<Employee> , we cannot change the type of storage inside the company (without the risk of breaking something in the rest of the code). If employees is private, we can change it to any other type that makes sense for the application.

If we have:

 class Company { private: std::vector<Employee> employees; ... }; 

we can easily change it to:

 class Company { private: std::map<std::string, Employee> employees; }; 

Now std::map is a "tree" that can be searched in steps log2 (n) for n elements, where the vector (on average) searches (n / 2) on average - if we have 10,000 people, then the difference between 16 steps and 5,000 steps to find the right things. If the company grows to 100,000 people, the average vector will be 45,000 steps, but only 3 to find the right one on the map.

The thing is that "we can change the way we store data and control how it is available."

0
source

As an example, suppose we had a very simple class of strings:

 struct String { char * data = nullptr; size_t size = 0; void resize(size_t new_size) { data = realloc(data, new_size); size = new_size; } char & at(size_t i) { if (i >= size) throw std::range_error(); return data[i]; } }; 

We see that this class has a pair of invariants:

  • data should point to memory allocated by malloc and friends;
  • The size member must match the selected size.

It is easy to break invariants without encapsulation:

 String s; s.size = 42; s[10] = 'X'; // BOOM! out-of-range access s.data = "Hello!"; s.resize(3); // BOOM! tries to reallocate static memory 

By providing private members with data, we can prevent people from changing them arbitrarily; they can only be changed through the open interface, which we carefully implement to preserve the invariants.

For bonus points, you might consider how to properly fix a memory leak in my example; but this is somewhat beyond the scope of this question.

0
source

Source: https://habr.com/ru/post/956527/


All Articles