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:

class Car {
  late int _speed; // private speed attribute

  Car() {
    this._speed = 0; // Initialize speed with 0
  }
}

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:

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

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:

class Car {
  late int _speed; // Private speed attribute

  Car() {
    this._speed = 0; // Initialize speed with 0
  }

  // Getter for speed
  int get speed => _speed;

  // Setter for speed
  set speed(int value) => _speed = value;
}

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

void main() {
  // Create a new Car instance
  var myCar = Car();
  
  // Access the speed using getter
  print(myCar.speed); // Outputs: 0, initial speed
  
  // Set the car's speed using setter
  myCar.speed = 100;
  print(myCar.speed); // Outputs: 100
}

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.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal