Abstraction in Dart – Dart Object Oriented Programming (OOP) Guide

Haris Bin Nasir Avatar

·

·

Abstraction is one of the four pillars of Object-Oriented Programming (OOP), along with encapsulation, inheritance, and polymorphism. In Dart, abstraction focuses on exposing only the necessary details of an object while hiding the complex implementation. This allows developers to work with more simplified and manageable components. This post will cover the concept of abstraction in Dart, how it works, and how to implement it using abstract classes and interfaces.

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 Abstraction?

Abstraction in Dart refers to hiding the complex implementation details of a class and showing only the essential features. It allows developers to work with higher-level concepts without worrying about the intricacies of how they work. This is achieved through abstract classes and interfaces.

Key Concepts of Abstraction in Dart:

  1. Abstract Classes: A class that cannot be instantiated and is meant to be extended by other classes.
  2. Abstract Methods: Methods declared in an abstract class without a body, leaving the implementation to the subclasses.
  3. Interfaces: Any class can serve as an interface in Dart, enabling abstraction by requiring other classes to implement specific methods.

Abstract Classes

In Dart, abstract classes are classes that cannot be instantiated. They serve as blueprints for other classes, providing a common interface for subclasses while leaving the actual implementation details to them. You define an abstract class using the abstract keyword.

Example of an Abstract Class
abstract class Animal {
  void sound();  // Abstract method with no implementation

  void eat() {
    print("Animal is eating");
  }
}

class Dog extends Animal {
  @override
  void sound() {
    print("Dog barks");
  }
}

class Cat extends Animal {
  @override
  void sound() {
    print("Cat meows");
  }
}

void main() {
  var dog = Dog();
  dog.sound();  // Output: Dog barks
  dog.eat();    // Output: Animal is eating

  var cat = Cat();
  cat.sound();  // Output: Cat meows
}

Explanation:

  • The Animal class is abstract and defines an abstract method sound(), but it also contains a concrete method eat(). The Dog and Cat classes inherit from Animal and provide their own implementation for the sound() method.
  • The abstract class provides a framework for the child classes, ensuring they implement the necessary methods while sharing common functionality like eat().

Abstract Methods

An abstract method is a method that is declared in an abstract class but has no body. Subclasses are required to provide an implementation for these methods. Abstract methods are used when you want to ensure that certain functionality is implemented in all subclasses but the specific implementation may vary.

Example of Abstract Methods
abstract class Shape {
  void calculateArea();  // Abstract method
}

class Circle extends Shape {
  double radius;

  Circle(this.radius);

  @override
  void calculateArea() {
    print("Area of Circle: ${3.14 * radius * radius}");
  }
}

class Rectangle extends Shape {
  double length, width;

  Rectangle(this.length, this.width);

  @override
  void calculateArea() {
    print("Area of Rectangle: ${length * width}");
  }
}

void main() {
  var circle = Circle(5);
  circle.calculateArea();  // Output: Area of Circle: 78.5

  var rectangle = Rectangle(4, 5);
  rectangle.calculateArea();  // Output: Area of Rectangle: 20
}

Explanation:

In this example, the Shape abstract class defines an abstract method calculateArea(). The Circle and Rectangle classes implement the calculateArea() method in their specific ways.


Interfaces and Abstraction

In Dart, any class can act as an interface, and other classes can implement it. Unlike abstract classes, classes that implement an interface must provide concrete implementations for all of the methods declared in the interface.

Example of Interfaces
class Printer {
  void printDocument();  // Abstract method by default
}

class LaserPrinter implements Printer {
  @override
  void printDocument() {
    print("Laser Printer printing document...");
  }
}

class InkjetPrinter implements Printer {
  @override
  void printDocument() {
    print("Inkjet Printer printing document...");
  }
}

void main() {
  Printer laserPrinter = LaserPrinter();
  laserPrinter.printDocument();  // Output: Laser Printer printing document...

  Printer inkjetPrinter = InkjetPrinter();
  inkjetPrinter.printDocument();  // Output: Inkjet Printer printing document...
}

Explanation:

Here, Printer acts as an interface, and both LaserPrinter and InkjetPrinter implement the printDocument() method. This ensures that both printer types can be used interchangeably under the Printer interface while providing different implementations for the same method.


Benefits of Abstraction

Abstraction in Dart provides several advantages:

  1. Simplification: By abstracting away the complex implementation details, abstraction simplifies the design and usage of classes.
  2. Code Reusability: Abstract classes and interfaces promote code reusability by defining common behavior that can be shared across multiple subclasses.
  3. Separation of Concerns: It separates the implementation from the interface, allowing different parts of the application to work independently of each other.
  4. Extensibility: Abstract classes and interfaces make the system more flexible, as you can add new classes that implement or extend the abstract class without modifying existing code.

Abstract Classes vs Interfaces

While both abstract classes and interfaces promote abstraction, there are key differences:

  • Abstract Classes: Can contain both abstract and concrete methods. They are intended to be extended by a single subclass.
  • Interfaces: Classes that implement an interface must provide concrete implementations for all methods. Interfaces can be implemented by multiple classes.

When to Use Abstract Classes:

  • When you want to define common behavior that multiple classes can inherit.
  • When some methods will have a default implementation while others need to be overridden.

When to Use Interfaces:

  • When you want to define a contract that multiple classes can implement, without sharing any concrete behavior.

Best Practices for Abstraction in Dart

  1. Use Abstraction to Simplify Complex Systems: Abstract away the complex implementation details and expose only the necessary functionalities.
  2. Combine Abstraction with Other OOP Principles: Use abstraction in conjunction with encapsulation, inheritance, and polymorphism for well-structured and maintainable code.
  3. Avoid Unnecessary Abstraction: Only abstract when it adds value to the design. Overuse of abstraction can lead to overly complex code.

Conclusion

Abstraction is a powerful feature in Dart that simplifies code by exposing only the necessary details while hiding complex implementations. By using abstract classes and interfaces, you can create a flexible, reusable, and maintainable codebase. Understanding and applying abstraction effectively is essential for building scalable applications in Dart. For more detailed explanations and advanced use cases, check out our in-depth articles on abstraction in Dart.

Happy Coding…!!!

Leave a Reply

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