Encapsulation in Dart – Dart OOP Guide

Haris Bin Nasir Avatar

·

·

Encapsulation is one of the core principles of object-oriented programming (OOP). It involves bundling data (variables) and methods that operate on that data into a single unit, usually a class, while restricting direct access to some of the object’s components. We prevent unauthorized access and enhance code maintainability and flexibility. In Dart, encapsulation uses private variables, getters, setters, and access control mechanisms. In this post, we will explore these topics in detail with practical examples.

For further information on other topics of Dart, click here.

For an in-depth exploration of other OOP topics in Dart, such as inheritance, polymorphism, and encapsulation etc, click here.

What is Encapsulation?

Encapsulation in Dart, on the other hand, refers to protecting an object’s internal state from unintended interference and misuse by external code. To achieve this, class properties are made private, while public methods are provided to access and update these properties safely.

Key Concepts of Encapsulation in Dart:

  • Private Variables: To begin with, data within a class can be made private by using the underscore (_) prefix.
  • Getters and Setters: Additionally, getters and setters are utilized to access and modify private data.
  • Access Modifiers: Unlike other languages, Dart does not have public, protected, or private keywords. Instead, it uses underscores (_) to define private access within libraries.

Private Variables

In Dart, any variable or method prefixed with an underscore (_) is considered private to its library. Consequently, this approach effectively hides the internal state of a class from other parts of the code.

Example of Private Variables

class Employee {
  String _name;  // Private variable
  int _age;      // Private variable

  Employee(this._name, this._age);

  void displayInfo() {
    print('Name: $_name, Age: $_age');
  }
}

void main() {
  var emp = Employee('Alice', 30);
  emp.displayInfo();  // Output: Name: Alice, Age: 30
  // emp._name = 'Bob';  // Error: '_name' is private
}

In the above example, _name and _age are private variables, and they cannot be accessed directly from outside the class.

Getters and Setters

Controlled access to private properties is achieved using Getters and Setters in Dart. Getters retrieve private variable values, while setters modify them.

Example of Getters and Setters

class BankAccount {
  double _balance;  // Private variable

  BankAccount(this._balance);

  // Getter for balance
  double get balance => _balance;

  // Setter for balance
  set balance(double amount) {
    if (amount >= 0) {
      _balance = amount;
    } else {
      print('Invalid amount');
    }
  }
}

void main() {
  var account = BankAccount(1000);
  print('Current balance: \$${account.balance}');  // Output: Current balance: $1000
  account.balance = 1500;  // Using setter
  print('Updated balance: \$${account.balance}');  // Output: Updated balance: $1500
  account.balance = -500;  // Output: Invalid amount
}

Explanation:

  • Getter: Allows reading the value of _balance.
  • Setter: Allows modifying the value of _balance with validation logic.

Benefits of Encapsulation

Encapsulation offers several benefits, including:

  1. Data Protection: Prevents unauthorized access to data by restricting access to private members. Consequently, this enhances the overall security of the system.
  2. Code Maintainability: Changes to a class’s implementation do not affect the external code that uses the class. Therefore, this results in a more manageable and adaptable codebase.
  3. Flexibility and Reusability: Internal implementation can be modified without affecting the code that uses the object, which in turn enhances both flexibility and reusability. Thus, it allows for easier updates and integration with other components.
  4. Improved Security: Sensitive data is hidden and protected, reducing the risk of unintended interference. As a result, this boosts the overall security of the system.

Encapsulation with Immutable Objects

Dart allows the creation of immutable objects using final variables. This ensures that once an object is created, its state cannot be changed.

Example of Encapsulation with Immutable Objects

class Student {
  final String _name;
  final int _age;

  const Student(this._name, this._age);

  String get name => _name;  // Getter
  int get age => _age;       // Getter
}

void main() {
  var student = Student('Bob', 20);
  print('Name: ${student.name}, Age: ${student.age}');  // Output: Name: Bob, Age: 20
  // student.name = 'Alice';  // Error: The setter 'name' isn't defined
}

Explanation:

  • Immutable Objects: The Student class is immutable because its properties are final and do not have setters.

Encapsulation in Libraries

In Dart, the underscore (_) makes a variable or method private to its library. As a result, this allows encapsulation to extend beyond a single class and apply to an entire library.

Example of Library-Level Encapsulation

File: library1.dart

// library1.dart
class LibraryClass {
  String _secret = "This is private to the library";

  void showSecret() {
    print(_secret);
  }
}

File: main.dart

// main.dart
import 'library1.dart';

void main() {
  var lib = LibraryClass();
  lib.showSecret();  // Output: This is private to the library
  // print(lib._secret);  // Error: '_secret' isn't accessible from outside the library
}

Explanation:

  • Library-Level Encapsulation: The _secret variable is private to library1.dart and cannot be accessed in main.dart.

Best Practices for Encapsulation

  1. Use Private Variables for Sensitive Data: Always mark sensitive data as private using an underscore (_).
  2. Use Getters and Setters: Instead of providing direct access to class fields, use getters and setters to control and validate data access.
  3. Minimize the Exposure of Internal State: Only expose what is necessary for the external use of the class.
  4. Follow the Principle of Least Privilege: Give access to data and methods only to the extent necessary for a class to perform its function.

Conclusion

Encapsulation is a powerful feature in Dart that, moreover, helps in protecting the internal state of an object and ensuring data integrity. By utilizing private variables, getters, setters, and library-level encapsulation, developers can create secure and maintainable codebases. Therefore, understanding and applying encapsulation effectively is crucial for writing robust object-oriented programs.

Happy Coding…!!!

Leave a Reply

Your email address will not be published. Required fields are marked *