Unlocking Dart's Potential with Classes and Objects

Introduction

Classes and objects are the foundation of object-oriented programming (OOP), a powerful programming paradigm that can help you write more reusable, maintainable, and scalable code. In Dart, classes and objects are used to represent real-world entities, such as cars, dogs, and people.

If you're new to OOP, or if you're looking to improve your understanding of classes and objects in Dart, then this blog post is for you. In this post, we'll cover the following topics:

  • What are classes and objects?

  • How to create classes and objects in Dart

  • The different types of properties and methods that can be defined in a class

  • How to inherit from a class

  • How to use classes and objects to create reusable and maintainable code

  • Examples of how classes and objects can be used to solve real-world problems

By the end of this post, you'll have a solid understanding of classes and objects in Dart, and you'll be able to use them to write more powerful and efficient code.

So what are you waiting for? Read on to learn more about classes and objects in Dart!

Benefits of reading this blog:

  • Learn the basics of classes and objects in Dart

  • Understand how to create reusable and maintainable code with classes and objects

  • See examples of how classes and objects can be used to solve real-world problems

  • Improve your understanding of object-oriented programming

How to Create Classes and Objects

Creating a class

To create a class in Dart, you use the class keyword. The name of the class should be descriptive of the object that it represents. For example, if you are creating a class to represent a car, you might call it Car.

The body of the class is enclosed in curly braces ({}). Inside the body of the class, you can define properties and methods.

Properties

Properties are variables that are associated with a class. They can be used to store data about the object. For example, a car might have properties for its color, make, and model.

To define a property, you use the var keyword followed by the name of the property. The value of the property can be initialized when the class is created, or it can be set later.

For example, the following code defines a property for the car's color:

class Car {
  var color;
}

Methods

Methods are functions that are associated with a class. They can be used to perform actions on the object. For example, a car might have a method for driving, a method for braking, and a method for honking the horn.

To define a method, you use the void keyword followed by the name of the method. The body of the method is enclosed in curly braces ({}).

For example, the following code defines a method for driving the car:

class Car {
  var color;

  void drive() {
    print("The car is driving.");
  }
}

Creating an object

Once you have created a class, you can create an object of that class. The name of the object should be descriptive of the object that it represents. For example, if you are creating an object of the Car class, you might call it myCar.

The object is created by the name of the class. The object can then be used to access the properties and methods of the class.

For example, the following code creates an object of the Car class and then sets the color of the car to red:

var myCar = Car();
myCar.color = "red";

The object can then be used to call the drive() method:

myCar.drive();

This will print the following output to the console:

The car is driving.

Constructors

You might be wondering what Constructor is... When you create a new object of a class constructor is automatically called. So we can say that it's the first thing that is called when our object is created. You can do many things with the help of constructors you can set default properties, and performing any necessary setup operations.

Here is a simple example of a constructor:

class Point {
    final int x;
    final int y;

    const Point(this.x, this.y);
}

void main() {
    Point point = Point(10, 30);
}

Factory constructors

Factory constructors are one type of constructor that might create a new instance of the class or return the instance of the class from the cache if any.

class Logger {
  final String name;
  bool mute = false;

  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

void main() {
  var logger1 = Logger("Alice");
  var logger2 = Logger("Bob");

  print(logger1.name); // Alice
  print(logger2.name); // Bob
}

In this example, the Logger class has two constructors: a generative constructor and a factory constructor. The generative constructor is used to create new instances of the Logger class, while the factory constructor is used to return an existing instance from a cache.

The different types of properties and methods that can be defined in a class

Four different types of properties and methods can be defined in a class of Dart:

  • Instance properties: Instance properties are variables that are associated with each object of a class. They can be accessed and modified using the dot notation. For example, the following code defines an instance property for the car's color:
class Car {
  var color;
}
  • Static properties: Static properties are variables that are associated with the class itself, rather than with individual objects of the class. They can be accessed and modified using the ClassName.propertyName notation. For example, the following code defines a static property for the number of cars that have been created:
class Car {
  static int numberOfCars = 0;
}
  • Instance methods: Instance methods are functions that are associated with each object of a class. They can be accessed and called using the dot notation. For example, the following code defines a method for driving the car:
class Car {
  var color;

  void drive() {
    print("The car is driving.");
  }
}
  • Static methods: Static methods are functions that are associated with the class itself, rather than with individual objects of the class. They can be accessed and called using the ClassName.methodName notation. For example, the following code defines a method for creating a new car:
class Car {
  static Car createCar(String color) {
    var car = new Car();
    car.color = color;
    return car;
  }
}

How to inherit from a class

  • To inherit from a class, you use the extends keyword. For example, the following code defines a class Car that inherits from the Vehicle class:
class Vehicle {
  String brand;
  String model;

  Vehicle({
    this.brand = '',
    this.model = '',
  });
}

class Car extends Vehicle {
  int numberOfDoors;

  Car({
    required super.brand,
    required super.model,
    required this.numberOfDoors,
  });
}
  • The Car class inherits all of the properties and methods of the Vehicle class. In addition, the Car class has its own property, numberOfDoors.

