Dart Abstraction


Abstraction in Dart

Abstraction is one of the fundamental concepts of Object-Oriented Programming (OOP). It refers to the concept of hiding the complex implementation details of a class while exposing only the essential features to the outside world. The main goal of abstraction is to reduce complexity and allow the user to focus on interacting with objects at a high level without needing to know their inner workings.

In Dart, abstraction is achieved using:

  1. Abstract Classes: A class that cannot be instantiated directly and is meant to be subclassed. It can have abstract methods (without implementation) that must be implemented by subclasses.
  2. Interfaces: Dart classes implicitly act as interfaces and can be used to define the blueprint for other classes.
  3. Methods with no implementation: These methods are defined in the abstract class but must be implemented by concrete subclasses.

Key Points of Abstraction:

  1. Abstract class: Contains both fully implemented methods and abstract methods (methods without implementation).
  2. Abstract methods: Methods that are declared but not implemented in the abstract class. Subclasses must provide the implementation for these methods.
  3. Concrete class: A subclass that implements the abstract methods from the abstract class.

Example of Abstraction in Dart

Let's create an example where we define an abstract class Shape with an abstract method area(). We then create two concrete classes, Circle and Rectangle, that extend Shape and implement the area() method.

// Abstract class abstract class Shape { // Abstract method (without implementation) double area(); // A regular method with implementation void displayShape() { print('This is a shape.'); } } // Concrete class Circle that implements the abstract method class Circle extends Shape { double radius; // Constructor to initialize the radius Circle(this.radius); // Implementing the abstract method @override double area() { return 3.14 * radius * radius; } } // Concrete class Rectangle that implements the abstract method class Rectangle extends Shape { double width; double height; // Constructor to initialize width and height Rectangle(this.width, this.height); // Implementing the abstract method @override double area() { return width * height; } } void main() { // Creating objects of Circle and Rectangle Shape circle = Circle(5); Shape rectangle = Rectangle(4, 6); // Displaying shape info circle.displayShape(); // Output: This is a shape. print('Area of Circle: ${circle.area()}'); // Output: Area of Circle: 78.5 rectangle.displayShape(); // Output: This is a shape. print('Area of Rectangle: ${rectangle.area()}'); // Output: Area of Rectangle: 24 }

Explanation:

  • Abstract class Shape:

    • It contains an abstract method area(), which has no implementation.
    • It also contains a regular method displayShape(), which is fully implemented and can be used by subclasses.
  • Concrete classes Circle and Rectangle:

    • Both classes extend the Shape class and implement the area() method.
    • In Circle, area() is implemented as π * r^2, while in Rectangle, area() is implemented as width * height.
  • In the main() function:

    • We create instances of Circle and Rectangle and invoke the area() method to calculate the area for each shape.

Output:

This is a shape. Area of Circle: 78.5 This is a shape. Area of Rectangle: 24

Benefits of Abstraction:

  1. Reduces Complexity: Users only interact with essential features of a class, avoiding unnecessary details.
  2. Improves Maintainability: Changes in the implementation of abstract methods in subclasses won't affect the code that uses the abstract class.
  3. Increases Flexibility: Abstraction allows you to define common interfaces and then provide different implementations for each subclass, making the code more flexible and adaptable to changes.

Key Points to Remember:

  • Abstract classes cannot be instantiated directly. You can only create objects of concrete subclasses that implement the abstract methods.
  • Abstract methods define a contract that the subclasses must adhere to by providing an implementation for those methods.
  • Concrete classes are responsible for providing the implementation of the abstract methods.

Example of Multiple Classes with Abstraction:

If we wanted to extend this example further, we could add additional classes, such as a Triangle, to further demonstrate the flexibility of abstraction.

// Concrete class Triangle that implements the abstract method class Triangle extends Shape { double base; double height; // Constructor to initialize base and height Triangle(this.base, this.height); // Implementing the abstract method @override double area() { return 0.5 * base * height; } } void main() { // Creating objects of Circle, Rectangle, and Triangle Shape circle = Circle(5); Shape rectangle = Rectangle(4, 6); Shape triangle = Triangle(4, 3); // Displaying shape info circle.displayShape(); print('Area of Circle: ${circle.area()}'); rectangle.displayShape(); print('Area of Rectangle: ${rectangle.area()}'); triangle.displayShape(); print('Area of Triangle: ${triangle.area()}'); }

Output:

This is a shape. Area of Circle: 78.5 This is a shape. Area of Rectangle: 24 This is a shape. Area of Triangle: 6

Summary:

  • Abstraction helps in hiding unnecessary implementation details and shows only the necessary features of an object.
  • Abstract classes are the building blocks of abstraction in Dart, enabling a clean design by focusing on the essential functionality.
  • Through abstract methods in abstract classes, you can enforce a contract that subclasses must follow, ensuring consistency while still allowing flexibility in implementation.

Abstraction is a great way to design your code in a more modular and maintainable manner, allowing you to build complex systems by focusing on high-level operations without worrying about the implementation details.