Lesson 4

Encapsulation and Data Privacy in Dart Classes

Introduction And Learning Goals

Welcome! Today, we begin our exploration of an essential aspect of Dart's object-oriented programming pattern: Encapsulation. This concept forms a protective barrier around an object's data, preventing any external part of the program from accessing it directly. Let's dive into it!

Why Encapsulation?

Encapsulation serves three main purposes: it increases the robustness of the code, controls data modification, and provides a structured interface that other parts of the code can interact with. Take a smartphone as an example — you interact with a user-friendly interface without worrying about the complex electronics inside. Similarly, encapsulation hides the complex internals while providing a straightforward interface.

Private Data In Dart

Now, let's move onto Private Data: In Dart, we make data private by using an underscore _ before its name. This data cannot be accessed directly outside of the class. Let's illustrate this with a Car class, introducing a private attribute named _speed:

1class Car { 2 late int _speed; // private speed attribute 3 4 Car() { 5 this._speed = 0; // Initialize speed with 0 6 } 7}

This class has a private instance variable, _speed, that's inaccessible directly outside the class. Here's what happens when we try to access _speed directly from outside:

1void main() { 2 var myCar = Car(); 3 print(myCar._speed); // This line will cause a compilation error 4}

Attempting to compile this code will result in an error similar to: "Error: The getter '_speed' isn't defined for the class 'Car'". Dart's privacy model prevents access to _speed from outside Car, enforcing encapsulation.

To safely interact with such private variables, Dart employs getters and setters, which we'll explore next.

Using Getters And Setters

Getters and Setters are used to manage access to private data. In our Car class, a getter retrieves the _speed attribute, while a setter changes it as follows:

1class Car { 2 late int _speed; // Private speed attribute 3 4 Car() { 5 this._speed = 0; // Initialize speed with 0 6 } 7 8 // Getter for speed 9 int get speed => _speed; 10 11 // Setter for speed 12 set speed(int value) => _speed = value; 13}

These methods enable us to safely access and modify the car's speed. Here's how we can use them:

1void main() { 2 // Create a new Car instance 3 var myCar = Car(); 4 5 // Access the speed using getter 6 print(myCar.speed); // Outputs: 0, initial speed 7 8 // Set the car's speed using setter 9 myCar.speed = 100; 10 print(myCar.speed); // Outputs: 100 11}

In the code blocks above, we demonstrated how to define and use private attributes in a Car class by employing getters and setters in Dart. We first created a Car object, then used a getter to retrieve its speed, updated this speed with a setter, and observed how these operations maintain data encapsulation while allowing controlled access to the private _speed attribute. This pattern enables us to protect the integrity of the object's state from unintended modifications.

Lesson Summary And Practice

Bravo! You've made it this far. This lesson equipped you with:

  • An understanding of private data in Dart
  • The knowledge to employ getters and setters

Inscribe these lessons into your programming mind and continue your journey! We have prepared hands-on exercises for you that will provide practical experience and help cement your understanding of the concepts learned. Keep going — becoming proficient in Dart is a journey worth taking!

Enjoy this lesson? Now it's time to practice with Cosmo!

Practice is how you turn knowledge into actual skills.