  • When you create a new instance of the Car class, you must specify the brand, model, and number of doors. The brand and model are passed on to the Vehicle constructor and the number of doors are passed to the Car constructor.

  • The Car class can also call the methods of the Vehicle class. For example, the following code creates a new Car instance and calls the drive() method:

Car car = Car(
  brand: 'Toyota',
  model: 'Corolla',
  numberOfDoors: 4,
);
car.drive();

The drive() method is defined in the Vehicle class, but it can be called by objects of the Car class because the Car class inherits from the Vehicle class.

How to use classes and objects to create reusable and maintainable code

Using classes, objects, inheritance, and constructors effectively can lead to reusable and maintainable code in object-oriented programming. Let's explore how you can achieve this:

  1. Define Classes:

    • Identify entities in your problem domain and create classes that represent them.

    • Define properties (attributes) and methods (functions) that describe the behaviour of the class.

    • Ensure that each class has a clear and specific purpose.

  2. Use Constructors:

    • Define constructors to initialize the state of objects when they are created.

    • Constructors can set initial values for properties or perform any necessary setup tasks.

    • Provide default constructors and parameterized constructors as needed.

  3. Inheritance:

    • Identify common attributes and behaviours shared by multiple classes.

    • Use inheritance to create a parent (base) class that contains shared properties and methods.

    • Subclasses (derived classes) can inherit these shared properties and methods, reducing code duplication.

  4. Creating Subclasses:

    • Create subclasses by extending the parent class using the extends keyword.

    • Subclasses inherit properties and methods from the parent class.

    • Add additional properties and methods specific to the subclass.

  5. Overriding Methods:

    • Subclasses can override inherited methods to provide specialized behaviour.

    • This allows you to customize the behaviour of methods in a subclass while reusing the structure from the parent class.

  6. Super Keyword:

    • Use the super keyword to call methods or constructors from the parent class within a subclass.

    • This is helpful when you want to extend the behaviour of the parent class's methods.

  7. Polymorphism:

    • Leverage polymorphism to treat objects of different classes as instances of a common parent class.

    • This allows you to write code that operates on the parent class and works seamlessly with its subclasses.

  8. Reusable and Maintainable Code:

    • By using inheritance, you can avoid duplicating code, resulting in cleaner and more maintainable code.

    • Changes made to the parent class are automatically reflected in its subclasses, reducing the need for updates in multiple places.

    • Subclasses can focus on their specific behaviour while inheriting common functionality from the parent class.

Example (in a hypothetical programming language):

class Shape {
  double area;

  Shape([this.area = 0.0]);

  void calculateArea() {
    print("Calculating area of the shape.");
  }
}

class Circle extends Shape {
  double radius;

  Circle(this.radius);

  @override
  void calculateArea() {
    area = 3.14159 * radius * radius;
    print("Calculating area of the circle.");
  }
}

class Rectangle extends Shape {
  double width;
  double height;

  Rectangle(this.width, this.height);

  @override
  void calculateArea() {
    area = width * height;
    print("Calculating area of the rectangle.");
  }
}

In this example, the Shape class serves as a parent class with common properties and methods. The Circle and Rectangle subclasses inherit from Shape and provide specialized implementations of the calculateArea method. This approach promotes code reuse and allows for easy extension of functionality.

By using classes, objects, constructors, and inheritance, you can create a structured and modular codebase that is both reusable and maintainable, making it easier to manage and update your code over time.

Examples of how classes and objects can be used to solve real-world problems

  • A class can be used to represent a customer in a customer relationship management (CRM) system. The class would have properties for the customer's name, address, phone number, and email address. It would also have methods for updating the customer's information, adding the customer to a mailing list, and sending the customer a newsletter.

  • A class can be used to represent a product in an online store. The class would have properties for the product's name, description, price, and inventory level. It would also have methods for adding the product to the shopping cart, removing the product from the shopping cart, and checking if the product is in stock.

  • A class can be used to represent a game character in a video game. The class would have properties for the character's name, health, attack power, and defence. It would also have methods for moving the character, attacking enemies, and taking damage.

  • A class can be used to represent a web page in a web application. The class would have properties for the page's title, content, and links. It would also have methods for rendering the page, adding content to the page, and removing content from the page.

These are just a few examples of how classes and objects can be used to solve real-world problems in Dart. Classes and objects are powerful tool that can be used to model real-world entities and to create reusable and maintainable code.

Conclusion

In conclusion, classes and objects form the cornerstone of object-oriented programming in Dart. They encapsulate data and behaviour, brought to life by constructors. Creating them involves defining properties and methods. Inheritance enables the construction of hierarchies for code reusability. Leveraging classes and objects fosters the creation of robust, modular, and maintainable codebases. Real-world applications span e-commerce, social platforms, finance, and more, showcasing their versatile problem-solving capabilities. By mastering these concepts, developers unlock the potential to craft elegant and effective software solutions that stand the test of time.

Did you find this article valuable?

Support Kishan's Blog by becoming a sponsor. Any amount is appreciated!